某某茶叶有限公司欢迎您!
金沙棋牌在线 > Web前端 > WebSocket 教程

WebSocket 教程

时间:2019-12-01 02:47

WebSocket 教程

2017/05/15 · 基础技术 · websocket

原文出处: 阮一峰   

WebSocket 是一种网络通信协议,很多高级功能都需要它。

本文介绍 WebSocket 协议的使用方法。

图片 1

WebSocket

提出正确的问题,往往等于解决了问题的大半。——海森堡

图片 2

Learn.png

  • ##### 百科定义

      WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并被RFC7936所补充规范。

  • ##### 同类技术

      Http协议。

  • ##### 对比同类的优缺点

    • 优点:

        对于http协议有长连接和短连接,短连接并且要实现三次握手,长连接要在一定时间内保持连接,并且http的head占用的字节比较大,传输速度慢,数据包大,如实时交互,服务器性能压力大,数据传输安全性差。而websocket传输数据为字节级,传输数据可自定义,数据量小对于手机应用讲:费用低),传输数据时间短,性能高,适合于客户端和服务器端之间信息实时交互,可以加密,数据安全性强。

    • 缺点:

        需对传输的数据进行解析,转化成应用级的数据;对开发人员的开发水平要求高;相对于Http协议传输,增加了开发量

  • ##### 组成部分

      客户端,服务端。

  • ##### 解决什么问题

      WebSocket支持推送服务,而http协议服务端永远是被动的,解决了http无法实现了服务端与客户端的实时交互。WebSocket的请求信息很短,而http协议的请求头head数据包很大,占用多余的带宽,降低服务端的性能压力。

      Http协议是无状态的协议,通俗的说就是,服务器因为每天要接待太多客户了,是个健忘鬼,你一挂电话,他就把你的东西全忘光了,把你的东西全丢掉了。你第二次还得再告诉服务器一遍。

    WebSocket 是什么原理?为什么可以实现持久连接?

  • ##### 没有这个技术前怎么做的

    • Polling(轮询)

        这种方式就是通过Browser/UA定时的向Web服务器发送http的Get请求,服务器收到请求后,就把最新的数据发回给客户端(Browser/UA),Browser/UA得到数据后,就将其显示出来,然后再定期的重复这一过程。虽然这样可以满足需求,但是也仍然存在一些问题,例如在某段时间内Web服务器端没有更新的数据,但是Browser/UA仍然需要定时的发送Get请求过来询问,那么Web服务器就把以前的老数据再传送过来,Browser/UA把这些没有变化的数据再显示出来,这样显然既浪费了网络带宽,又浪费了CPU的利用率。如果说把Browser发送Get请求的周期调大一些,就可以缓解这一问题,但是如果在Web服务器端的数据更新很快时,这样又不能保证Web应用程序获取数据的实时性。

    • Long Polling

        上面介绍了Polling遇到的问题,现在介绍一下LongPolling,它是对Polling的一种改进。
      Browser/UA发送Get请求到Web服务器,这时Web服务器可以做两件事情,第一,如果服务器端有新的数据需要传送,就立即把数据发回给Browser/UA,Browser/UA收到数据后,立即再发送Get请求给Web Server;第二,如果服务器端没有新的数据需要发送,这里与Polling方法不同的是,服务器不是立即发送回应给Browser/UA,而是把这个请求保持住,等待有新的数据到来时,再来响应这个请求;当然了,如果服务器的数据长期没有更新,一段时间后,这个Get请求就会超时,Browser/UA收到超时消息后,再立即发送一个新的Get请求给服务器。然后依次循环这个过程。
      这种方式虽然在某种程度上减小了网络带宽和CPU利用率等问题,但是仍然存在缺陷,例如假设服务器端的数据更新速率较快,服务器在传送一个数据包给Browser后必须等待Browser的下一个Get请求到来,才能传递第二个更新的数据包给Browser,那么这样的话,Browser显示实时数据最快的时间为2×RTT(往返时间),另外在网络拥塞的情况下,这个应该是不能让用户接受的。另外,由于http数据包的头部数据量往往很大(通常有400多个字节),但是真正被服务器需要的数据却很少(有时只有10个字节左右),这样的数据包在网络上周期性的传输,难免对网络带宽是一种浪费。

  • ##### 官方示例

      WebSocket-Web APIS
      WebSocket Client API

  • #### Demo

    • Js客户端代码

      var webSocket = new WebSocket('ws://localhost:8080/em/chat');
      
      webSocket.onerror = function(event) {
          onError(event)
      };
      
      webSocket.onopen = function(event) {
          onOpen(event)
      };
      //实时监听服务端反馈的信息
      webSocket.onmessage = function(event) {
          onMessage(event)
      };
      
      function onMessage(event) {
          document.getElementById('messages').innerHTML 
              += '<br />' + event.data;
      }
      
      function onOpen(event) {
          document.getElementById('messages').innerHTML 
              = 'Connection established';
          var text =document.getElementById('text').value;
          //想服务端传送数据
          webSocket.send(text);
      }
      
      function onError(event) {
          console.info(event.data);
      }
      
    • Java客户端代码

      @WebSocket(maxBinaryMessageSize = 60 * 1024)
      public class SimpleSocket
      {
          private final CountDownLatch closeLatch;
          @SuppressWarnings("unused")
          private Session session;
      
          private Map map = new HashMap<String,String>(16);
      
            public SimpleSocket()
            {
                this.closeLatch = new CountDownLatch(1);
            }

            public boolean awaitClose(int duration, TimeUnit unit) throws InterruptedException
            {
                return this.closeLatch.await(duration,unit);
            }

            @OnWebSocketClose
            public void onClose(int statusCode, String reason)
            {
                System.out.printf("Connection closed: %d - %s%n",statusCode,reason);
                this.session = null;
                this.closeLatch.countDown(); // trigger latch
            }

            @OnWebSocketConnect
            public void onConnect(Session session)
            {
                System.out.printf("Got connect: %s%n",session);
                this.session = session;
                try
                {
                    Future<Void> fut;

                    //向服务端传送指令
                    map.put("ACTION","REMIND");

                    JSONObject dataJson=new JSONObject(map);

                    fut = session.getRemote().sendStringByFuture(dataJson.toString());

                    fut.get(2,TimeUnit.SECONDS); // wait for send to complete.

                    session.close(StatusCode.NORMAL,"I'm done");
                }
                catch (Throwable t)
                {
                    t.printStackTrace();
                }
            }

            @OnWebSocketMessage
            public void onMessage(String msg)
            {
                //接受服务端接受的json字符串
                JSONObject dataJson=new JSONObject(msg);

                //判断指令
                if ("REMIND".equals(dataJson.get("ACTION"))) {

                    //// TODO: 2017/11/20
                }else{

                    //// TODO: 2017/11/20
                }

            }
        }

    `注:java客户端代码使用环境适于Jdk8`

