Python3.6.0实现websocketserver
Python 3.6.0 实现 websocket server
⽹上的好多教程都是基于Python2.X的,虽然差不多,但是对于我们这些刚刚听说过webSocket的⼩⽩来说,微⼩的差异也会让我们debug半天,所以以此博客做我实现的记录,仅供后来者参考
需要⽤到的知识:
python模块:socket, struct,hashlib, threading
JavaScript websocket简单使⽤
chrome开发者⼯具(对于websocket的报错更加详细,利于debug)
⼀、 webSocket协议
1. sebsocket client向服务器发送握⼿请求
格式如下:
GET / HTTP/1.1\r\n
/省略不相关信息/
Sec-WebSocket-Key: G4cZeCrg+0Znd6MLvVJSTg==\r\n
Connection: keep-alive, Upgrade\r\n
Upgrade: websocket\r\n\r\n
Sec-WebSocket-Key对应的键值由websocket client 随机⽣成;Connection: keep-alive, Upgrade表⽰⽹络协议升级(upgrade);Upgrade: websocket表⽰将协议升级为websocket连接协议
2. sebsocket server向client 返回基于Sec-WebSocket-Key的Sec-WebSocket-Accept
Magic_string = 258EAFA5-E914-47DA-95CA-C5AB0DC85B11(固定)
combined_string = Sec-WebSocket-Key + Magic_string
对 combined_String 取sha1数字摘要,然后进⾏base64编码,得到Sec-WebSocket-Accept_str
webserver接口开发返回格式
HTTP/1.1 101 Web Socket Protocol Handshake
/省略不相关信息/
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: Sec-WebSocket-Accept_str
3.websocket握⼿部分完成,现在可以进⾏双全⼯通信
3.1 client部分由javaScript完成,后⾯会进⾏详细的介绍
3.2 server端发送websocekt报⽂
向客户端发送的websocket报⽂分为3部分:
固定部分 ‘\x81’
报⽂内容长度
⼩于127, 填充8bit表⽰内容长度
⼩于2^16-1, 填充第⼀个8bit为126的⼗六进制表⽰,后⾯16bit表⽰内容长度
⼩于2^64-1, 填充第⼀个8bit为127的⼗六进制表⽰,后⾯64bit表⽰内容长度
报⽂内容
将三部分有序组装即可使⽤socket.send()发送给哭护短
3.3 server端解析websocekt报⽂
客户端发送⾄server的websocket报⽂分为四部分:
固定部分 ‘\x81’
报⽂内容长度(同上⽂”报⽂内容长度”)
掩码mask
mask由四字节组成
报⽂内容content
获得掩码mask和content,注意报⽂内容长度不同会影响mask和content在websocket报⽂中的起始位置
对content进⾏按字节循环与处理(python描述):
result = ""
i = 0
for d in content:
result += d ^ chr(d ^ ord(mask[i%4]))
i += 1
得到result即为client发送到server的数据
⼆、构建websocket客户端
为了能够使⼤家先体验⼀把websocket的乐趣,同时也可以为后⾯server构建过程中能够有debug参照,⾸先实现基于JavaScript的websocket的客户端
1.JavaScript事件驱动模型
简单理解就是,⽆阻塞,当发⽣A事件时,⾃动调⽤B函数,处理A事件。在js中,实现这⼀机制的就是回调函数的使⽤。样例:
var ws = new websocket("ws://127.0.0.1:8124");
<("error", function(e){
console.ssage);
}):
样例第⼀⾏表⽰创建websocket对象
样例第⼆⾏⾄第四⾏,error为关键字,function(e){…}即为回调函数。表⽰,当ws发⽣错误时,调⽤function(e){…}对错误进⾏处理
2. 创建websocket对象
var ws = new websocket("ws://127.0.0.1:8124");
解释url字段
ws 表⽰使⽤websocket协议,与http/https相似
url,即表⽰⽬的地址 ⽬的端⼝
3. 完整代码
<!DOCTYPE html>
<html>
<head>
<title>websocket</title>
</head>
<body>
<script type="text/javascript">
var ws;
function startWS() {
console.log('start once again');
// ws = new WebSocket("ws://127.0.0.1:8124");
ws = new WebSocket("ws://");
console.log('webSocket opened');
};
console.log('receive message : ' + message.data);
};
console.log('error :' + error.name + error.number);
};
console.log('webSocket closed');
};
}
function sendMessage () {
console.log("sending a message");
ws.send("websocket from js");
}
</script>
<button onclick="startWS()">createWebsocket</button><br>
<button onclick="sendMessage()">sendMessage</button>
</body>
</html>
将上述代码保存问xxx.html⽂件,即可使⽤浏览器打开。可在浏览器“开发者⼯具”->控制台console中进⾏查看client运⾏情况
三、 python模块解析
1. struct模块
struct.pack(fmt, value1, value2)
fmt为由特定字符组成的字符串,函数功能为,将python数据类型value1,value2转化为C数据类型fmt字符类型:
Format C Type Python type Standard size
x pad byte no value
c char bytes of length 1
b signed char integer 1
B unsigned char integer 1
_Bool bool 1
h short integer 2
H unsigned short integer 2
i int integer 4
I unsigned int integer 4
l long integer 4
L unsigned long integer 4
q long long integer 8
Q unsigned long long integer 8
n ssize_t integer
N size_t integer
e (7) float 2
f float float 4
d doubl
e float 8
s char[] bytes
p char[] bytes
P void * integer (6)
struct.unpack(fmt, buffer)
即为struct.pack(fmt, value..)操作的逆操作
详见
2. 其他知识
python分⽚
字符串:替换,⼦字符串,查
str <=> bytes
四、 python websocet server实现
1. 创建主线程,⽤于实现接受websocket建⽴请求
if __name__ == "__main__":
serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = ("127.0.0.1", 8124)
serverSocket.bind(host)
serverSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
serverSocket.listen(5)
print("server running")
while True:
print("getting connection")
clientSocket, addressInfo = serverSocket.accept()
print("get connected")
receivedData = v(2048))
# print(receivedData)
entities = receivedData.split("\\r\\n")
Sec_WebSocket_Key = entities[11].split(":")[1].strip() + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
print("key ", Sec_WebSocket_Key)
response_key = base64.b64encode(hashlib.sha1(bytes(Sec_WebSocket_Key, encoding="utf8")).digest())
response_key_str = str(response_key)
response_key_str = response_key_str[2:30]
# print(response_key_str)
response_key_entity = "Sec-WebSocket-Accept: " + response_key_str +"\r\n"
clientSocket.send(bytes("HTTP/1.1 101 Web Socket Protocol Handshake\r\n", encoding="utf8"))
clientSocket.send(bytes("Upgrade: websocket\r\n", encoding="utf8"))
clientSocket.send(bytes(response_key_entity, encoding="utf8"))
clientSocket.send(bytes("Connection: Upgrade\r\n\r\n", encoding="utf8"))
print("send the hand shake data")
强调多次调⽤clientSocket.send():因为socket.send()认为”\r\n”即为结束标记,所以对于websocket报⽂中要求的换
⾏”\r\n”,我们要多次调⽤clientSocket.send()⽅法将报⽂⼀⾏⼀⾏的发送出去,这也是与python2.x中构建websocket server中很重要的⼀点,笔者在此处踩坑
下⽂代码是笔者根据浏览器发送的handshake请求获得Sec_WebSocket_Key的⽅法,可能在不同的环境中会有差异,调试是可全部打印出websocket请求报⽂,即*取消注释 “print(receivedData)”*
Sec_WebSocket_Key = entities[11].split(“:”)[1].strip()
如何验证⾃⼰⽣成的Sec_WebSocket_Accept是正确的。上⽂提到构建websocket client。可打开“开发者⼯具”->“⽹络
network”,然后点击”createWebsocket”按钮,得到浏览器发送的报⽂与回复报⽂,可以到⼀对正确的
(Sec_WebSocket_Key, Sec_WebSocket_Accpet)。使⽤⾃⼰的Sec_WebSocket_Accept⽣成代码将Sec_WebSocket_Key加密,得到结果与正确Sec_WebSocket_Accept相⽐较,即可确认⾃⼰的Sec_WebSocket_Accept⽣成是否错误
2. 与websocket client 通信
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论