zoukankan      html  css  js  c++  java
  • webSocket的 原理 及 实现

    websocket协议是基于Tcp的一种新的网络协议,它实现了客户端与服务器的双向通行,并允许服务端主动发送信息给客户端。WebSocket是html5中的协议。

    Http协议与WebSocket协议:

    Http协议只有1.0 和1.1 两个版本,

    http协议是无状态的每一请求对应一个响应,而且响应完了就回结束连接,多个请求对应多个响应,每次都要重新建立连接。

    在http1.1 中增加了 keep-alive (只是客户端的一种建议) ,用于在一个Http连接中,可以发送多个Request,接收多个Response。但依旧是每隔request对应一个response。

    Connection:keep-alive

     这个是通知服务器在 request/response 之后,不要理解断开tcp连接,后面的request/response 任然可以通过这个连接完成。但是这个只是个建议,服务端可能不支持,也可能时间长了,断开了连接。(通俗点就根服务端说下 ,后面可能还有事需要找你先不要挂了)

    这个是不可能实现服务器主动push消息给客户端的。

    那么如何能够实现呢?

    But 我们可以使用ajax轮询 和long poll 技术,制造一个服务端push给客户端的假象。

    那什么是Ajax轮询?

    Ajax 轮询 就是隔几秒让浏览器发送请求询问服务端时候有消息了?

    场景:

    客户端:hi,有没有新信息(Request)
    服务端:没有(Response)
    客户端:hi,有没有新信息(Request)
    服务端:没有。。(Response)
    客户端:hi,有没有新信息(Request)
    服务端:没有啊。。(Response)
    客户端:hi有没有新消息(Request)
    服务端:好了,有啦给你。(Response)
    客户端:hi,有没有新消息(Request)
    服务端:没有......
    

    But 实际中这样会增加服务端的负载,降低服务端的效率。

    什么是long poll 呢?

    long poll 和ajax 轮询差不多,不过long poll 是阻塞的模式。也就是客户端发送一个request后 一直等待,知道服务端有消息response后才ok

    场景:

    客户端:hi,有没有新信息(Request)
    服务端:没有(Response)
    客户端:hi,有没有新信息(Request)
    客户端:hi,有没有新信息(Request)
    

    long pull 虽然降低了服务器的负载,但是需要服务器有很高的并发能力才可以。

    ajax轮询、long poll技术虽然都能实现服务端消息的实时通知,但是各有缺点,都不是根本的解决办法。

    既然他们都不行,那应该搞个新的协议。

    WebSocket协议解决的客户端与服务端的双工通信的问题。

    场景:

    客户端:啦啦啦,我要建立Websocket协议,需要的服务:chat,Websocket协议版本:13(HTTP Request)
    服务端:ok,确认,已升级为Websocket协议(HTTP Protocols Switched)
    客户端:麻烦你有信息的时候推送给我噢。。
    服务端:ok,有的时候会告诉你的。
    服务端:发斯蒂芬
    服务端:啊实打实大师
    服务端:哈哈哈哈哈啊哈
    服务端:笑死我了哈哈哈
    

      

    request header

    Accept-Encoding: gzip, deflate, br
    Accept-Language: zh-CN,zh;q=0.9
    Cache-Control: no-cache
    Connection: Upgrade #通知服务器协议升级
    Cookie: JSESSIONID=450BA8D626F785A1AE3BEB9AC9226CD0
    Host: localhost:8099
    Origin: http://localhost:8099
    Pragma: no-cache
    Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
    Sec-WebSocket-Key: yjbBHsadoLeWtVNa+3Y2ow== #传输给服务器的key
    Sec-WebSocket-Version: 13 #wesocket版本协议号13
    Upgrade: websocket #协议升级为websocket协议
    User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36
    
    Sec-WebSocket-Key 这个是客户端发送给服务端,服务端处理后再返回给客户端客户端进行验证 判断是否建立连接。
    Connection: Upgrade #通知服务器协议升级
    Upgrade: websocket #协议升级为websocket协议
    这个就是Websocket的核心了,告诉Apache、Nginx等服务器:注意啦,发起的是Websocket协议,快点帮我找到对应的助理处理~不是HTTP。
    Status Code:101 Switching Protocols  101 连接成功
    

     response Header:

    Connection: upgrade #升级成功
    Date: Thu, 12 Apr 2018 03:11:37 GMT
    Sec-WebSocket-Accept: NbF/mYm1P/lNy0NsMWxYD1Wi1EE= #服务端处理之后的key
    Sec-WebSocket-Extensions: permessage-deflate;client_max_window_bits=15
    Server: Apache-Coyote/1.1
    Upgrade: websocket
    

    握手成功!

    简单的代码示例:

    jsp 客户端:

    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
    </head>
    <script type="text/javascript">
    	var webSocket = null;
    	if ('WebSocket' in window) {
    		webSocket = new WebSocket("ws://localhost:8099/webSocketTest");
    	} else {
    		alert("浏览器不支持webSocket");
    	}
    
    	webSocket.onopen = function() {
    		writeDiv("websocket连接ok");
    	}
    
    	webSocket.onmessage = function(event) {
    		writeDiv("client 接受的消息是:" + event.data);
    	}
    
    	webSocket.onerror = function() {
    		writeDiv("发生错误");
    	}
    
    	function sendMsg() {
    		var val = document.getElementById("inputText").value;
    		webSocket.send(" 客户端:" + val);
    	}
    
    	function closes() {
    		webSocket.close();
    	}
    
    	webSocket.onclose = function() {
    		webSocket.close();
    		writeDiv("WebSocket連接關閉");
    	}
    
    	function writeDiv(innerHTML) {
    		var html = document.getElementById('message').innerHTML;
    		document.getElementById('message').innerHTML += innerHTML + '<br>';
    	}
    </script>
    <body>
    
    	<input type="text" id="inputText">
    	<button id="but" onclick="sendMsg()">submit</button>
    	<br>
    	<button id="close" onclick="closes()">关闭webSocket连接</button>
    	<br>
    	<div id="message"></div>
    </body>
    </html>
    

    服务端:

    package com.tgb.SpringActivemq.controller;
    
    import java.io.IOException;
    import java.util.HashSet;
    import java.util.Random;
    import java.util.Set;
    
    import javax.websocket.OnClose;
    import javax.websocket.OnError;
    import javax.websocket.OnMessage;
    import javax.websocket.OnOpen;
    import javax.websocket.Session;
    import javax.websocket.server.ServerEndpoint;
    
    import org.apache.log4j.Logger;
    
    @ServerEndpoint("/webSocketTest")
    public class webSocketController {
    
        private int count = 0;
    
        static  Set<Session> set = new HashSet<Session>();
    
        static final Logger logger = Logger.getLogger(webSocketController.class);
    
        private Session session;
    
        @OnOpen
        public void onOpen(Session sessio) {
            this.session = sessio;
            set.add(session);
            this.count++;
            logger.info("当前连接:" + this.count);
        }
    
        // 接受消息 处理
        @OnMessage
        public void onMessage(String message, Session ss) {
            logger.info("接受到的客户端消息是:" + message);
            Random r = new Random();
            int tet = r.nextInt(1000);
            try {
                ss.getBasicRemote().sendText("服务端发送的是:" + tet);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        @OnError
        public void Onerror(Session s, Throwable th) {
            logger.info(th.getMessage());
        }
    
        @OnClose
        public void onClose(Session s) {
            try {
                s.close();
                set.remove(s);
                this.count--;
                logger.info("当前连接数:" + this.count);
                logger.info("CLOSE WEBSOCKET");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

      

     

     

  • 相关阅读:
    《C++ Primer》之面向对象编程(四)
    《C++ Primer》之面向对象编程(三)
    《C++ Primer》之面向对象编程(二)
    《C++ Primer》之面向对象编程(一)
    《C++ Primer》之指向函数的指针
    C++ 隐式类类型转换和转换操作符
    python中的sum函数.sum(axis=1)
    numpy中tile函数
    sscanf函数详解
    snprintf()返回值的陷阱
  • 原文地址:https://www.cnblogs.com/msx-2018-begin/p/8806625.html
Copyright © 2011-2022 走看看