欧美vvv,亚洲第一成人在线,亚洲成人欧美日韩在线观看,日本猛少妇猛色XXXXX猛叫

新聞資訊

    為什么出現這個問題?

    大四上:這學期唯獨的一門課---“云計算”

    老師:要求在自己的電腦上安裝虛擬機還有Linux

    我:嫌電腦太重,不想負重去上課。所以直接購買了臺騰訊云的服務器(學生機賊便宜,我還有一臺阿里的),選裝下CentOS賊快就完成了,這下就可以用ssh工具隨時隨地登錄了。

    老師:后期要在Linux上用eclipse開發程序以及使用Mapreduce開發,簡單的ssh只有命令行,使用vim編輯程序想要累死我。急需安裝一個可遠程登錄的界面工具(Linux不像Windows自帶遠程桌面登錄,我的阿里服務器就是windows系統,遠程登錄桌面就很爽)于是就知道了VNCSERVER。

    tips:其實可以遠程訪問嘛,畢竟服務器有公網IP,訪問分布式文件系統so easy!

    接下來就是使用過程中的坎坎坷坷了!??!

    草率了!

    以為只是簡單的安裝完VNCSERVER還有VNCVIEW就可以了。

    一開始就直接用VNCVIEW連了服務器,結果就是藍屏

    還是啥都提示也沒的那種,也不知道怎么辦

    一直過了幾天才忽然看到需要安裝圖形用戶界面接口X Window System和圖形界面gnome



    好消息!

    安裝成功后重啟VNCSERVER服務,然后就連接成功了!嘻嘻嘻嘻嘻嘻

    原文鏈接:https://www.cnblogs.com/yanht/p/15352010.html

    背景

    用戶需要通過前端HTML頁面的noVNC(noVNC是什么?)客戶端連接底層VNC Server服務端,為了防止VNC Server的IP暴露,因此需要做一層代理。正常情況下使用Nginx、Apache等都可以搞定,但是由于項目架構的一些問題,暫時不能再加一臺反向代理服務器,所以決定寫一個單獨的模塊實現反向代理的功能。

    ? 在網上和Github上找了一下,使用了HTTP-Proxy-Servlet,引入該依賴搭建一個Spring Boot項目。

    搭建

    1.引入代理的依賴

    <dependency>
        <groupId>org.mitre.dsmiley.httpproxy</groupId>
        <artifactId>smiley-http-proxy-servlet</artifactId>
        <version>1.12</version>
    </dependency>

    2.通過注冊bean攔截指定URL路徑進行自定義操作

    @Configuration
    public class ProxyServletConfiguration {
    
        // 攔截所有請求交給下面的VNCProxyServlet去處理
        private final static String SERVLET_URL="/*";
    
        @Bean
        public ServletRegistrationBean<VNCProxyServlet> servletServletRegistrationBean() {
            ServletRegistrationBean<VNCProxyServlet> servletRegistrationBean=new ServletRegistrationBean<>(new VNCProxyServlet(), SERVLET_URL);
            //設置網址以及參數
            Map<String, String> params=ImmutableMap.of(
                    "targetUri", "null", //這里寫null是因為targetUri是在自定義的VNCProxyServlet類中動態傳入的,而且這里必須要有值
                    ProxyServlet.P_LOG, "true",
                    ProxyServlet.P_PRESERVEHOST,"true",
                    ProxyServlet.P_PRESERVECOOKIES,"true"
            );
            servletRegistrationBean.setInitParameters(params);
            return servletRegistrationBean;
        }
    
    }

    這里遇到的坑:

    ? 剛開始其實是準備在已有的一個模塊中加上這個代理功能,因為可以指定攔截的路徑,比如只攔截請求路徑為/proxy/*的,然后交給自定義的Servlet去代理,后來寫好測試時,發現代理過去后代理目標主頁一片空白,看了控制臺的Network后,主頁確實是返回200且加載正常,但是由主頁發起的js、css和img等靜態資源狀態碼都為404。

    當時以為是代碼的問題,后來發現靜態資源都是相對路徑的有問題,如果前端的靜態資源是引入第三方的,比如從CDN中引入Vue.js則不會出現問題,都可以正常的被代理。既然狀態碼是404,那肯定是找不到這個資源,看了一下發現如果在靜態資源的路徑前加上指定攔截的路徑/proxy/就可以被正常代理。此時才明白,因為訪問首頁的路徑中帶/proxy/是在地址欄主動輸入的,所以請求到后臺,后臺Servlet攔截發現路徑中帶/proxy/,把該請求交給自定義的代理Servlet去處理然后返回。而主頁上的js、css等靜態資源發起請求的路徑是不會帶/proxy/*的,因此不會走到代理Servlet,并且代理模塊中也沒有相應資源路徑,所以就理所應當的返回了404。

    ? 為此還專門在GitHub上問了一下作者,作者也是回復說這并不是這個代理模塊該做的事,最好是前端處理,或者讓前端使用絕對路徑。附上地址(Discussions)

    最后就是決定單獨拉出來寫一個Spring Boot項目做這個代理功能模塊,直接代理/*,這樣所有請求到這個模塊的都會被代理。

    3.自定義Servlet實現動態代理目標地址

    // VNCProxyServlet繼承了ProxyServlet 重寫了service方法 在方法中添加自定義操作 從請求地址中動態獲取
    @Override
        protected void service(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws ServletException, IOException {
            // 獲取請求地址
            String targetUri=servletRequest.getRequestURL().toString();
    		
            // 正則取請求地址中的參數 參數是放在域名中的
            Matcher matcher=DOMAIN_REX.matcher(targetUri);
            if(!matcher.find()){
                // 自定義的異常
                throw new GenericException("從域名中獲取vmId異常!");
            }
            // 取域名中的第一個 eg: http://vmId.xxx.cn得 [vmId,xxx,cn] 得 vmId
            Long vmId=Long.valueOf(matcher.group().split("\\.")[0]);
            
            // eg:業務邏輯根據vmId去拿 targetUri
        	targetUri=vmService.getTargetUrl(vmId);
    	
            if (StringUtils.isEmpty(targetUri)) {
                throw new GenericException("代理路徑不正確,請確認路徑");
            }
    
            // 設置Url
            if (servletRequest.getAttribute(ATTR_TARGET_URI)==null) {
                servletRequest.setAttribute(ATTR_TARGET_URI, targetUri);
            }
    
            // 設置Host
            if (servletRequest.getAttribute(ATTR_TARGET_HOST)==null) {
                URL trueUrl=URLUtil.url(targetUri);
                servletRequest.setAttribute(ATTR_TARGET_HOST, new HttpHost(trueUrl.getHost(), trueUrl.getPort(), trueUrl.getProtocol()));
            }
    		// 下面大部分都是父類的源碼 沒有需要特別修改的地方
            String method=servletRequest.getMethod();
            // 替換多余路徑
            String proxyRequestUri=this.rewriteUrlFromRequest(servletRequest);
    
            HttpRequest proxyRequest;
            if (servletRequest.getHeader(HttpHeaders.CONTENT_LENGTH) !=null ||
                    servletRequest.getHeader(HttpHeaders.TRANSFER_ENCODING) !=null) {
                proxyRequest=new BasicHttpRequest(method, proxyRequestUri);
            } else {
                proxyRequest=this.newProxyRequestWithEntity(method, proxyRequestUri, servletRequest);
            }
    
            this.copyRequestHeaders(servletRequest, proxyRequest);
            setXForwardedForHeader(servletRequest, proxyRequest);
            HttpResponse proxyResponse=null;
    
            try {
                // Execute the request
                proxyResponse=this.doExecute(servletRequest, servletResponse, proxyRequest);
    
                // Process the response
                int statusCode=proxyResponse.getStatusLine().getStatusCode();
    
                // "reason phrase" is deprecated but it's the only way to pass
                servletResponse.setStatus(statusCode, proxyResponse.getStatusLine().getReasonPhrase());
    
                // copying response headers to make sure SESSIONID or other Cookie which comes from remote server
                // will be saved in client when the proxied url was redirected to another one.
                copyResponseHeaders(proxyResponse, servletRequest, servletResponse);
    
                if (statusCode==HttpServletResponse.SC_NOT_MODIFIED) {
                    servletResponse.setIntHeader(HttpHeaders.CONTENT_LENGTH, 0);
                } else {
                    copyResponseEntity(proxyResponse, servletResponse, proxyRequest, servletRequest);
                }
            } catch (Exception e) {
                handleRequestException(proxyRequest, proxyResponse, e);
            } finally {
                if (proxyResponse !=null) {
                    EntityUtils.consumeQuietly(proxyResponse.getEntity());
                }
            }
        }
    1. ? 這里主要列出關鍵部分,因為方案還沒有完結。
    • 問題

    ? 本以為這樣就成功了,但是測試之后發現頁面和靜態資源都代理過去了,但是有一個websocket請求失敗了。像noVNC這種網頁版的黑窗口,早就該想到肯定是用websocket這種長鏈接的請求進行交互的。后來去搜了一下這個叫websockify的請求,就是最開始介紹noVNC博客中介紹的:

    ? 瀏覽器不支持VNC,所以不能直接連接VNC,但是可以使用代理,使用noVNC通過WebSocket建立連接,而VNC Server不支持WebSocket,所以需要開啟Websockify代理來做WebSocket和TCP Socket之間的轉換,這個代理在noVNC的目錄里,叫做websockify。

    ? 此時項目是能夠攔截到websockify這個請求的,但是由于servlet把這個請求當成普通的請求去代理到目標服務器,這樣是無法成功的,所以要做的就是類似實現一個websocket的反向代理,搜了一下的話發現例子不是很多,大多都是在前端做的,前端作為客戶端與服務端建立websocket連接,但目前的狀況很明顯是需要這個代理模塊既做websocket服務端與web端建立連接,再作為websocket客戶端與VNC 服務端建立連接,然后進行交互傳遞通信。

    ? 后面也找到了這篇博客通過noVNC和websockify連接到QEMU/KVM,然后總結一下從用戶發出請求到得到響應的流程:

    PC Chrome(客戶端)=> noVNC Server(noVNC端)=> websockify(websocket轉TCP Socket)=> VNC Server(VNC服務端)=> websockify(TCP Socket轉websocket)=> noVNC Server(noVNC端)=> PC Chrome(客戶端)

    用戶使用PC Chrome瀏覽器請求 noVNC端(因為無法直接訪問VNC Server端,VNC Server是不支持Websocket連接),經由websockify將websocket轉為TCP Socket請求到VNC服務端,返回TCP響應,經由websockify轉換為websocket返回給客戶端瀏覽器,這樣來進行交互。整個過程 websockify 代理器是關鍵,noVNC 可以被放在瀏覽器端

    1.noVNC網頁端與代理模塊建立websocket通信

    @Configuration
    public class WebSocketConfig {
        @Bean
        public ServerEndpointExporter serverEndpointExporter() {
            return new ServerEndpointExporter();
        }
    }
    @ServerEndpoint("/websockify")
    @Component
    public class WebSocketServer {
    
        /**
         * 連接建立成功調用的方法
         */
        @OnOpen
        public void onOpen(Session session) {
            logger.info("open...");
        }
    
        /**
         * 連接關閉調用的方法
         */
        @OnClose
        public void onClose() {
            logger.info("close...");
        }
    
        /**
         * 收到客戶端消息后調用的方法
         */
        @OnMessage
        public void onMessage(String message, Session session) {
            logger.info(message);
        }
    
        /**
         * 發生錯誤調用的方法
         */
        @OnError
        public void onError(Session session, Throwable error) {
            logger.error("用戶錯誤原因:"+error.getMessage());
            error.printStackTrace();
        }
    
    }

    都是很常用的websocket服務端的代碼,唯一要注意的是前端請求'/websockify'地址發起websocket連接時,要注意用ip,尤其是本地,使用localhost會報錯,要使用127.0.0.1。最后測試連接成功,返回狀態碼101,并且消息可以正常接收。noVNC網頁端與代理模塊建立websocket通信完成。

    2.代理模塊與VNC Server建立websocket通信

    java后臺作為websocket客戶端很少,大多是用Netty去寫的,但是不適合目前的情況,最后還是找到了一個感覺比較合適的

    public class MyWebSocketClient {
        public static WebSocketClient mWs;
        public static void main(String[] args) {
            try {
                // 
                String url="ws://172.28.132.11:8888/websocketify";
                URI uri=new URI(url);
                HashMap<String, String> httpHeadersMap=new HashMap<>();
                httpHeadersMap.put("Sec-WebSocket-Version", "13");
                httpHeadersMap.put("Sec-WebSocket-Key", "YBhzbbwLI83U5EH8Tlutwg==");
                httpHeadersMap.put("Connection","Upgrade");
                httpHeadersMap.put("Upgrade","websocket");
                httpHeadersMap.put("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36");
                httpHeadersMap.put("Cookie","token=8asda2das-84easdac-asdaqwe4-2asda-asdsadas");
                httpHeadersMap.put("Sec-WebSocket-Extensions","permessage-deflate; client_max_window_bits");
                mWs=new WebSocketClient(uri,httpHeadersMap){
                    @Override
                    public void onOpen(ServerHandshake serverHandshake) {
                        System.out.println("open...");
                        System.out.println(serverHandshake.getHttpStatus());
                        mWs.send("666");
                    }
    
                    @Override
                    public void onMessage(String s) {
                        System.out.println(s);
                    }
    
                    @Override
                    public void onClose(int i, String s, boolean b) {
                        System.out.println("close...");
                        System.out.println(i);
                        System.out.println(s);
                        System.out.println(b);
                    }
    
                    @Override
                    public void onError(Exception e) {
                        System.out.println("發生了錯誤...");
                    }
                };
                mWs.connect();
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    }
    
    // 調用后報錯  直接關閉了連接  狀態碼為1002 
    // close...
    // 1002
    // Invalid status code received: 400 Status line: HTTP/1.1 400 Client must support 'binary' or 'base64' protocol

    發生錯誤后,發現關鍵地方,客戶端必須支持 binary或base64協議,一番搜索后再Stack Overflow找到了線索,并且是Kanaka(noVNC和websockify的開發者)親自回答的,大概意思就是擬需要在構造函數中提供這些協議。

    然后我又在websockify.js的源碼中找到了這個構造,確實需要傳遞一個protocols的數組參數,可是這是前端,并不知道Java如何完成這個操作。

    • 后續

    ? 首先再次感謝開源項目和各位博主大佬的分享,依舊在尋找解決方案......

網站首頁   |    關于我們   |    公司新聞   |    產品方案   |    用戶案例   |    售后服務   |    合作伙伴   |    人才招聘   |   

友情鏈接: 餐飲加盟

地址:北京市海淀區    電話:010-     郵箱:@126.com

備案號:冀ICP備2024067069號-3 北京科技有限公司版權所有