python怎么连接websocket

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python怎么连接websocket相关的知识,希望对你有一定的参考价值。

参考技术A websocket是一个浏览器和服务器通信的新的协议,一般而言,浏览器和服务器通信最常用的是http协议,但是http协议是无状态的,每次浏览器请求信息,服务器返回信息后这个浏览器和服务器通信的信道就被关闭了,这样使得服务器如果想主动给浏览器发送信息变得不可能了,服务器推技术在http时代的解决方案一个是客户端去轮询,或是使用comet技术,而websocket则和一般的socket一样,使得浏览器和服务器建立了一个双工的通道。
具体的websocket协议在rfc6455里面有,这里简要说明一下。websocket通信需要先有个握手的过程,使得协议由http转变为webscoket协议,然后浏览器和服务器就可以利用这个socket来通信了。

首先浏览器发送握手信息,要求协议转变为websocket

GET / HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin:
服务器接收到信息后,取得其中的Sec-WebSocket-Key,将他和一个固定的字符串258EAFA5-E914-47DA-95CA-C5AB0DC85B11做拼接,得到的字符串先用sha1做一下转换,再用base64转换一下,就得到了回应的字符串,这样服务器端发送回的消息是这样的

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
这样握手就完成了,用python来实现这段握手过程的话就是下面这样。

def handshake(conn):
key =None
data = conn.recv(8192)
if not len(data):
return False
for line in data.split('\r\n\r\n')[0].split('\r\n')[1:]:
k, v = line.split(': ')
if k =='Sec-WebSocket-Key':
key =base64.b64encode(hashlib.sha1(v +'258EAFA5-E914-47DA-95CA-C5AB0DC85B11').digest())
if not key:
conn.close()
return False
response ='HTTP/1.1 101 Switching Protocols\r\n'\
'Upgrade: websocket\r\n'\
'Connection: Upgrade\r\n'\
'Sec-WebSocket-Accept:'+ key +'\r\n\r\n'
conn.send(response)
return True
握手过程完成之后就是信息传输了,websocket的数据信息格式是这样的。

+-+-+-+-+-------+-+-------------+-------------------------------+
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| | Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+
值得注意的是payload len这项,表示数据的长度有多少,如果小于126,那么payload len就是数据的长度,如果是126那么接下来2个字节是数据长度,如果是127表示接下来8个字节是数据长度,然后后面会有四个字节的mask,真实数据要由payload data和mask做异或才能得到,这样就可以得到数据了。发送数据的格式和接受的数据类似,具体细节可以去参考rfc6455,这里就不过多赘述了。                                                                              

Python的Websocket客户端:Websocket-Client

Websocket-Client 是 Python 上的 Websocket 客户端。它只支持 hybi-13,且所有的 Websocket API 都支持同步。

Installation
This module is tested on Python 2.7 and Python 3.x.
Type "python setup.py install" or "pip install websocket-client" to install.
Caution!
from v0.16.0, we can install by "pip install websocket-client" for python 3.
This module depend on
six
backports.ssl_match_hostname for Python 2.x

Python通过websocket与js客户端通信示例分析

这里,介绍如何使用 Python 与前端 js 进行通信。
websocket 使用 HTTP 协议完成握手之后,不通过 HTTP 直接进行 websocket 通信。
于是,使用 websocket 大致两个步骤:使用 HTTP 握手,通信。
js 处理 websocket 要使用 ws 模块; Python 处理则使用 socket 模块建立 TCP 连接即可,比一般的 socket ,只多一个握手以及数据处理的步骤。

包格式
js 客户端先向服务器端 python 发送握手包,格式如下:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin:
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

服务器回应包格式:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat

其中, Sec-WebSocket-Key 是随机的,服务器用这些数据构造一个 SHA-1 信息摘要。
方法为: key+migic , SHA-1 加密, base-64 加密

Python 中的处理代码:
MAGIC_STRING = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
res_key = base64.b64encode(hashlib.sha1(sec_key + MAGIC_STRING).digest())

握手完整代码
js 端
js 中有处理 websocket 的类,初始化后自动发送握手包,如下:
var socket = new WebSocket('ws://localhost:3368');
Python 端
Python 用 socket 接受得到握手字符串,处理后发送
HOST = 'localhost'
PORT = 3368
MAGIC_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"

def handshake(con):
#con为用socket,accept()得到的socket

headers =
shake = con.recv(1024)

if not len(shake):
return False

header, data = shake.split('\r\n\r\n', 1)
for line in header.split('\r\n')[1:]:
key, val = line.split(': ', 1)
headers[key] = val

