一、什么是 WebSocket ?

 

WebSocket 是一种标准协议,用于在客户端和服务端之间进行双向数据传输。但它跟 HTTP 没什么关系,它是基于 TCP 的一种独立实现。

以前客户端想知道服务端的处理进度,要不停地使用 Ajax 进行轮询,让浏览器隔个几秒就向服务器发一次请求,这对服务器压力较大。另外一种轮询就是采用 long poll 的方式,这就跟打电话差不多,没收到消息就一直不挂电话,也就是说,客户端发起连接后,如果没消息,就一直不返回 Response 给客户端,连接阶段一直是阻塞的。

而 WebSocket 解决了 HTTP 的这几个难题。当服务器完成协议升级后( HTTP -> WebSocket ),服务端可以主动推送信息给客户端,解决了轮询造成的同步延迟问题。由于 WebSocket 只需要一次 HTTP 握手,服务端就能一直与客户端保持通信,直到关闭连接,这样就解决了服务器需要反复解析 HTTP 协议,减少了资源的开销。

 

 

 

 

主要使用场景:

 

没有其他能像 WebSocket 一样实现全双工传输的技术了,迄今为止,大部分开发者还是使用 Ajax 轮询来实现,但这是个不太优雅的解决办法,WebSocket 虽然用的人不多,可能是因为协议刚出来的时候有安全性的问题以及兼容的浏览器比较少,但现在都有解决。如果你有这些需求可以考虑使用 WebSocket:

 

  1. 多个用户之间进行交互;
  2. 需要频繁地向服务端请求更新数据。

 

比如弹幕、消息订阅、多玩家游戏、协同编辑、股票基金实时报价、视频会议、在线教育等需要高实时的场景。

 

**主要还是:消息推送

 

 

 

实现一个简单的聊天室程序,代码如下:

#-*- coding:utf8 -*-

import threading
import hashlib
import socket
import base64

global clients
clients = {}

#通知客户端
def notify(message):
   for connection in clients.values():
       connection.send('%c%c%s' % (0x81, len(message), message))

#客户端处理线程
class websocket_thread(threading.Thread):
   def __init__(self, connection, username):
       super(websocket_thread, self).__init__()
       self.connection = connection
       self.username = username
    
   def run(self):
       print 'new websocket client joined!'
       data = self.connection.recv(1024)
       headers = self.parse_headers(data)
       token = self.generate_token(headers['Sec-WebSocket-Key'])
       self.connection.send('\
HTTP/1.1 101 WebSocket Protocol Hybi-10\r\n\
Upgrade: WebSocket\r\n\
Connection: Upgrade\r\n\
Sec-WebSocket-Accept: %s\r\n\r\n' % token)
       while True:
           try:
               data = self.connection.recv(1024)
           except socket.error, e:
               print "unexpected error: ", e
               clients.pop(self.username)
               break
           data = self.parse_data(data)
           if len(data) == 0:
               continue
           message = self.username + ": " + data
           notify(message)
            
   def parse_data(self, msg):
       v = ord(msg[1]) & 0x7f
       if v == 0x7e:
           p = 4
       elif v == 0x7f:
           p = 10
       else:
           p = 2
       mask = msg[p:p+4]
       data = msg[p+4:]
       return ''.join([chr(ord(v) ^ ord(mask[k%4])) for k, v in enumerate(data)])
        
   def parse_headers(self, msg):
       headers = {}
       header, data = msg.split('\r\n\r\n', 1)
       for line in header.split('\r\n')[1:]:
           key, value = line.split(': ', 1)
           headers[key] = value
       headers['data'] = data
       return headers

   def generate_token(self, msg):
       key = msg + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
       ser_key = hashlib.sha1(key).digest()
       return base64.b64encode(ser_key)

#服务端
class websocket_server(threading.Thread):
   def __init__(self, port):
       super(websocket_server, self).__init__()
       self.port = port

   def run(self):
       sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
       sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
       sock.bind(('127.0.0.1', self.port))
       sock.listen(5)
       print 'websocket server started!'
       while True:
           connection, address = sock.accept()
           try:
               username = "ID" + str(address[1])
               thread = websocket_thread(connection, username)
               thread.start()
               clients[username] = connection
           except socket.timeout:
               print 'websocket connection timeout!'

if __name__ == '__main__':
   server = websocket_server(9000)
   server.start()

 

测试页面:

<!--
@http://www.cnblogs.com/zhuweisky/p/3930780.html
-->
<!DOCTYPE html>
</html>
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        <h3>WebSocketTest</h3>
        <div id="login">
            <div>
                <input id="serverIP" type="text" placeholder="服务器IP" value="127.0.0.1" autofocus="autofocus" />
                <input id="serverPort" type="text" placeholder="服务器端口" value="9000" />
                <input id="btnConnect" type="button" value="连接" οnclick="connect()" />
            </div>
            <div>
                <input id="sendText" type="text" placeholder="发送文本" value="I'm WebSocket Client!" />
                <input id="btnSend" type="button" value="发送" οnclick="send()" />
            </div>
            <div>
                <div>
                   来自服务端的消息
                </div>
                <textarea id="txtContent" cols="50" rows="10" readonly="readonly"></textarea>
            </div>
        </div>
    </body>
    <script>
        var socket;

        function connect() {
            var host = "ws://" + $("serverIP").value + ":" + $("serverPort").value + "/"
            socket = new WebSocket(host);
            try {

                socket.onopen = function (msg) {
                    $("btnConnect").disabled = true;
                    alert("连接成功!");
               };

                socket.onmessage = function (msg) {
                    if (typeof msg.data == "string") {
                        displayContent(msg.data);
                   }
                    else {
                        alert("非文本消息");
                   }
               };