基于socket实现websocket服务
websocket原理
首先,Websocket是一个持久化的协议,相对于HTTP这种非持久的协议来说。
HTTP的生命周期通过 Request
来界定,也就是一个 Request
一个 Response
,那么在 HTTP1.0
中,这次HTTP请求就结束了。
在HTTP1.1中进行了改进,使得有一个keep-alive,也就是说,在一个HTTP连接中,可以发送多个Request,接收多个Response。但是请记住 Request = Response
, 在HTTP中永远是这样,也就是说一个request只能有一个response。而且这个response也是被动的,不能主动发起。
Websocket是基于HTTP协议的,或者说借用了HTTP的协议来完成一部分握手。
首先我们来看个典型的 Websocket
握手
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
熟悉HTTP的童鞋可能发现了,这段类似HTTP协议的握手请求中,多了几个东西。我会顺便讲解下作用。
Upgrade: websocket
Connection: Upgrade
这个就是Websocket的核心了,告诉 Apache
、 Nginx
等服务器:注意啦,我发起的是Websocket协议,快点帮我找到对应的助理处理~不是那个老土的HTTP。
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Sec-WebSocket-Key
是一个 Base64 encode
的值,这个是浏览器随机生成的,告诉服务器:泥煤,不要忽悠窝,我要验证尼是不是真的是Websocket助理。
然后, Sec_WebSocket-Protocol
是一个用户定义的字符串,用来区分同URL下,不同的服务所需要的协议。
最后, Sec-WebSocket-Version
是告诉服务器所使用的 Websocket Draft
(协议版本),在最初的时候,Websocket协议还在 Draft
阶段,各种奇奇怪怪的协议都有,而且还有很多期奇奇怪怪不同的东西,什么Firefox和Chrome用的不是一个版本之类的,后来做了统一。
然后服务器会返回下列东西,表示已经接受到请求, 成功建立Websocket啦!
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
这里开始就是HTTP最后负责的区域了,告诉客户,我已经成功切换协议啦~
Upgrade: websocket
Connection: Upgrade
依然是固定的,告诉客户端即将升级的是 Websocket
协议,而不是mozillasocket,lurnarsocket或者shitsocket。
然后, Sec-WebSocket-Accept
这个则是经过服务器确认,并且加密过后的 Sec-WebSocket-Key
。
后面的, Sec-WebSocket-Protocol
则是表示最终使用的协议。
至此,HTTP已经完成它所有工作了。
实例
<html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>lujiacheng</h1> <script> ws=new WebSocket("ws://127.0.0.1:8096") ws.onopen=function () { alert('successful'); ws.send("Hellowww"); } ws.onmessage=function (event) { alert(event.data) } </script> </body> </html>
import socket import base64 import hashlib import struct # 向客户端发送消息 def send_msg(conn,msg_bytes): token=b"\x81" length=len(msg_bytes) if length<126: token+=struct.pack("B",length) elif length<0xFFFF: token+=struct.pack("!BH",126,length) else: token+=struct.pack("!BQ",127,length) msg=token+msg_bytes print(msg) conn.send(msg) return True def get_websocket_message(str_header): header,body=str_header.split(b'\r\n\r\n') header_dict={} headers=header.split(b'\r\n') for str_spilt in headers: str_hd=str_spilt.split(b':') if len(str_hd)==2: header_dict[str(str_hd[0],encoding='utf-8')]=str(str_hd[1],encoding='utf-8').strip() magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' values=header_dict['Sec-WebSocket-Key']+magic_string ac=base64.b64encode(hashlib.sha1(values.encode('utf-8')).digest()) request_tpl="HTTP/1.1 101 Switching Protocols\r\n" \ "Upgrade: websocket\r\n" \ "Connection: Upgrade\r\n" \ "Sec-WebSocket-Accept: %s\r\n\r\n" %(str(ac,encoding='utf-8')) return request_tpl.encode('utf-8') sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) sock.bind(('127.0.0.1',8096)) sock.listen(5) # 等待用户连接 conn,addr=sock.accept() # 握手消息 data=conn.recv(8096) # 获取握手消息,magic_string,shal加密 # 发送给客户端 recv=get_websocket_message(data) conn.send(recv) # 建立成功后,接收客户端发过来的消息 info = conn.recv(1024) # 解析客户端发过来的数据 while True: # payload_len决定数据头和数据所占的位数 # payload_len等于126,要在往后移2字节,即16bit # payload_len等于127,要在往后移8字节,即64bit # mask解码 payload_len=info[1] & 127 if payload_len==126: extend_payload_len=info[2:4] mask=info[4:8] decoded=info[8:] elif payload_len==127: extend_payload_len = info[2:10] mask = info[10:14] decoded = info[14:] else: extend_payload_len = None mask = info[2:6] decoded = info[6:] bytes_list=bytearray() for i in range(len(decoded)): chunk=decoded[i] ^ mask[i % 4] bytes_list.append(chunk) body=str(bytes_list,encoding="utf-8") print(body) text=input(">>>") send_msg(conn,bytes(text,encoding="utf-8"))