Python系列 之 socket模块 - TCP协议类型 - 不同客户端之间通信

本文介绍如何使用Python的socket模块,基于TCP协议让不同客户端实现通信。服务端利用多线程处理客户端注册、数据分发及断开连接,客户端设置昵称后进行数据收发。通过示例代码展示了服务端和客户端的实现方式,探讨了socket的阻塞模式,并预告了非阻塞模式的学习。
摘要由CSDN通过智能技术生成

客户端之间通信

上一篇文章中了解了 socket对象的一些接收数据和发送数据的方法,以及服务端与多个客户端之间的通信,未能实现不同客户端之间的通信;现在还是基于TCP协议链接的模式来实现不同客户端之间实现通信;主要是服务端根据客户端发送过来的数据进行分析,然后分发给其他客户端。

服务端

服务端和上篇文章中大致思路是一样的;都是利用多线程模式来实现和多个客户端进行连接;
利用clients_dict变量来存储不同客户端的信息:客户端昵称,客户端连接对象。

# demo_socket_server_1.py文件
import logging
import socket
import threading
import re

logging.basicConfig(level=logging.DEBUG,
                    format="%(asctime)s>%(message)s", datefmt="%Y-%m-%d %H:%M:%S")
logger = logging.getLogger(__name__)

class ServerClass(object):
    """docstring for ServerClass"""
    __HOST = "127.0.0.1"
    __PORT = 9898

    def __init__(self):
        # 创建 套接字对象 socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
        # 参数:family:地址簇  type:套接字类型
        self.__TCP_SOCKET = socket.socket(
            family=socket.AF_INET, type=socket.SOCK_STREAM)
        # 服务端的地址 address
        self.__ADDR = (ServerClass.__HOST, ServerClass.__PORT)
        # 存放 客户端 信息
        self.clients_dict = dict()

    def start_server(self):
        """启动服务端方法"""
        with self.__TCP_SOCKET as sock:
            # 将套接字绑定到 address
            sock.bind(self.__ADDR)
            # 启动一个服务器用于接受连接 socket.listen([backlog])
            sock.listen(5)
            logger.info("等待连接...")
            # 循环控制 用来等待客户端连接 以及保持连接状态
            while True:
                # 接受一个连接 返回值是一个 (conn, address)
                conn, addr = sock.accept()
                # 多个客户端连接需要开启新的线程或者是进程
                th = threading.Thread(target=self.conn_client,
                                      args=(conn, addr), daemon=True)
                th.start()

然后是启动线程所对应的方法:
主要实现三个功能:

  1. 所有连接的客户端需要先注册一个昵称,并验证昵称是否合法,验证通过就相当于登录成功了,可以进行交互了,并把客户端信息添加到clients_dict 变量;给已经在线的客户端发送上线信息
  2. 接收客户端连接发送的数据
  3. 对接收到的数据进行发送
  4. 客户端断开连接从clients_dict 变量删除客户端信息;给在线的客户端发送下线信息
# demo_socket_server_1.py文件
    def conn_client(self, conn, addr):
        """连接客户端,并进行收发数据"""
        with conn:
            # 如果有新的连接进来就 发送数据;
            message = ["hi! %s %s \n" % addr, "请先输入你的昵称:"]
            message = "".join(message)
            conn.sendall(message.encode("utf-8"))
            # 获取客户端昵称
            client_username = self.login_client(conn)
            logger.info("正在与 %s 建立连接...", client_username)
            # 循环控制 使用accept()方法返回的套接字对象 收发数据
            while True:
                try:
                    recv_data = conn.recv(1024).decode("utf-8")
                    if (not recv_data) or (recv_data in ("quit", "exit")):
                        logger.info("与 %s,断开连接...", client_username)
                        # 客户端退出
                        self.quit_client(client_username)
                        break
                    # 根据收到的数据 进行判断该怎么处理数据
                    self.send_msg_by_type(client_username, recv_data)
                except ConnectionResetError as e:
                    logger.error("与 %s 意外断开连接...",
                                 client_username, exc_info=True)
                    self.quit_client(client_username)
                    break

登录和验证客户端昵称的方法:

# demo_socket_server_1.py文件
    def login_client(self, conn):
        """
        客户端上线
        验证客户端昵称是否合法;
        通知其他客户端 client_username 上线了
        添加客户端信息到clients_dict
        """
        client_username = self.ver_username(conn)
        message = f"{
     client_username} 上线了"
        if self.clients_dict != {
   }:
            self.send_msg(message, *self.clients_dict.keys())
        self.add_client(client_username, conn)
        return client_username

    def ver_username(self, conn):
        """
        接收客户端昵称并验证是否合法
        如果验证不通过 递归接着验证
        """
        client_username = conn.recv(1024).decode("utf-8")
        if client_username in self.clients_dict.keys():
            message = f"{
     client_username} 已经存在了"
            conn.sendall(message.encode("utf-8"))
            self.ver_username(conn)
        elif len(client_username) > 5:
            message = f"{
     client_username} 这个昵称太长了,不能超过5个字符"
            conn.sendall(message.encode("utf-8"))
            self.ver_username(conn)
        elif re.search(r"[^0-9a-zA-Z]", client_username):
            message = f"{
     client_username} 昵称只能是数字或者字母,或者数字和字母组合"
            conn.sendall(message.encode("utf-8"))
            self.ver_username(conn)
        else:
            message = f"OK,设置成功! 可以使用 {
     client_username} 这个昵称\n输入 -help 查看帮助文档"
            conn.sendall(message.encode("utf-8"))
            return client_username

    def add_client(self, client_username, conn):
        """增加客户端信息"""
        self
  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值