-   服务端代码

        @ServerEndpoint("/api")
        public class WebSocketTestChat {
            Set<Session> session_list =null;
            @OnMessage
            public void onMessage(String message, Session session) 
                throws IOException, InterruptedException {

                JSONObject dataJson=new JSONObject(message);

                String action = (String) dataJson.get("ACTION");
                if(action.equals("REMIND")){
                    //// TODO: 2017/11/20

                }else{
                    //// TODO: 2017/11/20
                }
                session_list =session.getOpenSessions();
                for(Session s:session_list){
                    s.getBasicRemote().sendText(dataJson.toString());
                }
            }
            @OnOpen
            public void onOpen () {
            }

            @OnClose
            public void onClose () {
            }
        }
  • ##### 分享

      https://github.com/zhangqiaobo/WebSockets_demo

概述

一、为什么需要 WebSocket?

初次接触 WebSocket 的人,都会问同样的问题:我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来什么好处?

答案很简单,因为 HTTP 协议有一个缺陷:通信只能由客户端发起。

举例来说,我们想了解今天的天气,只能是客户端向服务器发出请求,服务器返回查询结果。HTTP 协议做不到服务器主动向客户端推送信息。

图片 3

这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。我们只能使用“轮询”:每隔一段时候,就发出一个询问,了解服务器有没有新的信息。最典型的场景就是聊天室。

轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。因此,工程师们一直在思考,有没有更好的方法。WebSocket 就是这样发明的。

WebSocket 是什么?

WebSocket 是一种网络通信协议。RFC6455 定义了它的通信标准。

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

二、简介

WebSocket 协议在2008年诞生,2011年成为国际标准。所有浏览器都已经支持了。

它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。

图片 4

其他特点包括:

(1)建立在 TCP 协议之上,服务器端的实现比较容易。

