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

这篇具有很好参考价值的文章主要介绍了python套接字(二):实现一个服务器和多客户端连接。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

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

一、问题

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

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

1、问题分析

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

2、代码实现

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

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

a、服务器端

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

import socket
from threading import Thread
import time
import 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 socket
from threading import Thread


class 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 多个客户端,socket,python,socket
python 多个客户端,socket,python,socket
python 多个客户端,socket,python,socket
可以看到它们发送的消息对方都能收到。
下一章:python套接字(三):结合pyside2实现多人聊天室文章来源地址https://www.toymoban.com/news/detail-624699.html

到了这里,关于python套接字(二):实现一个服务器和多客户端连接的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 兼容流式套接字与数据报 套接字的回射服务器

    实验流程: 1)设计基于select模型的双协议服务器方案 创建套接字:为每种协议创建一个套接字(例如,TCP和UDP)。 绑定套接字:将套接字绑定到指定的端口上。 设置套接字选项:为每个套接字设置相应的选项,例如允许重用地址、设置超时时间等。 监听套接字:对于TCP套

    2024年04月16日
    浏览(44)
  • TCP服务器(套接字通信)

    服务器     客户端     结果    

    2024年02月12日
    浏览(37)
  • 服务器搭建(TCP套接字)-libevent版(服务端)

         Libevent 是一个开源的 事件驱动库 ,用于开发高性能、并发的网络应用程序。它提供了跨平台的事件处理和网络编程功能,具有高性能、可扩展性和可移植性。下面详细讲解 Libevent 的主要组成部分和使用方法。 事件基础结构(event_base)是 Libevent 的核心组件,用于

    2024年02月07日
    浏览(91)
  • [Linux] 网络编程 - 初见TCP套接字编程: 实现简单的单进程、多进程、多线程、线程池tcp服务器

    网络的上一篇文章, 我们介绍了网络变成的一些重要的概念, 以及 UDP套接字的编程演示. 还实现了一个简单更简陋的UDP公共聊天室. [Linux] 网络编程 - 初见UDP套接字编程: 网络编程部分相关概念、TCP、UDP协议基本特点、网络字节序、socket接口使用、简单的UDP网络及聊天室实现…

    2024年02月16日
    浏览(59)
  • 【Linux后端服务器开发】socket套接字

    目录 一、socket 套接字概述 二、socket 函数接口 三、IP地址与端口号的网络格式 四、TCP协议的本地通信C语言示例 socket 是什么? socket 本质上是一个抽象的概念,它是一组用于 网络通信的 API , 提供了一种统一的接口 ,使得应用程序可以通过网络进行通信。在不同的操作系统

    2024年02月16日
    浏览(34)
  • 网络编程之 Socket 套接字(使用数据报套接字和流套接字分别实现一个小程序(附源码))

    网络编程是指网络上的主机,通过不同的进程,以编程的方式实现 网络通信(或称为网络数据传输) 只要满足不同的进程就可以进行通信,所以即便是在同一个主机,只要不同的进程,基于网络传输数据,也属于网络编程 在一次网络传输中: 发送端: 数据的 发送方进程

    2024年02月03日
    浏览(45)
  • 【Linux网络编程】网络编程套接字(TCP服务器)

    作者:爱写代码的刚子 时间:2024.4.4 前言:本篇博客主要介绍TCP及其服务器编码 只介绍基于IPv4的socket网络编程,sockaddr_in中的成员struct in_addr sin_addr表示32位 的IP地址 但是我们通常用点分十进制的字符串表示IP地址,以下函数可以在字符串表示和in_addr表示之间转换 字符串转in

    2024年04月14日
    浏览(63)
  • TCP流套接字编程(模拟多个客户端与服务器交互)

    目录 一、ServerSocket API 1.1、ServerSocket构造方法 1.2、ServerSocket方法 二、Socket API  2.1、socket构造方法  2.2、socket方法 三、TCP 中的长短连接 四、示例  实现聊天室功能 五、存在的问题  ServerSocket 是创建TCP服务端Socket的API。 1.1、ServerSocket构造方法 方法签名 方法说明 ServerSocket

    2024年02月13日
    浏览(39)
  • 【网络编程】网络套接字&udp通用服务器和客户端

    端口号(port)是传输层协议的内容: 端口号是一个2字节16位的整数(uint16) 端口号用来标识主机上的一个进程 IP地址+port能够标识网络上的某一台主机和某一个进程 一个端口号只能被一个进程占用 此处我们先对TCP(Transmission Control Protocol 传输控制协议) 有一个直观的认识,后面再

    2024年02月16日
    浏览(78)
  • psql: 错误: 连接到套接字“/var/run/postgresql/.s.PGSQL.5432“上的服务器失败:没有那个文件或目录服务器是否在本地运行并接受该套接字上的连接?

    查看 Postgresql 日志文件: 编码引起的报错,选择相应的编码支持。 这里我的是 en_US.UTF-8 查看一下本机所支持的所有编码 可见目前并不支持 en_US.UTF-8 ,所以先试着添加该支持,用命令: 通过空格选中 en_US.UTF-8 该编码格式,并确定。确定之后,系统环境默认的区域设置中会有

    2024年02月13日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包