python websocket server_python版websocket

背景

目前看到实现websocket的框架的

- django的channels

- flask

- sanic

好奇,用python怎样写一个websokcet服务器,以及socket如何与应用框一起启动

后记

accept 与recv是阻塞模式,调用时候,同时新开一个threading解决同步阻塞的问题

while True 要写在 sock.accept() 之前,因为在要等不同的socket连接,如果写在这之后,只能有一个socket连接

首次连接通过http建立,之后直接互相通信 while True 同样要写在conn.recv(8094) 之前, 因为要不停的通信

整个socket通信要写两次 while True

werkzeug与websocket同时启动, 在主进程内,各开一个子进程即可

6.在此基础上可以按需扩展,如改成python3 的async写法 , 引用更高级别的库websocket或python3的protocol等。万变不离其宗,从python层面,这是比较底层的写法,其它的写法大多是在此基础上封装的。

第一版后端

import socket

import struct

import hashlib

import base64

def get_headers(data):

headers = {}

data = str(data, encoding="utf-8")

header, body = data.split("\r\n\r\n", 1)

header_list = header.split("\r\n")

for i in header_list:

i_list = i.split(":", 1)

if len(i_list) >= 2:

headers[i_list[0]] = "".join(i_list[1::]).strip()

else:

i_list = i.split(" ", 1)

if i_list and len(i_list) == 2:

headers["method"] = i_list[0]

headers["protocol"] = i_list[1]

return headers

def parse_payload(payload):

payload_len = payload[1] & 127

if payload_len == 126:

extend_payload_len = payload[2:4]

mask = payload[4:8]

decoded = payload[8:]

elif payload_len == 127:

extend_payload_len = payload[2:10]

mask = payload[10:14]

decoded = payload[14:]

else:

extend_payload_len = None

mask = payload[2:6]

decoded = payload[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')

return body

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

conn.sendall(msg)

return True

def server_socket():

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

sock.bind(("127.0.0.1", 10083))

sock.listen(5)

conn, addr = sock.accept()

print(conn)

data = conn.recv(8096)

headers = get_headers(data)

# 对请求头中的sec-websocket-key进行加密

response_tpl = "HTTP/1.1 101 Switching Protocols\r\n" \

"Upgrade:websocket\r\n" \

"Connection: Upgrade\r\n" \

"Sec-WebSocket-Accept: %s\r\n" \

"WebSocket-Location: ws://%s\r\n\r\n"

magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'

if headers.get('Sec-WebSocket-Key'):

value = headers['Sec-WebSocket-Key'] + magic_string

ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())

response_str = response_tpl % (ac.decode('utf-8'), headers.get("Host"))

conn.sendall(bytes(response_str, encoding="utf-8"))

while True:

data_1 = conn.recv(8096)

data_2 = parse_payload(data_1)

print(data_2)

send_msg(conn, b"test")

if __name__ == "__main__":

server_socket()

web 端

*{

margin: 0;

padding: 0;

}

.message{

width: 60%;

margin: 0 10px;

display: inline-block;

text-align: center;

height: 40px;

line-height: 40px;

font-size: 20px;

border-radius: 5px;

border: 1px solid #B3D33F;

}

.form{

width:100%;

position: fixed;

bottom: 300px;

left: 0;

}

.connect{

height: 40px;

vertical-align: top;

/* padding: 0; */

width: 80px;

font-size: 20px;

border-radius: 5px;

border: none;

background: #B3D33F;

color: #fff;

}

var oUl=document.getElementById('content');

var oConnect=document.getElementById('connect');

var oSend=document.getElementById('send');

var oInput=document.getElementById('message');

var ws=null;

oConnect.οnclick=function(){

ws=new WebSocket('ws://127.0.0.1:10083');

ws.onopen=function(){

oUl.innerHTML+="

客户端已连接";

}

ws.onmessage=function(evt){

console.log("fdsa")

oUl.innerHTML+="

"+evt.data+"";

}

ws.onclose=function(){

oUl.innerHTML+="

客户端已断开连接";

};

ws.οnerrοr=function(evt){

oUl.innerHTML+="

"+evt.data+"";

};

};