(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此不容易屏蔽,能通过各种 HTTP 代理服务器。

(3)数据格式比较轻量,性能开销小,通信高效。

(4)可以发送文本,也可以发送二进制数据。

(5)没有同源限制,客户端可以与任意服务器通信。

(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

ws://example.com:80/some/path

1
2
ws://example.com:80/some/path
 

图片 5

为什么需要 WebSocket ?

了解计算机网络协议的人,应该都知道:HTTP 协议是一种无状态的、无连接的、单向的应用层协议。它采用了请求/响应模型。通信请求只能由客户端发起,服务端对请求做出应答处理。

这种通信模型有一个弊端:HTTP 协议无法实现服务器主动向客户端发起消息。

这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。大多数 Web 应用程序将通过频繁的异步JavaScript和XML(AJAX)请求实现长轮询。轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。

图片 6

ajax-long-polling.png

因此,工程师们一直在思考,有没有更好的方法。WebSocket 就是这样发明的。WebSocket 连接允许客户端和服务器之间进行全双工通信,以便任一方都可以通过建立的连接将数据推送到另一端。WebSocket 只需要建立一次连接,就可以一直保持连接状态。这相比于轮询方式的不停建立连接显然效率要大大提高。

图片 7

websockets-flow.png

三、客户端的简单示例

WebSocket 的用法相当简单。

下面是一个网页脚本的例子(点击这里看运行结果),基本上一眼就能明白。

var ws = new WebSocket("wss://echo.websocket.org"); ws.onopen = function(evt) { console.log("Connection open ..."); ws.send("Hello WebSockets!"); }; ws.onmessage = function(evt) { console.log( "Received Message: " + evt.data); ws.close(); }; ws.onclose = function(evt) { console.log("Connection closed."); };

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var ws = new WebSocket("wss://echo.websocket.org");
 
ws.onopen = function(evt) {
  console.log("Connection open ...");
  ws.send("Hello WebSockets!");
};
 
ws.onmessage = function(evt) {
  console.log( "Received Message: " + evt.data);
  ws.close();
};
 
ws.onclose = function(evt) {
  console.log("Connection closed.");
};      
 

WebSocket 如何工作?

Web浏览器和服务器都必须实现 WebSockets 协议来建立和维护连接。由于 WebSockets 连接长期存在,与典型的HTTP连接不同,对服务器有重要的影响。

基于多线程或多进程的服务器无法适用于 WebSockets,因为它旨在打开连接,尽可能快地处理请求,然后关闭连接。任何实际的 WebSockets 服务器端实现都需要一个异步服务器。

四、客户端的 API

WebSocket 客户端的 API 如下。

WebSocket 客户端

在客户端,没有必要为 WebSockets 使用 JavaScript 库。实现 WebSockets 的 Web 浏览器将通过 WebSockets 对象公开所有必需的客户端功能(主要指支持 Html5 的浏览器)。

4.1 WebSocket 构造函数

WebSocket 对象作为一个构造函数,用于新建 WebSocket 实例。

var ws = new WebSocket('ws://localhost:8080');

1
2
var ws = new WebSocket('ws://localhost:8080');
 

执行上面语句之后,客户端就会与服务器进行连接。

实例对象的所有属性和方法清单,参见这里。

客户端 API

以下 API 用于创建 WebSocket 对象。

var Socket = new WebSocket(url, [protocol] );

以上代码中的第一个参数 url, 指定连接的 URL。第二个参数 protocol 是可选的,指定了可接受的子协议。

4.2 webSocket.readyState

readyState属性返回实例对象的当前状态,共有四种。

  • CONNECTING:值为0,表示正在连接。
  • OPEN:值为1,表示连接成功,可以通信了。
  • CLOSING:值为2,表示连接正在关闭。
  • CLOSED:值为3,表示连接已经关闭,或者打开连接失败。

下面是一个示例。

switch (ws.readyState) { case WebSocket.CONNECTING: // do something break; case WebSocket.OPEN: // do something break; case WebSocket.CLOSING: // do something break; case WebSocket.CLOSED: // do something break; default: // this never happens break; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
switch (ws.readyState) {
  case WebSocket.CONNECTING:
    // do something
    break;
  case WebSocket.OPEN:
    // do something
    break;
  case WebSocket.CLOSING:
    // do something
    break;
  case WebSocket.CLOSED:
    // do something
    break;
  default:
    // this never happens
    break;
}
 

WebSocket 属性

以下是 WebSocket 对象的属性。假定我们使用了以上代码创建了 Socket 对象:

属性 描述
Socket.readyState 只读属性 readyState 表示连接状态,可以是以下值:0 - 表示连接尚未建立。1 - 表示连接已建立,可以进行通信。2 - 表示连接正在进行关闭。3 - 表示连接已经关闭或者连接不能打开。
Socket.bufferedAmount 只读属性 bufferedAmount 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数。