if 'Sec-WebSocket-Key' not in headers:
print ('This socket is not websocket, client close.')
con.close()
return False

sec_key = headers['Sec-WebSocket-Key']
res_key = base64.b64encode(hashlib.sha1(sec_key + MAGIC_STRING).digest())

str_handshake = HANDSHAKE_STRING.replace('1', res_key).replace('2', HOST + ':' + str(PORT))
print str_handshake
con.send(str_handshake)
return True

通信
不同版本的浏览器定义的数据帧格式不同, Python 发送和接收时都要处理得到符合格式的数据包,才能通信。
Python 接收
Python 接收到浏览器发来的数据,要解析后才能得到其中的有用数据。

固定字节:
( 1000 0001 或是 1000 0002 )这里没用,忽略
包长度字节:
第一位肯定是 1 ,忽略。剩下 7 个位可以得到一个整数 (0 ~ 127) ,其中
( 1-125 )表此字节为长度字节,大小即为长度;
(126)表接下来的两个字节才是长度;
(127)表接下来的八个字节才是长度;
用这种变长的方式表示数据长度,节省数据位。
mark 掩码:
mark 掩码为包长之后的 4 个字节,之后的兄弟数据要与 mark 掩码做运算才能得到真实的数据。
兄弟数据:
得到真实数据的方法:将兄弟数据的每一位 x ,和掩码的第 i%4 位做 xor 运算,其中 i 是 x 在兄弟数据中的索引。

完整代码
def recv_data(self, num):
try:
all_data = self.con.recv(num)
if not len(all_data):
return False
except:
return False
else:
code_len = ord(all_data[1]) & 127
if code_len == 126:
masks = all_data[4:8]
data = all_data[8:]
elif code_len == 127:
masks = all_data[10:14]
data = all_data[14:]
else:
masks = all_data[2:6]
data = all_data[6:]
raw_str = ""
i = 0
for d in data:
raw_str += chr(ord(d) ^ ord(masks[i % 4]))
i += 1
return raw_str
js 端的 ws 对象,通过 ws.send(str) 即可发送
ws.send(str)

Python 发送
Python 要包数据发送,也需要处理
固定字节:固定的 1000 0001( ‘ \x81 ′ )
包长:根据发送数据长度是否超过 125 , 0xFFFF(65535) 来生成 1 个或 3 个或 9 个字节,来代表数据长度。
def send_data(self, data):
if data:
data = str(data)
else:
return False
token = "\x81"
length = len(data)
if length < 126:
token += struct.pack("B", length)
elif length <= 0xFFFF:
token += struct.pack("!BH", 126, length)
else:
token += struct.pack("!BQ", 127, length)
#struct为Python中处理二进制数的模块,二进制流为C,或网络流的形式。
data = '%s%s' % (token, data)
self.con.send(data)
return True

python之WebSocket协议

技术文章第一时间送达!


一、WebSocket理论部分

1、websocket是什么

Websocket是html5提出的一个协议规范,参考rfc6455。

websocket约定了一个通信的规范,通过一个握手的机制,客户端(浏览器)和服务器(webserver)之间能建立一个类似tcp的连接,从而方便c-s之间的通信。在websocket出现之前,web交互一般是基于http协议的短连接或者长连接。

WebSocket是为解决客户端与服务端实时通信而产生的技术。websocket协议本质上是一个基于tcp的协议,是先通过HTTP/HTTPS协议发起一条特殊的http请求进行握手后创建一个用于交换数据的TCP连接,此后服务端与客户端通过此TCP连接进行实时通信。

注意:此时不再需要原HTTP协议的参与了。


2、websocket的优点

以前web server实现推送技术或者即时通讯,用的都是轮询(polling),在特点的时间间隔(比如1秒钟)由浏览器自动发出请求,将服务器的消息主动的拉回来,在这种情况下,我们需要不断的向服务器发送请求,然而HTTP request 的header是非常长的,里面包含的数据可能只是一个很小的值,这样会占用很多的带宽和服务器资源。

而最比较新的技术去做轮询的效果是Comet – 用了AJAX。但这种技术虽然可达到全双工通信,但依然需要发出请求(reuqest)。

WebSocket API最伟大之处在于服务器和客户端可以在给定的时间范围内的任意时刻,相互推送信息。 浏览器和服务器只需要要做一个握手的动作,在建立连接之后,服务器可以主动传送数据给客户端,客户端也可以随时向服务器发送数据。 此外,服务器与客户端之间交换的标头信息很小。

WebSocket并不限于以Ajax(或XHR)方式通信,因为Ajax技术需要客户端发起请求,而WebSocket服务器和客户端可以彼此相互推送信息;

