初试WebSocket构建聊天程序

上一篇文章中使用了Ajax long polling实现了一个简单的聊天程序,对于web实时通讯,今天就来试用一下基于WebSocket的长链接方式。html

WebSocket简介

为了加强web通讯的功能,在HTML5中,提供了WebSocket,它不单单是一种web通讯方式,也是一种应用层协议。web

WebSocket提供了客户端和服务端之间的双全工跨域通讯,经过客户端和服务端之间创建WebSocket链接(其实是TCP链接,后面会看到),在同一时刻可以实现客户端到服务器和服务器到客户端的数据发送。跨域

Ajax long polling是一种客户端去服务端拉取数据的方式,而WebSocket则能真正实现服务端主动向客户端推送数据。下图形象的展现了WebSocket的工做方式。浏览器

对于WebSocket这种新的应用层协议,在实现应用的时候,客户端和服务端都须要遵循WebSocket协议,关于更多的WebSocket内容,请参考websocket.org服务器

实现

首先,仍是先看看经过WebSocket实现的聊天程序的代码以及效果,而后再看WebSocket工做方式相关的内容。websocket

客户端

由于并非全部版本的浏览器都可以支持WebSocket,因此例子中经过下面代码来检测当前浏览器是否支持WebSocket。app

if(window.WebSocket){
    //support WebSocket, more code here
}
else{
    alert("WebSocket was not supported");
}

对于客户端,主要就是updater这个对象,该对象会建立并维护了一个WebSocket对象,经过这个WebSocket对象就能够跟服务端进行交互(收取或发送消息)。socket

var updater = {
     socket: null,

     start: function() {
         var url = "ws://" + location.host + "/chatsocket";
         updater.socket = new WebSocket(url);
         
         updater.socket.onopen = function(event) {
         }
         
         updater.socket.onclose = function(event) {
             alert("server socket closed");
         }
         
         updater.socket.onmessage = function(event) {
             updater.showMessage(event.data);
         }
     },

     showMessage: function(message) {
         console.log(message);
         $("#inbox").append(message);
         $("#message").val("");
     }
 };

服务端

对于服务端,此次使用了gevent-websocket这个库,能够很方便的经过pip进行安装。url

服务端经过MessageBuffer这个类来管理全部的消息,以及全部的WebSocket client。因为WebSocket是一种长链接的方式,因此能够很容易的统计出当前在线的client的数量。spa

class MessageBuffer(object):
    def __init__(self, cache_size = 200):
        self.cache = []
        self.cache_size = cache_size
        self.clients = []        

    def new_message(self, msg):
        self.cache.append(msg)
        if len(self.cache) > self.cache_size:
            self.cache = self.cache[-self.cache_size:]
            
    def update_clients(self, msg):
        for client in self.clients:
            client.send(msg)    

跟上次相比,使用WebSocket以后,服务器代码更加简洁了。当客户端发起"/chatsocket"请求后,服务器就会跟客户端创建链接,并将客户端加入"messageBuffer.clients"列表中;当客户端断开链接,就会将客户端从"messageBuffer.clients"列表中移除。

当服务器收到新消息后,就会经过"messageBuffer.update_clients"方法,将新消息推送到全部的客户端。

def application(env, start_response):
    # visit the main page
    if env['PATH_INFO'] == '/':
        # some code to load main page here
    
    elif env['PATH_INFO'] == '/chatsocket':
        ws = env["wsgi.websocket"]
        messageBuffer.clients.append(ws)
        print "new client join, total client count %d" %len(messageBuffer.clients)
        while True:
            message = ws.receive()
            if message is None:
                messageBuffer.clients.remove(ws)
                print "client leave, total client count %d" %len(messageBuffer.clients)
                break
            print "Got message: %s" %message
            message = "<div>{0}</div>".format(message)
            messageBuffer.new_message(message)
            messageBuffer.update_clients(message)

运行效果

下面就是代码的运行效果。

因为WebSocket是长链接的方式,因此能够方便的统计当前在线客户端数量。

当关闭服务器的时候,客户端也能够检测到链接的断开。

WebSocket工做机制

下面就从工做机制来看看WebSocket是怎么为应用提供长链接服务的。

WebSocket链接创建

虽然WebSocket是一种新的应用层协议,可是它的工做也是要依赖于http协议的。

经过Wireshark,咱们能够抓到下面的数据包。

这两个数据包就是创建WebSocket链接的握手过程(WebSocket Protocol handshake):

1. 客户端的WebSocket实例绑定一个须要链接到的服务器地址,当客户端链接服务端的时候,会向服务端发送一个相似下面的HTTP GET请求

在上面的请求中有一个Upgrade首部,这个首部是告诉服务端须要将通讯协议切换到WebSocket

2. 在收到带有"Upgrade: websocket"首部的请求后,若是服务端支持WebSocket协议,那么它就会将本身的通讯协议切换到WebSocket,同时发给客户端相似如下的响应报文头。

响应报文的状态码为101,表示赞成客户端协议转换请求,并将它转换为WebSocket协议。到此,客户端和服务端的WebSocket链接就创建成功了,之后的通讯就是基于WebSocket链接了。

WebSocket链接保活

WebSocket底层的工做/实现都是基于TCP协议,因此链接的保活机制是跟TCP同样的,就是经过"TCP Keep-Alive"心跳包来保证链接始终处于有效状态。

WebSocket链接关闭

对于WebSocket链接的关闭,也是主动关闭端发送"FIN, ACK"数据包来完成关闭的。

总结

本文简单介绍了HTML5中的WebSocket协议,并经过WebSocket实现了一个简单的聊天程序。

WebSocket能在客户端和服务端创建长链接,并提供全双工的数据传输,提供了服务器推送数据的模式。

跟Ajax long polling方式进行对比,这种服务器主动推送数据的方式更加适合实时数据交互应用。

Ps:

经过此处能够下载例子的源码。

相关文章
相关标签/搜索