python+websocket 实现聊天室服务端程序
github项目地址
推荐直接去github获取
聊天室服务端的一个简单实现
使用python与websocket实现
本服务端代码是在写一个远程同步观看并聊天的功能实现的,所以如果只是想要聊天的话只需要保留核心逻辑即可
本代码对应的客户端app下载地址39.101.160.55
代码逻辑
- 每一个端口号作为一个房间
- 对一定时间内没有用户的房间进行清除,释放端口号和服务器资源
代码运行
- 需要修改一些代码内容来适配你自己的服务器
- create_room.py中在创建子进程运行son_watch.py中,你需要修改python的路径
- 对应开放的端口号根据情况进行改变
- 更多的修改可以通过阅读代码来理解
creat_room.py
import asyncio
import websockets
import random
import nest_asyncio
nest_asyncio.apply()
import subprocess
import threading
import time
import re
#receive the msg and send to all client
#the port list which can be connected
port_list=[i for i in range(8230,8280)]
#用于根据房间号分配房间
room_dict={}
#用于释放端口
port_dict={}
#the list to sign which port has already used
used_port_list=[False for i in range(8230,8280)]
threads=[]
#随机生成房间号
def generate_room_id(length):
digits = "0123456789"
random_string = ''.join(random.choice(digits) for _ in range(length))
return random_string
#read port msg from file and update the msg in program and generate a free port
def generate_port():
with open("ports_using.txt","r")as f:
s=f.readline().split(" ")
for i in range(10):
if(str(i+8230)in s):
used_port_list[i]=True
else:
used_port_list[i]=False
for i in range(0,10):
if(used_port_list[i]is False):
used_port_list[i]=True
return str(port_list[i])
# if the port was all used then return none
return "none"
async def receive_string(websocket, path):
while True:
message=await websocket.recv()
if message.startswith("cr"):
#get the url of video to be played
split_string = message.split()
video_url = split_string[1]
print(video_url)
# 产生房间号
print("GET create command")
room_id=generate_room_id(6)
port=generate_port()
print("TRY "+"create room : "+room_id+" room port : "+port)
#更新roomid信息
room_dict[room_id]=[port,video_url]
#update port msg
port_dict[port]=room_id
# 创建新的房间
asyncio.create_task(create_room(room_id, port, video_url))
await touch_html(video_url,room_id)
await websocket.send("room_id " + room_id+" port "+port)
elif message.startswith("id"):
room_id=message.split()[1]
# 发送对应房间号
try:
await websocket.send("room_id "+room_id+" port "+room_dict[room_id][0])
except:
await websocket.send("error")
else:
print("USELESS command")
await websocket.send("USELESS command")
async for message in websocket:
websocket
asyncio.create_task(deal_msg(websocket,message))
async def main():
async with websockets.serve(receive_string, "0.0.0.0","8627"):
await asyncio.Future() # 挂起,直到服务器关闭
async def create_room(room_id, port, video_url):
print(2)
#await asyncio.sleep(5)
await create_room_op(room_id, port, video_url)
print(3)
async def create_room_op(room_id, port, video_url):
# 启动子进程
process = await asyncio.create_subprocess_exec(
'/home/wangxv/test_image/venv/bin/python', '/home/wangxv/test_image/test_watch_together/son_watch.py',
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
# 发送输入到子进程
input_str = port+" "+room_id+" "+video_url
process.stdin.write(input_str.encode())
await process.stdin.drain()
process.stdin.close()
# 读取子进程的标准输出和标准错误输出
output, error = await asyncio.gather(
process.stdout.readline(),
process.stderr.readline())
# 等待子进程结束
await process.wait()
# 处理输出结果
output_str = output.decode().strip()
error_str = error.decode().strip()
return output_str, error_str
async def touch_html(url,room_id):
print("create new html")
process = await asyncio.create_subprocess_exec(
'touch',
'/var/www/chat_app_update/watch_html/'+room_id+'.html',
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
# 读取子进程的标准输出和标准错误输出
output, error = await asyncio.gather(
process.stdout.readline(),
process.stderr.readline())
# 等待子进程结束
await process.wait()
print("cp op finish")
# 处理输出结果
output_str = output.decode().strip()
error_str = error.decode().strip()
with open("/var/www/chat_app_update/watch_html/origin.html","r")as f:
s=f.read()
with open('/var/www/chat_app_update/watch_html/'+room_id+'.html',"w")as f1:
f1.write(html_with_url(s,url))
return output_str,error_str
def html_with_url(html,url):
# 定义正则表达式
pattern = r'(?<=source": ")[^"]+'
# 使用正则表达式查找需要替换的URL
new_html = re.sub(pattern, url, html)
return new_html
asyncio.run(main())
son_watch.py
import websockets
import asyncio
class WatchChat:
def __init__(self, host, port,room_id,video_url):
self.host = host
self.port = port
self.time_set={}
self.room_id=room_id
self.video_url=video_url
self.clients = set()
async def handle_client(self, websocket, path):
# 存储连接的客户端
self.clients.add(websocket)
try:
await self.on_connect(websocket)
async for message in websocket:
await self.on_message(websocket, message)
finally:
# 客户端断开连接时从集合中移除
self.clients.remove(websocket)
await self.on_disconnect(websocket)
async def on_connect(self, websocket):
# 在客户端连接时触发的回调函数
print(f"New client connected: {websocket.remote_address}")
async def on_message(self, websocket, message):
# 在接收到客户端消息时触发的回调函数
print(f"Received from {websocket.remote_address}: {message}")
if message.startswith("time"):#接收到的消息时间格式进行特殊处理
second=message.split()[1]
min_progress=999999999
self.time_set[websocket]=int(second)
for client in self.clients:
try:
if min_progress>self.time_set[client]:
min_progress=self.time_set[client]
except:
pass
for client in self.clients:
try:
if self.time_set[client]-min_progress>20:
await self.send_to_all("ad "+str(min_progress))
break
except:
pass
else:
await self.send_to_all(message) # 将消息发送给所有客户端
async def on_disconnect(self, websocket):
# 在客户端断开连接时触发的回调函数
print(f"Client disconnected: {websocket.remote_address}")
async def send_to_all(self, message):
print("TRY send to all")
print("Client num"+str(len(self.clients)))
# 发送消息给所有客户端
for client in self.clients:
await client.send(message)
async def check_clients(self):
while True:
await asyncio.sleep(60) # 每隔30秒检查一次
if len(self.clients) == 0:
print("No clients, stopping server...")
#update the file
remove_port_from_file("ports_using.txt",self.port)
exit("no connection")
self.server.close() # 关闭server
await self.server.wait_closed() # 等待server关闭
break
def start(self):
self.server = websockets.serve(self.handle_client, self.host, self.port)
asyncio.get_event_loop().create_task(self.check_clients()) # 创建定时任务
asyncio.get_event_loop().run_until_complete(self.server)
asyncio.get_event_loop().run_forever()
def remove_port_from_file(filename, port):
try:
with open(filename, "r") as file:
ports = file.readline().split()
if str(port) in ports:
ports.remove(str(port))
with open(filename, "w") as file:
file.write(" ".join(ports))
print(f"Port {port} removed from {filename} successfully.")
except IOError as e:
print(f"Error occurred while removing port {port} from {filename}: {e}")
def add_port_to_file(filename, port):
try:
with open(filename, "a") as file:
file.write(str(port) + " ")
print(f"Port {port} added to {filename} successfully.")
except IOError as e:
print(f"Error occurred while adding port {port} to {filename}: {e}")
s=input()
t=s.split(" ")
add_port_to_file("ports_using.txt",t[0])
WatchChat("0.0.0.0",t[0],t[1],t[2]).start()