0


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

文章目录


前言

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

一、问题

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

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

1、问题分析

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

2、代码实现

目标:模拟创建一个多人聊天室(类似微信群),一个人在上面发消息,所有客户端都能看到。
因为有些命令有特殊的功能,因此自定义了如下规则:
命令格式说明name -n更改用户名为name并且重新进入聊天室message -ta发送消息给聊天室的所有成员exit退出聊天室

a、服务器端

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

import socket
from threading import Thread
import time
import sys

# 创建存储对象classNode:def__init__(self):
        self.Name =None# 用户名
        self.Thr =None# 套接字连接对象classTcpServer:
    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)defstart(self):"""
        启动服务器
        """
        self.tcp_socket.listen(10)# 设置服务器接受的链接数量print(self.get_time(),"系统:等待连接")whileTrue: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()defdo_request(self, conn):"""
        监听客户端传送的消息,并将该消息发送给所有用户
        """
        conn_node = Node()whileTrue:
            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()breakelse:try:
                    A = info_list[-2], info_list[-1]except IndexError:
                    conn.send((self.get_time()+' 系统:无法识别您的指令,请重新输入!').encode('gb2312'))continueif 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)defsend_to_all(self, msg):"""
        对所有用户发送消息
        """print(msg)for i in self.user_name.values():
            i.Thr.send(msg.encode('utf-8'))defsend_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:continuedefget_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 socket
from threading import Thread

classTcpClient:
    server_addr =('127.0.0.1',9999)def__init__(self):
        self.tcp_cli_socket = socket.socket()defmsg_recv(self):"""
        接收数据
        """whileTrue:
            data = self.tcp_cli_socket.recv(1024)if data.decode("utf-8")=="exit":print('客户端退出')
                self.tcp_cli_socket.close()breakprint(data.decode("utf-8"))defmsg_send(self):"""
        发送数据
        """whileTrue:
            data_info =input("请发言:")if data_info =="exit":
                self.tcp_cli_socket.send(data_info.encode("utf-8"))breakelse:
                self.tcp_cli_socket.send((data_info +' -ta').encode("utf-8"))defstart(self):"""
        连接服务器
        """try:
            self.tcp_cli_socket.connect(self.server_addr)except Exception as e:print("连接失败,请重试!")
            self.tcp_cli_socket.close()print(e)returnwhileTrue:
            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("你已成功进入聊天室")breakelse: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实现多人聊天室

标签: python socket

本文转载自: https://blog.csdn.net/fresh_nam/article/details/131171502
版权归原作者 fresh_nam 所有, 如有侵权,请联系我们删除。

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

还没有评论