因此从服务器角度来说,websocket有以下好处:

    1. 节省每次请求的header
      http的header一般有几十字节

    2. Server Push
      服务器可以主动传送数据给客户端


3、websocket的协议规范

3.1基于flash的握手协议

使用场景是IE的多数版本,因为IE的多数版本不都不支持WebSocket协议,以及FF、CHROME等浏览器的低版本,还没有原生的支持WebSocket。此处,server唯一要做的,就是准备一个WebSocket-Location域给client,没有加密,可靠性很差。


3.2基于md5加密方式的握手协议

其中 Sec-WebSocket-Key1,Sec-WebSocket-Key2 和 [8-byte security key] 这几个头信息是web server用来生成应答信息的来源,依据 draft-hixie-thewebsocketprotocol-76 草案的定义。
web server基于以下的算法来产生正确的应答信息:

1. 逐个字符读取 Sec-WebSocket-Key1 头信息中的值,将数值型字符连接到一起放到一个临时字符串里,同时统计所有空格的数量;
2. 将在第(1)步里生成的数字字符串转换成一个整型数字,然后除以第(1)步里统计出来的空格数量,将得到的浮点数转换成整数型;
3. 将第(2)步里生成的整型值转换为符合网络传输的网络字节数组;
4. 对 Sec-WebSocket-Key2 头信息同样进行第(1)到第(3)步的操作,得到另外一个网络字节数组;
5. 将 [8-byte security key] 和在第(3)、(4)步里生成的网络字节数组合并成一个16字节的数组;
6. 对第(5)步生成的字节数组使用MD5算法生成一个哈希值,这个哈希值就作为安全密钥返回给客户端,以表明服务器端获取了客户端的请求,同意创建websocket连接


3.3基于sha加密方式的握手协议

也是目前见的最多的一种方式,这里的版本号目前是需要13以上的版本。

客户端请求:

GET /ls HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: www.qixing318.com
Sec-WebSocket-Origin: http://www.qixing318.com
Sec-WebSocket-Key: 2SCVXUeP9cTjV+0mWB8J6A==
Sec-WebSocket-Version: 13

服务器返回:

HTTP/1.1 101 Switching Protocols 
Upgrade: websocket Connection:
Upgrade Sec-WebSocket-Accept: mLDKNeBNWz6T9SxU+o0Fy/HgeSw=

其中 server就是把客户端上报的key拼上一段GUID( “258EAFA5-E914-47DA-95CA-C5AB0DC85B11″),拿这个字符串做SHA-1 hash计算,然后再把得到的结果通过base64加密,最后再返回给客户端。

-格式:\r\n

-创建链接之后默认不断开


3.4、基于sha加密的Opening Handshake(握手环节)

客户端发起连接Handshake请求

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

服务器端响应:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat


Upgrade:WebSocket
表示这是一个特殊的 HTTP 请求,请求的目的就是要将客户端和服务器端的通讯协议从 HTTP 协议升级到 WebSocket 协议。
Sec-WebSocket-Key
是一段浏览器base64加密的密钥,server端收到后需要提取Sec-WebSocket-Key 信息,然后加密。
Sec-WebSocket-Accept
服务器端在接收到的Sec-WebSocket-Key密钥后追加一段神奇字符串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,并将结果进行sha-1哈希,然后再进行base64加密返回给客户端(就是Sec-WebSocket-Key)。 比如:

