1.1.1 WebSocket概述
WebSockets通过一次握手建立连接,服务器就可以主动地向客户端发送消息。
一举淘汰了Comet和Ajax轮询(polling),长轮询(long-polling),已经流(streaming)解决方案的。 在全双工的实时浏览器通信中,大大减少了网络流量并降低了网络延迟。
1.1.2 传统实时HTTP应用程序的复杂性
如图所示
传统的实时,体验的代价非常高,包括额外的时间延迟,不必要的网络流量和CPU性能消耗。
1.1.3 WebSocket握手
为了建立WebSocket通信,客户端和服务器在初始握手时,将HTTP协议升级到WebSocket协议
1.2 简单的WebSocket服务器
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import asyncore
import socket
import struct
import time
from hashlib import sha1
from base64 import encodestring
class WebSocketConnection(asyncore .dispatcher_with_send):
#初始化类
def __init__(self , conn, server):
asyncore .dispacher_with_send.__init__(self , conn) #每一个WebSocket连接都是一个支持缓冲发送的一步套接字包装程序
self .server = server
self .server. session.append (self)
self .readystat = "connecting"
self .buffer = ""
#读取来自客户端的数据
def handle_read(self ):
data = self. recv(1024 ) #接受客户端发来的数据
self .buffer += data #接收到的数据添加到缓冲区中
if self. readystat == "connecting":
self .parse_connnecting() #如果是连接状态,则进行连接数据解析
elif self. readystat == "open":
selft .parse_frametype() #如果是打开状态,
解析数据帧类型
#关闭连接
def handle_close(self ):
self .server. sessions.remove (self)
self .close()
#解客户端的连接数据
def parse_connnecting(self ):
header_end =
self.buffer.find ("\r\n\r\n") #计算数据结束的位置
if header_end == -1 :
return
else:
header = self. buffer[:header_end ] #取开头的数据
self .buffer = self.buffer [header_end + 4:] #删除开头的数据及\r\n\r\n
header_lines = header.split("\r\n" )
headers = {}
#HTTP请求header数据类似
#GET /chat HTTP/1.1 (第一行数据)
#Host: example.com
#...
method , path, protocal = header_lines [0]. split("
" )
if method != "GET" or protocal != "HTTP/1.1" or path[ 0] != "/":
self .terminate() #格式有误,终止连接
return
#解析头部信息key,value,类似Host:
example.com
for line in header_lines[1 :]:
key , value = line.split (":
")
headers [key] = value
headers ["Location"] = "ws://" + headers["Host" ] + path
self .readystate = "open"
self .handler = self.server .handlers.get(path , None )(self)
if "Sec-WebSocket-Key1" in headers.keys ():
self .send_server_handshake_76(headers)
else:
self .send_server_handshake_75(headers)
#终止连接
def terminate(self ):
self .ready_state = "closed"
self .close()
#v.76版本的websocket协议返回信息
def send_server_handshake_76(self , headers):
"""
Send the WebSocket Protocol v.76 handshake response
"""
key1 = headers["Sec-WebSocket-Key1" ]
key2 = headers["Sec-WebSocket-Key2" ]
# read additional 8 bytes from
buffer
key3 , self. buffer = self .buffer[: 8], self .buffer[ 8:]
#应答的token
response_token = self. calculate_key(key1 , key2, key3)
# write out response headers
self .send_bytes("HTTP/1.1
101 Web Socket Protocol Handshake\r\n")
self .send_bytes("Upgrade:
WebSocket\r\n" )
self .send_bytes("Connection:
Upgrade\r\n" )
self .send_bytes("Sec-WebSocket-Origin:
%s\r\n" % headers["Origin"])
self .send_bytes("Sec-WebSocket-Location:
%s\r\n" % headers["Location"])
if "Sec-WebSocket-Protocol" in headers:
protocol = headers["Sec-WebSocket-Protocol" ]
self .send_bytes("Sec-WebSocket-Protocol:
%s\r\n" % protocol)
self .send_bytes("\r\n" )
# write out hashed response
token
self .send_bytes(response_token)
#计算key
def calculate_key(self , key1, key2, key3 ):
# parse keys 1 and 2 by extracting
numerical characters
num1 = int( "".join ([digit for digit in list (key1) if digit.isdigit()]))
spaces1 = len([ char for char in list( key1) if char == "
" ])
num2 = int( "".join ([digit for digit in list (key2) if digit.isdigit()]))
spaces2 = len([ char for char in list( key2) if char == "
" ])
combined = struct.pack(">II" , num1/ spaces1, num2 /spaces2) + key3
# md5 sum the combined bytes
return hashlib.md5(combined ).digest()
#v.75版本的websocket协议返回信息
def send_server_handshake_75(self , headers):
"""
Send the WebSocket Protocol v.75 handshake response
"""
self .send_bytes("HTTP/1.1
101 Web Socket Protocol Handshake\r\n")
self .send_bytes("Upgrade:
WebSocket\r\n" )
self .send_bytes("Connection:
Upgrade\r\n" )
self .send_bytes("WebSocket-Origin:
%s\r\n" % headers["Origin" ])
self .send_bytes("WebSocket-Location:
%s\r\n" % headers["Location"])
if "Protocol" in headers:
self .send_bytes("WebSocket-Protocol:
%s\r\n" % headers["Protocol"])
self .send_bytes("\r\n" )
#解析 数据帧类型
def parse_frametype(self ):
while len( self.buffer ):
type_byte = self. buffer[0 ]
if type_byte == "\x00":
if not self.parse_textframe ():
return
#解析 文本帧
def parse_textframe(self ):
terminator_index = self. buffer.find ("\xFF")
if terminator_index != -1 :
frame = self. buffer[1 :terminator_index]
self .buffer = self.buffer [terminator_index+1:]
s = frame. decode("UTF8" )
self .handler.dispatch(s )
return True
else:
# incomplete frame
return false
#发送数据
def send(self , s):
if self. readystate == "open": #打开状态下,才发送
self .send_bytes("\x00" ) #发送数据头部
self .send_bytes(s.encode ("UTF8"))
self .send_bytes("\xFF" ) #发送数据尾部
def send_bytes(self , bytes):
asyncore .dispatcher_with_send.send(self , bytes)
#输出控制器
class EchoHandler(object ):
"""
The EchoHandler repeats each incoming string to the same websocket
"""
def __init__(self , conn):
self .conn = conn
def dispatch(self , data):
try:
self .conn. send(data ) #发送数据
except:
pass
#web服务
class WebSocketServer(asyncore .dispatcher):
def __init__(self , port= 80, handlers =None):
asyncore .dispatcher.__init__(self )
self .handlers = handlers
self .sessions = []
self .port = port
self .create_socket(socket.AF_INET , socket.SOCK_STREAM)
self .set_reuse_addr()
self .bind(( "", port ))
self .listen( 5)
def handle_accept(self ):
conn , addr = self.accept ()
session = WebSocketConnection(conn, self ) #处理websocket服务
#主函数
if __name__ == "__main__":
print "Starting
WebSocket Server"
WebSocketServer(port =8080, handlers={"/echo" : EchoHandler})
asyncore.loop ()