方法:
1.TCP协议
2.客户端使用多线程
3.服务端使用多路复用
服务端:
import socket
import select
'''
因为是单线程程序,所以没有设计管理员功能
'''
sock = socket.socket()
sock.bind(("0.0.0.0",8888))
sock.listen(1)
r_list = [sock]
w_list = []
x_list = [sock]
user = {}
while True:
rlist,wlist,xlist = select.select(r_list,w_list,x_list)
for r in rlist:
if r == sock:
soc,addr = r.accept()
while True:
# 客户端在连接时会立刻将用户名发送到服务端
name = soc.recv(1024).decode("utf-8")
if name in user:
soc.send("00".encode("utf-8")) # 表示重名
continue
soc.send("成功进入群聊".encode("utf-8"))
break
user[name] = addr
get_into = "%s进入群聊" % name
print(get_into)
# 向其他用户发送通知
for s in r_list:
if s == sock:
continue
elif s == r:
continue
else:
s.send(get_into.encode("utf-8"))
r_list.append(soc)
else:
try:
data = r.recv(1024).decode("utf-8")
# 若客户端退出过快会抛出此异常
except ConnectionResetError as e:
print(e)
data = "# 未知用户"
# 处理客户端退出请求
if data[0] == "#":
ls_data = data.split(" ")
get_out = "%s退出群聊" % ls_data[1]
print(get_out)
for s in r_list:
if s == sock:
continue
elif s == r:
continue
else:
s.send(get_out.encode("utf-8"))
del user[ls_data[1]]
r_list.remove(r)
r.close()
continue
# 转发信息
for s in r_list:
if s == sock:
continue
elif s == r:
continue
else:
s.send(data.encode("utf-8"))
for w in wlist:
pass
for x in xlist:
if x == sock:
x.close()
客户端:
import socket
import threading
'''
主进程负责发送信息,分支进程负责接收信息
使用状态变量控制控制台的使用权
'''
sock = socket.socket()
sock.connect(("127.0.0.1",8888))
# 储存对话信息
recv_data = []
# 储存程序状态,若为空字符串为输入状态,此时只会接收服务端信息,而不会在控制台打印
state = "0"
# 请求进入群聊
while True:
name = input("请输入用户名:")
if "#" in name:
print("用户名中不能包含#号")
continue
if "管理员" in name:
print("用户名中不能包含‘管理员’")
continue
sock.send(name.encode("utf-8"))
ser_data = sock.recv(1024).decode("utf-8")
if ser_data == "00":
print("用户名重复")
continue
print(ser_data)
break
# 分支线程执行函数
def client_recv():
global state
while True:
ser_data = sock.recv(1024).decode("utf-8")
if state == "":
recv_data.append(ser_data)
continue
print(ser_data)
# 创建分支线程
thread = threading.Thread(name="分支线程",target=client_recv,args=(),kwargs={})
thread.daemon = True #设置分支线程随主线程的退出而退出
thread.start()
while True:
# 按回车进入输入状态
state = input()
data = input("我说:")
if data == "":
for re in recv_data:
print(re)
recv_data.clear()
state = "0"
continue
# 输入#号退出
if data[0] == "#":
con = input("以#号开头表示退出群聊,输入1确认退出,输入0重新输入:")
if con == "0":
for re in recv_data:
print(re)
recv_data.clear()
state = "0"
continue
sock.send(("# %s" % name).encode("utf-8"))
print("已退出群聊")
break
sock.send(("%s说:%s" % (name,data)).encode("utf-8"))
for re in recv_data:
print(re)
recv_data.clear()
state = "0"
sock.close()