function encry($req)
{
   $key = $this->getKey($req);
   $mask = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
   # 将 SHA-1 加密后的字符串再进行一次 base64 加密
   return base64_encode(sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
}
如果加密算法错误,客户端在进行校检的时候会直接报错。如果握手成功,则客户端侧会出发onopen事件。
Sec-WebSocket-Protocol
表示客户端请求提供的可供选择的子协议,及服务器端选中的支持的子协议,“Origin”服务器端用于区分未授权的websocket浏览器
Sec-WebSocket-Version: 13
客户端在握手时的请求中携带,这样的版本标识,表示这个是一个升级版本,现在的浏览器都是使用的这个版本。
HTTP/1.1 101 Switching Protocols
101为服务器返回的状态码,所有非101的状态码都表示handshake并未完成。


Data Framing

Websocket协议通过序列化的数据帧传输数据。数据封包协议中定义了opcode、payload length、Payload data等字段。其中要求:

  1. 客户端向服务器传输的数据帧必须进行掩码处理:服务器若接收到未经过掩码处理的数据帧,则必须主动关闭连接。

  2. 服务器向客户端传输的数据帧一定不能进行掩码处理。客户端若接收到经过掩码处理的数据帧,则必须主动关闭连接。

针对上情况,发现错误的一方可向对方发送close帧(状态码是1002,表示协议错误),以关闭连接。
具体数据帧格式如下图所示:

FIN
标识是否为此消息的最后一个数据包,占 1 bit

RSV1, RSV2, RSV3: 用于扩展协议,一般为0,各占1bit

Opcode
数据包类型(frame type),占4bits
0x0:标识一个中间数据包
0x1:标识一个text类型数据包
0x2:标识一个binary类型数据包
0x3-7:保留
0x8:标识一个断开连接类型数据包
0x9:标识一个ping类型数据包
0xA:表示一个pong类型数据包
0xB-F:保留

MASK:占1bits
用于标识PayloadData是否经过掩码处理。如果是1,Masking-key域的数据即是掩码密钥,用于解码PayloadData。客户端发出的数据帧需要进行掩码处理,所以此位是1。

Payload length
Payload data的长度,占7bits,7+16bits,7+64bits:

    如果其值在0-125,则是payload的真实长度。

    如果值是126,则后面2个字节形成的16bits无符号整型数的值是payload的真实长度。注意,网络字节序,需要转换。

    如果值是127,则后面8个字节形成的64bits无符号整型数的值是payload的真实长度。注意,网络字节序,需要转换。

这里的长度表示遵循一个原则,用最少的字节表示长度(尽量减少不必要的传输)。举例说,payload真实长度是124,在0-125之间,必须用前7位表示;不允许长度1是126或127,然后长度2是124,这样违反原则。

Payload data

应用层数据

server解析client端的数据

接收到客户端数据后的解析规则如下:

1byte

1bit: frame-fin,x0表示该message后续还有frame;x1表示是message的最后一个frame

3bit: 分别是frame-rsv1、frame-rsv2和frame-rsv3,通常都是x0

4bit: frame-opcode,x0表示是延续frame;x1表示文本frame;x2表示二进制frame;x3-7保留给非控制frame;x8表示关 闭连接;x9表示ping;xA表示pong;xB-F保留给控制frame

2byte

1bit: Mask,1表示该frame包含掩码;0表示无掩码

7bit、7bit+2byte、7bit+8byte: 7bit取整数值,若在0-125之间,则是负载数据长度;若是126表示,后两个byte取无符号16位整数值,是负载长度;127表示后8个 byte,取64位无符号整数值,是负载长度

3-6byte: 这里假定负载长度在0-125之间,并且Mask为1,则这4个byte是掩码

7-end byte: 长度是上面取出的负载长度,包括扩展数据和应用数据两部分,通常没有扩展数据;若Mask为1,则此数据需要解码,解码规则为- 1-4byte掩码循环和数据byte做异或操作。


示例代码:

while True:
   # 对数据进行解密
   # send_msg(conn, bytes('alex', encoding='utf-8'))
   # send_msg(conn, bytes('SB', encoding='utf-8'))
   # info = conn.recv(8096)
   # print(info)

   info = conn.recv(8096)
   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)
   msg = str(bytes_list, encoding='utf-8')

   rep = msg + 'sb'
   send_msg(conn,bytes(rep,encoding='utf-8'))


5、原理代码:

后端

import socket
import hashlib
import base64


def get_headers(data):
   """
   将请求头格式化成字典
   :param data:
   :return:
   """

   header_dict = {}
   data = str(data, encoding='utf-8')

   header, body = data.split('\r\n\r\n', 1)
   header_list = header.split('\r\n')
   for i in range(0, len(header_list)):
       if i == 0:
           if len(header_list[i].split(' ')) == 3:
               header_dict['method'], header_dict['url'], header_dict['protocol'] = header_list[i].split(' ')
       else:
           k, v = header_list[i].split(':', 1)
           header_dict[k] = v.strip()
   return header_dict

def send_msg(conn, msg_bytes):
   """
   WebSocket服务端向客户端发送消息
   :param conn: 客户端连接到服务器端的socket对象,即: conn,address = socket.accept()
   :param msg_bytes: 向客户端发送的字节
   :return:
   """

   import struct

   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.send(msg)
   return True

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('127.0.0.1', 8002))
sock.listen(5)

# 等待用户连接
conn, address = sock.accept()

# WebSocket发来的连接
# 1. 获取握手数据
data = conn.recv(1024)
headers = get_headers(data)

# 2. 对握手信息进行加密:
magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
value = headers['Sec-WebSocket-Key'] + magic_string
ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())

# 3. 返回握手信息
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://127.0.0.1:8002\r\n\r\n"

