使用WebSocket进行全双工通信

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用WebSocket进行全双工通信相关的知识,希望对你有一定的参考价值。

参考技术A WebSocket 是 html5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

为了实现 WebSocket 通信,在 HTTP 连接建立之后,需要完成一 次“握手”(Handshaking)的步骤。

握手·请求

为了实现 WebSocket 通信,需要用到 HTTP 的 Upgrade 首部字 段,告知服务器通信协议发生改变,以达到握手的目的。

Sec-WebSocket-Key 字段内记录着握手过程中必不可少的键值。 Sec-WebSocket-Protocol 字段内记录使用的子协议。 子协议按 WebSocket 协议标准在连接分开使用时,定义那些连接 的名称。

177 握手·响应

对于之前的请求,返回状态码 101 Switching Protocols 的响应。

Sec-WebSocket-Accept 的字段值是由握手请求中的 SecWebSocket-Key 的字段值生成的。 成功握手确立 WebSocket 连接之后,通信时不再使用 HTTP 的数 据帧,而采用 WebSocket 独立的数据帧。

成功握手确立 WebSocket 连接之后,通信时不再使用 HTTP 的数 据帧,而采用 WebSocket 独立的数据帧。

javascript 可调用“The WebSocket API”,以下为调用 WebSocket API,每 50ms 发送一次数据的实例。

HTML5基于TCP的全双工通信协议WebSocket

我们从小就在学习分享,我们分享过糖果、分享过玩具,今天我们一起分享知识。


webSocket

  • 1.websocket背景简介

  • 2.websocket的客户端使用方式

  • 3.websocket服务端使用方式

1.websocket背景介绍

HTTP和WebSocket两者的差距不大

浏览网页时,经过三个过程

  • 1、浏览器经过三次握手与web服务器建立链接,

  • 2、web服务器返回响应

  • 3、浏览器通过四次握手主动断开链接

    因为第三步导致不能持久链接,那我们去掉第三步不就可以了实现持久链接了吗?这就是WebSocket与HTTP最大的不同(Web服务器是不会主动断开连接的),当然还有更多的数据封装格式的不同。 可以看到WebSocket是在HTTP上做的改动,有人曾经用单片机的TCP/IP协议栈封装符合HTTP协议格式的字符串,去连接Web服务器。WebSocket和HTML5没有多大关系。

    如果要搭建一个Web服务器,我们会有很多选择,市场上也有很多成熟的产品供我们应用,比如开源的Apache,安装后只需简单的配置(或者默认配置)就可以工作了。但是如果想搭建一个WebSocket服务器就没有那么轻松了,因为WebSocket是一种新的通信协议,目前还是草案,没有成为标准,市场上也没有成熟的WebSocket服务器或者Library实现WebSocket协议,我们就必须自己动手写代码去解析和组装WebSocket的数据包。要这样完成一个WebSocket服务器,估计所有的人都想放弃,幸好的是市场上有几款比较好的开源库供我们使用,我们可以调用这些接口,这在很大程度上减少了我们的工作量。

    百万websocket常连接的服务器

    Netty服务器

    Spray服务器

    node.js

    Websocket协议之握手连接

    一、协议包含两个部分,第一个是“握手”,第二个是数据传输。

    ws://127.0.0.1:8080 //不太安全

    wss://127.0.0.1:8080 //更安全

    类似于http https的区别

    二、握手(Opening > Closing Handshake)打开连接

    1、发送握手请求

    2、返回握手应答

    3、错误处理 所有数据传输都是UTF-8编码的数据,当一端接收到的字节流数据不是一个有效的UTF-8数据流,那么接收到的这一方必须要马上关闭连接。这个规则在开始握手一直到所有的数据交换过程都要进行验证。

2.websocket的客户端使用方式

浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。

那么简言之用户所使用的浏览器就看作为客户端(client)那么我们需要在html中利用Javascript去创建我们的客户端

由于是html5提供的WebSocket服务那么现在主流的浏览器都可以直接使用WebSocket。(ie8+均可使用)

书归正传

WebSocket 属性
以下是 WebSocket 对象的属性。假定我们使用了以上代码创建了 Socket 对象:
属性Socket.readyState 
只读属性 readyState 表示连接状态,可以是以下值:
0 - 表示连接尚未建立。
1 - 表示连接已建立,可以进行通信。
2 - 表示连接正在进行关闭。
3 - 表示连接已经关闭或者连接不能打开。
Socket.bufferedAmount   
只读属性 bufferedAmount 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数。
WebSocket 事件
以下是 WebSocket 对象的相关事件。假定我们使用了以上代码创建了 Socket 对象:
事件     事件处理程序              描述
open    Socket.onopen   连接建立时触发
message Socket.onmessage    客户端接收服务端数据时触发
error   Socket.onerror  通信发生错误时触发
close   Socket.onclose  连接关闭时触发
WebSocket 方法
以下是 WebSocket 对象的相关方法。假定我们使用了以上代码创建了 Socket 对象:
方法  描述
Socket.send()   
使用连接发送数据
Socket.close()  
关闭连接

