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
()