一、什么是websocket
WebSocket是HTML5开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
在WebSocket API中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。
当你获取 Web Socket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。 ----------------- 出自菜鸟教程
二、客户端请求报文
客户端请求的链接:ws://localhost:8080
和传统http报文不同的地方:
Connection: Upgrade
Upgrade: websocket ----- 表示发起的是websocket协议
Sec-WebSocket-Key: TD7emWUct4iW4vddYWbMqQ== ------ 由浏览器随机生成,提供基本的防护
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits ---- 协议的扩展
Sec-WebSocket-Version: 13 ---- 版本号
三、服务器接收请求报文
服务器收到请求报文后,会发起tcp的三次握手,和客户端建立链接,这个地方和tcpsocket基本一样。
#创建基于tcp的服务器
serverSocket =socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serverSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
host=(HOST, PORT)
serverSocket.bind(host)
serverSocket.listen(128)print("服务器运行, 等待用户链接")whileTrue:#print("getting connection")
clientSocket, addressInfo =serverSocket.accept()#print("get connected")
request = clientSocket.recv(2048)print(request.decode())#获取Sec-WebSocket-Key
ret = re.search(r"Sec-WebSocket-Key: (.*==)", str(request.decode()))ifret:
key= ret.group(1)else:returnSec_WebSocket_Key= key +MAGIC_STRING#print("key ", Sec_WebSocket_Key)
#将Sec-WebSocket-Key先进行sha1加密,转成二进制后在使用base64加密
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)
#构建websocket返回数据
response = HANDSHAKE_STRING.replace("{1}", response_key_str).replace("{2}", HOST + ":" +str(PORT))
clientSocket.send(response.encode())#print("send the hand shake data")
四、因为websocket是基于tcp的全双工通信协议,所以,他可以一边接收,一边发送
1、接收并解析websocket报文
b'\x81\x84\xa3l\xcf\x10\x92^\xfc$'
客户端发送到server的websocket的报文分为四个部分:
a、固定部分‘\81’
b、报文内容长度
c、掩码 b'\xa3l\xcf\x10'
d、报文内容 b'\x92^\xfc$'
defrecv_data(clientSocket):try:
info= clientSocket.recv(2048)if notinfo:return
except:return
else:
code_len= info[1] & 0x7f
if code_len == 0x7e:
extend_payload_len= info[2:4]
mask= info[4:8]
decoded= info[8:]elif code_len == 0x7f:
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 inrange(len(decoded)):
chunk= decoded[i] ^ mask[i % 4]
bytes_list.append(chunk)
raw_str= str(bytes_list, encoding="utf-8")print(raw_str)
2、server端发送数据
server端发送数据分为三个部分
a、固定部分‘\81’
b、报文长度
c、报文内容
struct用法:
Format C Type Python type Standard size Notes
x pad byte no value
c char bytes of length1 1b signed char integer1 (1),(3)
B unsigned char integer1 (3)
? _Bool bool1 (1)
h short integer2 (3)
H unsigned short integer2 (3)
i int integer4 (3)
I unsigned int integer4 (3)
l long integer4 (3)
L unsigned long integer4 (3)
q long long integer8 (2), (3)
Q unsigned long long integer8 (2), (3)
n ssize_t integer (4)
N size_t integer (4)
e (7) float 2 (5)
f float float4 (5)
d double float8 (5)
s char[] bytes
p char[] bytes
P void* integer (6)
Character Byte order Size Alignment
@ native native native=native standard none< little-endian standard none> big-endian standard none
! network (= big-endian) standard none
服务端发送数据代码:
defsend_data(clientSocket):
data= "need to send messages中文"token= b'\x81'length=len(data.encode())if length<=125:
token+= struct.pack('B', length)elif length <= 0xFFFF:
token+= struct.pack('!BH', 126, length)else:
token+= struct.pack('!BQ', 127, length)
data= token +data.encode()
clientSocket.send(data)
全部代码:
py:
importsocketimportbase64importhashlibimportreimportthreadingimportstruct
HOST= "localhost"PORT= 8080MAGIC_STRING= '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'HANDSHAKE_STRING= "HTTP/1.1 101 Switching Protocols\r\n"\"Upgrade:websocket\r\n"\"Connection: Upgrade\r\n"\"Sec-WebSocket-Accept: {1}\r\n"\"WebSocket-Location: ws://{2}/chat\r\n"\"WebSocket-Protocol:chat\r\n\r\n"
defrecv_data(clientSocket):try:
info= clientSocket.recv(2048)if notinfo:return
except:return
else:print(info)
code_len= info[1] & 0x7f
if code_len == 0x7e:
extend_payload_len= info[2:4]
mask= info[4:8]
decoded= info[8:]elif code_len == 0x7f:
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()print(mask)print(decoded)for i inrange(len(decoded)):
chunk= decoded[i] ^ mask[i % 4]
bytes_list.append(chunk)
raw_str= str(bytes_list, encoding="utf-8")print(raw_str)defsend_data(clientSocket):
data= "need to send messages中文"token= b'\x81'length=len(data.encode())if length<=125:
token+= struct.pack('B', length)elif length <= 0xFFFF:
token+= struct.pack('!BH', 126, length)else:
token+= struct.pack('!BQ', 127, length)
data= token +data.encode()
clientSocket.send(data)defhandshake(serverSocket):whileTrue:#print("getting connection")
clientSocket, addressInfo =serverSocket.accept()#print("get connected")
request = clientSocket.recv(2048)print(request.decode())#获取Sec-WebSocket-Key
ret = re.search(r"Sec-WebSocket-Key: (.*==)", str(request.decode()))ifret:
key= ret.group(1)else:returnSec_WebSocket_Key= key +MAGIC_STRING#print("key ", Sec_WebSocket_Key)
#将Sec-WebSocket-Key先进行sha1加密,转成二进制后在使用base64加密
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)
#构建websocket返回数据
response = HANDSHAKE_STRING.replace("{1}", response_key_str).replace("{2}", HOST + ":" +str(PORT))
clientSocket.send(response.encode())#print("send the hand shake data")
t1 = threading.Thread(target = recv_data, args =(clientSocket,))
t1.start()
t2= threading.Thread(target = send_data, args =(clientSocket,))
t2.start()defmain():#创建基于tcp的服务器
serverSocket =socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serverSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
host=(HOST, PORT)
serverSocket.bind(host)
serverSocket.listen(128)print("服务器运行, 等待用户链接")#调用监听
handshake(serverSocket)if __name__ == "__main__":
main()
html:
w