1.需要先创建WebSocket 并且判断一下当前浏览器是否支持

var websocket = null;
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
    websocket = new WebSocket("ws://localhost:8080/webSocket/websocket");
} else {
    alert('当前浏览器 Not support websocket')
}

创建成功后利用WebSocket提供的方法去实现WebSocket的服务

//连接发生错误的回调方法
websocket.onerror = function() {
    setMessageInnerHTML("WebSocket连接发生错误");
};
//连接成功建立的回调方法
websocket.onopen = function() {
    setMessageInnerHTML("WebSocket连接成功");
}
//接收到消息的回调方法
websocket.onmessage = function(event) {
    setMessageInnerHTML(event.data);
}
//连接关闭的回调方法
websocket.onclose = function() {
    setMessageInnerHTML("WebSocket连接关闭");
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function() {
    closeWebSocket();
}
//将消息显示在网页上
function setMessageInnerHTML(innerHTML) {
    document.getElementById('message').innerHTML += innerHTML + '<br/>';
}
//关闭WebSocket连接
function closeWebSocket() {
    websocket.close();
}
//发送消息
function send() {
    var message = document.getElementById('text').value;
    websocket.send(message);
}

3.websocket服务端使用方式

那么根据对客户端的理解,java中的servlet也就是我们服务端,处理业务逻辑的地方

需要注意

因为使用websocket与传统的http请求是不同的那么在servlet就与之前学习过的有很大的不同

  • 不同一 注解不同

    以前我们访问一个servlet使用的是@WebServlet("/login.do")如果换成websocket方式需要改成@ServerEndpoint("/websocket")

  • 不同二 方法不同

    我们不在需要doGet(),doPost()这个两个方法了。因为webSocket是一种新的协议与传统http不同(前文提到过)

    /**
     * @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一
     * 个websocket服务器端,
     * 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接
     * 到WebSocket服务器端
     */
    @ServerEndpoint("/websocket")
    public class WebSocket {
        // 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
        private static int onlineCount;
        // concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,
        //其中Key可以为用户标识
        private static CopyOnWriteArraySet<WebSocket> webSocketSet = new CopyOnWriteArraySet<WebSocket>();
        // 与某个客户端的连接会话,需要通过它来给客户端发送数据
        private Session session;
    
        /**
         * 连接建立成功调用的方法
         * @param session
         *  可选的参数。session为与某个客户端的连接会话,
         *  需要通过它来给客户端发送数据
         */
        @OnOpen
        public void onOpen(Session session) {
            this.session = session;
            webSocketSet.add(this); // 加入set中
            addOnlineCount(); // 在线数加
            System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
        }
    
        /**
         * 连接关闭调用的方法
         */
        @OnClose
        public void onClose() {
            webSocketSet.remove(this); // 从set中删除
            subOnlineCount(); // 在线数减
            System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
        }
    
        /**
         * 收到客户端消息后调用的方法
         * @param message
         * 客户端发送过来的消息
         * @param session
         * 可选的参数
         */
        @OnMessage
        public void onMessage(String message, Session session) {
            System.out.println("来自客户端的消息:" + message);
            // 群发消息
            for (WebSocket item : webSocketSet) {
                try {
                    item.sendMessage(message);
                } catch (IOException e) {
                    e.printStackTrace();
                    continue;
                }
            }
        }
    
        /**
         * 发生错误时调用
         * @param session
         * @param error
         */
        @OnError
        public void onError(Session session, Throwable error) {
            System.out.println("发生错误");
            error.printStackTrace();
        }
    
        /**
         * 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。
         * getBasicRemote() 与 getAsyncRemote()区别
         * getAsyncRemote是非阻塞式的
         * getBasicRemote阻塞模式
         * 也就是说如果使用getBasicRemote那么每一条信息都需要等待上一条信息发送完毕之后才能发送
         * ,反之区别getAsyncRemote是不需要的
         * @param message
         * @throws IOException
         */
        public void sendMessage(String message) throws IOException {
            this.session.getBasicRemote().sendText(message);
            // this.session.getAsyncRemote().sendText(message);
        }
    
        public static synchronized int getOnlineCount() {
            return onlineCount;
        }
    
        public static synchronized void addOnlineCount() {
            WebSocket.onlineCount++;
        }
    
        public static synchronized void subOnlineCount() {
            WebSocket.onlineCount--;
        }
    }



以上是关于使用WebSocket进行全双工通信的主要内容,如果未能解决你的问题,请参考以下文章

(chap9 基于HTTP的功能追加协议) WebSocket使用浏览器进行全双工通信

每日灵魂一问-对WebSocket的理解?应用场景?

网页打印时提示websocket没有准备好

WebSocket是什么,有什么作用和特点?

WebSocket是什么,有什么作用和特点?

Android最佳实践——深入浅出WebSocket协议