用Python做一个websocket服务端
我对websocket服务端的功能定义是:
1.能将client端的软硬件信息关联(如client_name和对应ip:port),且不支持重复关联;
2.可以判断接收自client端的消息属于哪种任务,并对应执行(如关联、发消息);
3.根据接收自client端的消息判断是点对点发送,还是广播发送,并执行。
参考其他大神们写的代码,我用python实现了上述功能需求:
import asyncio
import websockets
import json
import threading
# 存储所有的客户端
Clients = []
# 服务端
class WS_Server():
def __init__(self):
self.ip = "127.0.0.1"
self.port = 9090
# 发送消息
async def sendMsg(self, websocket, msg):
if websocket != None:
await websocket.send(msg)
else:
self.broadcast(msg)
await asyncio.sleep(0.2)
# 群发消息
async def broadcast(self, msg):
for user in Clients:
await user['socket'].send(msg)
# 连接一个客户端,起一个循环监听
async def echo(self, websocket):
# 握手
client_ip, client_port = websocket.remote_address
print(f"连接到:{client_ip}:{client_port}")
await websocket.send(json.dumps({"stat": "success"}))
# 循环监听
while True:
try:
recv_text = await websocket.recv()
message = "收到消息: {}".format(recv_text)
await websocket.send(message)
data = json.loads(recv_text)
receiver = data['to']
content = data['content']
sender = data['from']
stat = data['stat']
if stat == "link":
checked = False
if len(Clients) > 0 :
for usr in Clients:
if usr['name'] == sender:
checked = True
break
if checked:
print(f"{client_ip}:{client_port}不能被重复关联")
else:
Clients.append({"socket": websocket, "name": sender})
print(f"{sender} 已关联 {client_ip}:{client_port}")
else:
Clients.append({"socket": websocket, "name": sender})
print(f"{sender} 已关联 {client_ip}:{client_port}")
else:
msg = json.dumps({"stat": stat, "to": receiver, "content": content, "from": sender})
if receiver == "":
await self.broadcast(msg)
print('消息已群发')
else:
checked1 = False
for usr in Clients:
if usr['name'] == receiver:
socket = usr['socket']
await self.sendMsg(socket, msg)
checked1 = True
break
if checked1:
print(f"已给{receiver}发送消息")
else:
print(f"发送失败,未找到{receiver}")
except websockets.ConnectionClosed:
print(f"从{client_ip}:{client_port}断开连接")
break
except websockets.InvalidState:
print("无效状态")
break
except Exception as e:
print("ws连接报错", e)
break
# 启动服务器
async def runServer(self):
async with websockets.serve(self.echo, self.ip, self.port):
await asyncio.Future() # run forever
# 多协程模式,防止阻塞主线程无法做其他事情
def WebSocketServer(self):
asyncio.run(self.runServer())
# 多线程启动
def startServer(self):
# 多线程启动,否则会堵塞
thread = threading.Thread(target=self.WebSocketServer)
thread.start()
# thread.join()
if __name__=='__main__':
s = WS_Server()
s.startServer()
print("ws服务已启动")
启动WS服务后,客户端需要按照如下数据格式发消息:
{"stat":"","to": "", "content": "", "from":""}
其中,stat包括link和send两种状态,to是消息接者者(receiver),from是消息发送者(sender)。若to为空,则表示该消息是广播消息;若to不为空,但对象不存在,则该消息不会被执行发送。
最后,附上我参考的两篇CSDN文章:
Python中websockets服务端从客户端接收消息并发送给多线程_python websocket 多线程-CSDN博客
python的websocket方法教程_python websocket-CSDN博客