response_str = response_tpl % (ac.decode('utf-8'),)

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

# 之后,才能进行首发数据。

while True:
   # 对数据进行解密
   # send_msg(conn, bytes('alex', encoding='utf-8'))
   # send_msg(conn, bytes('SB', encoding='utf-8'))
   # info = conn.recv(8096)
   # print(info)

   info = conn.recv(8096)
   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)
   msg = str(bytes_list, encoding='utf-8')

   rep = msg + 'sb'
   send_msg(conn,bytes(rep,encoding='utf-8'))


二、应用:

1、Flask中应用: pip3 install gevent-websocket

View Code

from flask import Flask,request,render_template,session,redirect
import uuid
import json
from geventwebsocket.handler import WebSocketHandler
from gevent.pywsgi import WSGIServer


app = Flask(__name__)
app.secret_key = 'asdfasdf'

GENTIEMAN = {
   '1':{'name':'钢弹','count':0},
   '2':{'name':'铁锤','count':0},
   '3':{'name':'闫帅','count':0},
}

WEBSOCKET_DICT = {

}

@app.before_request
def before_request():
   if request.path == '/login':
       return None
   user_info = session.get('user_info')
   if user_info:
       return None
   return redirect('/login')

@app.route('/login',methods=['GET','POST'])
def login():
   if request.method == "GET":
       return render_template('login.html')
   else:
       uid = str(uuid.uuid4())
       session['user_info'] = {'id':uid,'name':request.form.get('user')}
       return redirect('/index')

@app.route('/index')
def index():
   return render_template('index.html',users=GENTIEMAN)

@app.route('/message')
def message():
   # 1. 判断到底是否是websocket请求?
   ws = request.environ.get('wsgi.websocket')
   if not ws:
       return "请使用WebSocket协议"
   # ----- ws连接成功 -------
   current_user_id = session['user_info']['id']
   WEBSOCKET_DICT[current_user_id] = ws
   while True:
       # 2. 等待用户发送消息,并接受
       message = ws.receive() # 帅哥ID
       # 关闭:message=None
       if not message:
           del WEBSOCKET_DICT[current_user_id]
           break

       # 3. 获取用户要投票的帅哥ID,并+1
       old = GENTIEMAN[message]['count']
       new = old + 1
       GENTIEMAN[message]['count'] = new

       data = {'user_id': message, 'count': new,'type':'vote'}
       # 4. 给所有客户端推送消息
       for conn in WEBSOCKET_DICT.values():
           conn.send(json.dumps(data))
   return 'close'

@app.route('/notify')
def notify():
   data = {'data': "你的订单已经生成,请及时处理;", 'type': 'alert'}
   print(WEBSOCKET_DICT)
   for conn in WEBSOCKET_DICT.values():
       conn.send(json.dumps(data))
   return '发送成功'

if __name__ == '__main__':
   http_server = WSGIServer(('192.168.11.143', 5000), app, handler_class=WebSocketHandler)
   http_server.serve_forever()


login.html

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>Title</title>
</head>
<body>
<form method="post">
   <input type="text" name="user">
   <input type="submit" value="提交">
</form>
</body>
</html>

login.html


index.html

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>Title</title>
</head>
<body>
   <h1>投票系统:参与投票的人</h1>
   <ul>
       {% for k,v in users.items() %}
           <li id="user_{{k}}" ondblclick="vote('{{k}}')">{{v.name}} <span>{{v.count}}</span> </li>
       {% endfor %}

   </ul>
   <script src="{{ url_for('static',filename='jquery-3.3.1.min.js')}}"></script>
   <script>
       var socket = new WebSocket("ws://192.168.11.143:5000/message");

       socket.onmessage = function (event) {
           /* 服务器端向客户端发送数据时,自动执行 */
           var response = JSON.parse(event.data); // {'user':1,'count':new}
           if(response.type == 'vote'){
               var nid = '#user_' + response.user_id;
               $(nid).find('span').text(response.count)
           }else{
               alert(response.data);
           }
       };

       /*
       我要给某人投票
        */

        function vote(id) {
           socket.send(id);
       }

   </script>
</body>
</html>


2、Django应用:channel

3、Tornado应用:自己有



识别图中二维码,欢迎关注python宝典

以上是关于python怎么连接websocket的主要内容,如果未能解决你的问题,请参考以下文章

python怎么连接redis(附源码)

python爬虫 请教一下,python怎么连接websocket

pythontelnet批量连接脚本怎么能多个不同网段连接

怎么用python连接mysql数据库

python3中,pycharm中怎么连接数据库

python3连接MSSQL数据库 中文乱码怎么解决