WebSocket 简单介绍和使用

今天我们来和大家聊聊HTML5Web Socket

什么样的背景让这些疯狂的程序员们想出了WebSocket

在正式解释Web Socket之前,我们先来看这样一个场景:假设我们现在正在一个网站上看股价的实时变化图 ,我们希望能够及时得到股票的变化信息,那么从技术角度来讲,我们就需要及时得到server端数据变化的情况。一个简单的解决方案就是和PM argue,让user去不停地刷新页面,可惜很显然这样做的后果很可能会被PM拉去祭天,哈哈。

在和PM讨论无果败下阵来之后,我们该怎么办呢?因为HTTP连接是一个半双工的连接,它是基于request/response这样的形式来进行数据传输的,要想在这个技术上面实现实时的数据变化,我们无非有这样几种思路,一种就是我们定期进行轮询,发送request过去,等待response的到来。另外一种就是把一次connection的时间放长,让他能够在有更新时及时发送回来。基于这样的基础,有了以下几种实现:

轮询(polling)

轮询就是我们定期发送request,立即接受response。看起来还不错,但很显然问题就出在这个定期上面,因为我们并不知道server端的数据是什么时候能好,所以很难设置这个轮询的interval。假如设置小了,轮询太频繁,会有很多无效的request。假如设置大了,轮询的频率太低,就无法及时得到server端的数据更新了。