oSend.οnclick=function(){

if(ws){

ws.send($("#message").val())

}

}

效果

image.png

image.png

这版有些问题

客户端断开,服务端就挂了

单线程.....

改进版

import socket

import struct

import hashlib

import base64

import threading

def get_headers(data):

headers = {}

data = str(data, encoding="utf-8")

header, body = data.split("\r\n\r\n", 1)

header_list = header.split("\r\n")

for i in header_list:

i_list = i.split(":", 1)

if len(i_list) >= 2:

headers[i_list[0]] = "".join(i_list[1::]).strip()

else:

i_list = i.split(" ", 1)

if i_list and len(i_list) == 2:

headers["method"] = i_list[0]

headers["protocol"] = i_list[1]

return headers

def parse_payload(payload):

payload_len = payload[1] & 127

if payload_len == 126:

extend_payload_len = payload[2:4]

mask = payload[4:8]

decoded = payload[8:]

elif payload_len == 127:

extend_payload_len = payload[2:10]

mask = payload[10:14]

decoded = payload[14:]

else:

extend_payload_len = None

mask = payload[2:6]

decoded = payload[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')

return body

def send_msg(conn, msg_bytes):

# 接收的第一字节,一般都是x81不变

first_byte = b"\x81"

length = len(msg_bytes)

if length < 126:

first_byte += struct.pack("B", length)

elif length <= 0xFFFF:

first_byte += struct.pack("!BH", 126, length)

else:

first_byte += struct.pack("!BQ", 127, length)

msg = first_byte + msg_bytes

conn.sendall(msg)

return True

sock_pool = []

def handler_accept(sock):

while True:

conn, addr = sock.accept()

data = conn.recv(8096)

headers = get_headers(data)

# 对请求头中的sec-websocket-key进行加密

response_tpl = "HTTP/1.1 101 Switching Protocols\r\n" \

"Upgrade:websocket\r\n" \

"Connection: Upgrade\r\n" \

"Sec-WebSocket-Accept: %s\r\n" \

"WebSocket-Location: ws://%s\r\n\r\n"

# 第一次连接发回报文

magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'

if headers.get('Sec-WebSocket-Key'):

value = headers['Sec-WebSocket-Key'] + magic_string

ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())

response_str = response_tpl % (ac.decode('utf-8'), headers.get("Host"))

conn.sendall(bytes(response_str, encoding="utf-8"))

t = threading.Thread(target=handler_msg, args=(conn, ))

t.start()

def handler_msg(conn):

with conn as c:

while True:

data_recv = c.recv(8096)

if data_recv[0:1] == b"\x81":

data_parse = parse_payload(data_recv)

print(data_parse)

send_msg(c, bytes("recv: {}".format(data_parse), encoding="utf-8"))

def server_socket():

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

sock.bind(("127.0.0.1", 10083))

sock.listen(5)

t = threading.Thread(target=handler_accept(sock))

t.start()

# data = conn.recv(8096)

# headers = get_headers(data)

#

# # 对请求头中的sec-websocket-key进行加密

# response_tpl = "HTTP/1.1 101 Switching Protocols\r\n" \

# "Upgrade:websocket\r\n" \

# "Connection: Upgrade\r\n" \

# "Sec-WebSocket-Accept: %s\r\n" \

# "WebSocket-Location: ws://%s\r\n\r\n"

#

# # 第一次连接发回报文

# magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'

# if headers.get('Sec-WebSocket-Key'):

# value = headers['Sec-WebSocket-Key'] + magic_string

#

# ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())

# response_str = response_tpl % (ac.decode('utf-8'), headers.get("Host"))

# conn.sendall(bytes(response_str, encoding="utf-8"))

if __name__ == "__main__":

server_socket()

参考

你可能感兴趣的:(python,websocket,server)