文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

python套接字(二):实现一个服务器和多客户端连接

2023-09-18 16:02

关注

文章目录


前言

在上一篇博客python套接字(一):socket的使用简单说明了一下套接字的使用,也实现了使用套接字来传输消息,但是也有一个问题,就是这种实现方式只能一个服务器连接一个客户端,意味着有几个个客户就要创建结果服务器,而且客户端直接还不能通信,这样就和现实生活中的情况不符,接下来讲一下如何实现一个服务器和多个客户端进行连接。本篇博客参考了python+tcp实现多人聊天室

一、问题

在上一章的基础上在执行客户端代码,相当于有两个客户端同时向服务器发送请求,会有如下结果:
客户端:
在这里插入图片描述
服务器:
在这里插入图片描述
你会发现,服务器上面既没有打印第二个客户端的信息,也没有显示第二个客户端发送的消息,说明服务器只能处理第一个客户端的消息。

二、实现一个服务器连接多个客户端

1、问题分析

为什么两个客户端都能连接服务器,但是服务器只能处理一个客户端的消息呢?因为服务器里面只有一个主线程,该线程接收到第一个客户端的连接之后,就腾不出手来解决其他线程了。要解决这个问题,就要使用到多线程。

2、代码实现

目标:模拟创建一个多人聊天室(类似微信群),一个人在上面发消息,所有客户端都能看到。
因为有些命令有特殊的功能,因此自定义了如下规则:

命令格式说明
name -n更改用户名为name并且重新进入聊天室
message -ta发送消息给聊天室的所有成员
exit退出聊天室

a、服务器端

服务器端不仅要接收源源不断的客户端请求,而且还要接收和发送数据,所以大概的设计思路如下:主线程负责对发起请求的客户创建链接,并且将每个用户对应的链接保存到一个字典中去,方便调用。对于每个用户链接,都创建两个子线程一个子线程用来发送数据另外一个子线程用来接收数据。实现代码如下:
tcp_server.py

import socketfrom threading import Threadimport timeimport sys# 创建存储对象class Node:    def __init__(self):        self.Name = None    # 用户名        self.Thr = None     # 套接字连接对象class TcpServer:    user_name = {}  # 存储用户信息; dict 用户名:Node对象    def __init__(self, port):        """        初始化服务器对象        port:   服务器端口        """        self.server_port = port      # 服务器端口        self.tcp_socket = socket.socket()       # tcp套接字        self.tcp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)       # 端口重用        self.tcp_socket.bind(self.server_port)    def start(self):        """        启动服务器        """        self.tcp_socket.listen(10)      # 设置服务器接受的链接数量        print(self.get_time(), "系统:等待连接")        while True:            try:                conn, addr = self.tcp_socket.accept()       # 监听客户端的地址和发送的消息            except KeyboardInterrupt:       # 按下ctrl+c会触发此异常                self.tcp_socket.close()     # 关闭套接字                sys.exit("\n" + self.get_time() + "系统:服务器安全退出!")        # 程序直接退出,不捕捉异常            except Exception as e:                print(e)                continue            # 为当前链接创建线程            t = Thread(target=self.do_request, args=(conn, ))            t.start()    def do_request(self, conn):        """        监听客户端传送的消息,并将该消息发送给所有用户        """        conn_node = Node()        while True:            recv_data = conn.recv(1024).decode('utf-8').strip()     # 获取客户端发来的数据            info_list = recv_data.split(" ")        # 切割命令            # 如果接收到命令为exit,则表示该用户退出,删除对应用户信息,关闭连接            if recv_data == "exit":                msg = self.get_time() + " 系统:用户" + conn_node.Name + "退出聊天室!"                print(msg)                self.send_to_other(conn_node.Name, msg)                conn.send('exit'.encode("utf-8"))                self.user_name.pop(conn_node.Name)                conn.close()                break            else:                try:                    A = info_list[-2], info_list[-1]                except IndexError:                    conn.send((self.get_time() + ' 系统:无法识别您的指令,请重新输入!').encode('gb2312'))                    continue            if info_list[-1] == '-n':                # 新用户注册                print(self.get_time() + ' 系统:' + info_list[0] + '连接成功')                data_info = self.get_time() + ' 系统:' + info_list[0] + '加入了聊天'                self.send_to_all(data_info)                conn.send('OK'.encode('utf-8'))                conn_node.Name = info_list[0]                conn_node.Thr = conn                self.user_name[info_list[0]] = conn_node            elif info_list[-1] == '-ta':                # 群发消息                msg = self.get_time() + ' %s:' % conn_node.Name + ' '.join(info_list[:-1])                self.send_to_all(msg)    def send_to_all(self, msg):        """        对所有用户发送消息        """        print(msg)        for i in self.user_name.values():            i.Thr.send(msg.encode('utf-8'))    def send_to_other(self, name, msg):        """        对除了当前发送信息的用户外的其他用户发送消息        """        # print("收到消息:" + msg)        for n in self.user_name:            if n != name:                self.user_name[n].Thr.send(msg.encode('utf-8'))            else:                continue    def get_time(self):        """        返回当前系统时间        """        return '[' + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + ']'if __name__ == '__main__':    HOST = "127.0.0.1"    POST = 9999    server = TcpServer((HOST, POST))    server.start()

b、客户端

客户端就简单一点,只需要不断的发送数据和接收数据即可。主线程创建链接并和服务器连接。然后创建两个子线程,分别负责数据的接收和发送。代码实现如下:
tcp_clinet.py

import socketfrom threading import Threadclass TcpClient:    server_addr = ('127.0.0.1', 9999)    def __init__(self):        self.tcp_cli_socket = socket.socket()    def msg_recv(self):        """        接收数据        """        while True:            data = self.tcp_cli_socket.recv(1024)            if data.decode("utf-8") == "exit":                print('客户端退出')                self.tcp_cli_socket.close()                break            print(data.decode("utf-8"))    def msg_send(self):        """        发送数据        """        while True:            data_info = input("请发言:")            if data_info == "exit":                self.tcp_cli_socket.send(data_info.encode("utf-8"))                break            else:                self.tcp_cli_socket.send((data_info + ' -ta').encode("utf-8"))    def start(self):        """        连接服务器        """        try:            self.tcp_cli_socket.connect(self.server_addr)        except Exception as e:            print("连接失败,请重试!")            self.tcp_cli_socket.close()            print(e)            return        while True:            name = input("请输入用户名:")            self.tcp_cli_socket.send((name + ' -n').encode('utf-8'))            data = self.tcp_cli_socket.recv(128).decode('utf-8')            print(data)            if data == "OK":                print("你已成功进入聊天室")                break            else:                print(data)        t = Thread(target=self.msg_recv)        t.start()        t1 = Thread(target=self.msg_send)        t1.start()if __name__ == '__main__':    client = TcpClient()    client.start()

3、运行

启动一个服务器和两个客户端,两个客户端之间进行交流,服务器则负责转发它们发送的消息(有点瑕疵)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以看到它们发送的消息对方都能收到。
下一章:python套接字(三):结合pyside2实现多人聊天室

来源地址:https://blog.csdn.net/fresh_nam/article/details/131171502

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-服务器
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