长轮询(long polling

所谓长轮询就是在一定时间内没有response就保持连接存在,就像乞讨一样,不给钱就不走。当然计算机还没有这么无赖,在一定时间没有response,也会timeout。这种方法就解决了上面轮询设置interval的苦恼,如是能设置一个合理的timeout,也不需要担心有太多无效的request了。他的模型如下图一所示:

图一 长轮询示意图

毫无疑问,长轮询解决了之前轮询中的一些问题,但是仍然有很多问题没有解决,比如说假设我们的数据更新比较频繁,那么长轮询和轮询基本就没有差别了,这样我们为了获取及时的数据,就需要不停地发送request,这必然会有performanceissue

流(stream

既然长轮询也有问题,天才的ITers显然不会止步于此,你不是说我总是发送request会让performance不好,那好,我建立连接之后就不close了,一直在等待数据的刷新,直到被关闭或者超时(比如防火墙丢弃时间过长的连接,或者服务器端的timeout等)。流模式的模型如下图二所示

图二 流模式的示意图

那么这样的流模式是否就是完美无缺的呢,事实上也不是,因为流模式依然基于HTTP,他很有可能被防火墙或者代码bufferresponse从而导致latency,这时我们就需要选择TLS(SSL)来避免responsebuffer,在这种情况下连接的建立和关闭就会很重。另外一个问题就是,这仍然是一个半双工的情况,就是client如果想在连接过程中发送数据给server,仍然只能建立一个新的连接。

什么是WebSocket

好了,讲了这么多,该是时候回归到本文想讲的内容了,WebSocket

我想不用我多说,大家应该可以猜到了,websocket就是用来解决上面这些问题的。WebSocket是一个全双工的双向的通信通道。只需要建立一次连接,可以一直保持,而且是双向都可以主动进行通信。WebSocket的模式示意图如下图三所示:

图三 websocket的模式示意图

WebSocket的特点

WebSocket有以下几个特点:

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

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

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

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

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

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

WebSocket Client如何使用

我们从图三可以看出,WebSocket其实有Client端和Server端两块,那么Client端是如何编程的呢,首先我们第一步需要和server端建立一个连接,建立连接的代码如下:

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

其中第一个url就是我们要连接的url,也就是server端的url。第二个参数protocol是可选的,指定了可接受的子协议。

在我们使用了上面的代码建立了连接之后,我们可以通过Socket.send()的方法主动向server发送message,也可以通过Socket.close()来关闭连接了。

除了主动出击外,我们还可以设置各种事件的回调函数,用来处理连接的开关,出错以及收到message等事件,websocket支持的事件如下表所示:

事件

事件处理程序

描述

Open

Socket.onopen

连接建立时触发

message

Socket.onmessage

客户端接收到服务端的数据时触发

error

Socket.onerror

通信发生错误时触发

close

Socket.onclose

连接关闭时触发

代码示例

这里我们使用一个websocket.org已经预先搭好的server来进行测试,它是一个回显的server,就是client端发送什么message过去,就会显示什么message回来。

 

              // 打开一个 web socket
               var ws = new WebSocket("wss://echo.websocket.org");
                
               ws.onopen = function()
               {
                  // Web Socket 已连接上,使用 send() 方法发送数据
                  ws.send("发送数据");
                  alert("数据发送中...");
               };
                
               ws.onmessage = function (evt) 
               { 
                  var received_msg = evt.data;
                   console.info(received_msg);
                  alert("数据已接收...");
               };
                
               ws.onclose = function()
               { 
                  // 关闭 websocket
                  alert("连接已关闭..."); 
               };

最终我们可以在console中看到我们发送的message: “发送数据被回发到client端。

Websocket server端的使用

Websocket在服务器端的实现非常丰富,Node.js, Java, C++,Python, C#等多种语言都有自己的解决方案。简单例一些实现的参考:

  1. php – http://code.google.com/p/phpwebsocket/
  2. jetty – http://jetty.codehaus.org/jetty/ (版本7开始支持websocket)
  3. netty – http://www.jboss.org/netty
  4. ruby – http://github.com/gimite/web-socket-ruby
  5. Kaazing – http://www.kaazing.org/confluence/display/KAAZING/Home
  6. Tomcat – http://tomcat.apache.org/ (7.0.26支持websocket)
  7. WebLogic – http://www.oracle.com/us/products/middleware/cloud-app-foundation/weblogic/overview/index.html (12.1.2 开始支持)
  8. node.js – https://github.com/Worlize/WebSocket-Node
  9. node.js – http://socket.io
  10. nginx – http://nginx.com/
  11. mojolicious – http://mojolicio.us/

对于C#来说,主要的实现方法有以下几种

  1. 使用.NET 自带的WebSocket (4.5),这种方法实现使用HttpListener,然后通过context中的AcceptWebSocketAsync来得到对应的websocket,具体大家可以参见后面的参考文章
  2. 使用websocket-sharp dll提供的WebSocket来实现

这个其实是我个人比较推荐的,只要写以下几行代码就可以实现回显了

 

public class Laputa : WebSocketBehavior
	        {
	            protected override void OnMessage(MessageEventArgs e)
	            {
	                var name = Context.QueryString["name"];
	                Send(!name.IsNullOrEmpty() ? String.Format("\"{0}\" to {1}", e.Data, name) : e.Data);
	            }
	        }
	
	        static void Main(string[] args)
	        {
	            Console.WriteLine("Hello World!");
	
	            var ws = new WebSocketServer("ws://localhost:1321");
	            ws.AddWebSocketService<Laputa>("/Laputa");
	            ws.Start();
	
	            Console.WriteLine("\nPress Enter key to stop the server...");
	            Console.ReadLine();
	
	            ws.Stop();
	        }

client端,我们只要把websocket连接的url改成下面这样就可以了:

var ws = new WebSocket("ws://localhost:1321/Laputa");

更多的接口,可以参见相关的文档。

至此,我们就把websocket出现和使用简单介绍完毕了,若是有什么错误的地方,欢迎大家指出。


转载请注明出处:http://www.softlifelogging.com/2018/06/08/websocket-简单介绍和使用/

更多精彩内容,敬请关注公众号: 随手记生活

4 Comments

  • Anonymous June 10, 2018 Reply

    的确就是简单的介绍

    • Gordon June 10, 2018 Reply Author

      是的,高阶应该还是需要大家在真实使用场景中去探索

  • 开发者头条 June 11, 2018 Reply

    感谢分享!已推荐到《开发者头条》:https://toutiao.io/posts/7srszv 欢迎点赞支持!使用开发者头条 App 搜索 347032 即可订阅《随手记生活》

    • Gordon June 11, 2018 Reply Author

      感谢推荐!

Leave a Reply

Your email address will not be published.