需求
python通过tcp/socket实现多人聊天室
思路
- 服务器以多线程方式处理客户端数据
- 客户端连接上服务器先把客户端名字发送给服务器(#name#)
- 服务器把在线客户端存放在字典中{client_socket:name}
- 客户端A给客户端B发送数据格式(B:内容),假如群发的话直接发送(内容)
服务器
- 创建socket并设置为端口复用
- 绑定ip和port
- 设置为被动socket(listen)
- 等待客户端到来
- 每一个客户端到来创建一个线程收发数据
实现代码
#!/usr/bin/env python3
# coding=utf-8
from socket import *
from threading import Thread
def recv_data(client_socket, client_addr, dict_client):
while True:
# 接收到的数据按照utf-8解码
data = client_socket.recv(1024).decode("utf-8")
if data:
if data.startswith("#") and data.endswith("#"):
name = data[1:len(data)-1]
print("\"{}\"已上线!".format(name))
dict_client.setdefault(client_socket, name)
else:
name = dict_client[client_socket]
send_data(dict_client, data, name)
else:
# 有客户端断开连接的话将对于的client_socket:name从字典中删除,同时将对应的client_socket关闭
del_name = dict_client.pop(client_socket)
print("\"{}\"已下线!".format(del_name))
break
client_socket.close()
def send_data(dict_client, source_data, source_name):
if source_data.startswith("@") and source_data.endswith(";"):
temp_data = source_data[1:len(source_data)-1]
print("temp_data={}".format(temp_data))
if ":" in temp_data:
#单发
n = temp_data.find(":")
target_name = temp_data[:n]
target_data = temp_data[n+1:]
print("\"{}\" send \"{}\" to \"{}\"".format(source_name, target_data, target_name))
for client_socket, name in dict_client.items():
if name == target_name:
client_socket.send((source_name + ":" + target_data).encode("utf-8"))
else:
#群发
print("\"{}\" send \"{}\" to all".format(source_name, temp_data))
for client_socket, name in dict_client.items():
if name != source_name:
client_socket.send((source_name + ":" + temp_data).encode("utf-8"))
def main():
# 1、创建socket
server_socket = socket(AF_INET, SOCK_STREAM)
# 2、设置端口复用
server_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, True)
# 3、绑定端口
server_socket.bind(("", 8888))
# 4、设置为被动socket
server_socket.listen(64)
print("Listen...")
# 创建一个字典用来保存客户端{client_socket:name}
dict_client = {}
try:
while True:
# 5、等待客户端连接
client_socket, client_addr = server_socket.accept()
#print("新客户:[{},{}]已连接".format(client_addr[0], client_addr[1]))
# 6、创建一个线程处理客户端的数据
t1 = Thread(target=recv_data, args=(client_socket, client_addr, dict_client))
# 7、设置线程守护
t1.setDaemon(True)
# 8、启动线程
t1.start()
finally:
# 当为所有的客户端服务完成之后再进行关闭,表示不再接收新的客户端的连接
server_socket.close()
if __name__ == "__main__":
main()
客户端
- 创建socket
- 连接服务器
- 发送用户名给服务器
- 创建两个线程用于收发数据
实现代码
from socket import *
from threading import Thread
def recv_data(my_socket):
while True:
data = my_socket.recv(1024).decode("utf-8")
if data:
n = data.find(":")
source_name = data[:n]
source_data = data[n+1:]
print("\t{}\t{}\n>".format(source_name, source_data), end="")
else:
my_socket.close()
break
def send_data(my_socket,name):
while True:
temp_data = input(">")
data = "@" + temp_data + ";"
my_socket.send(data.encode("utf-8"))
def main():
# 设置用户名
name = input("请输入用户名:")
# 创建socket
my_socket = socket(AF_INET, SOCK_STREAM)
# 连接
my_socket.connect(("127.0.0.1", 8888))
# 收发数据
data = "#" + name + "#"
my_socket.send(data.encode("utf-8"))
tr = Thread(target=recv_data, args=(my_socket,))
ts = Thread(target=send_data, args=(my_socket,name))
#tr.setDaemon(True)
#ts.setDaemon(True)
tr.start()
ts.start()
if __name__ == "__main__":
main()