Python 编写多个 TCP Client一起聊天程序

Python 编写多个 TCP Client一起聊天程序

目录

Python 编写多个 TCP Client一起聊天程序

查询电脑IP地址

建立TCP Client端

建立TCP Server端

建立通信

查询电脑IP地址

首先可以通过进入命令窗口之后,输入:ipconfig ,按下回车即可看到着整个电脑的ip详细信息,这里可以看到IPv4 地址为: 10.88.34.234

或者也可以通过网络与共享中心中的已链接的网络,看到网络的详细IP配置信息。

TCP通信需要经过创建连接、数据传送、终止连接三个步骤。在通信开始之前,一定要先建立相关的链接,才能发送数据

建立TCP Client

下面为简单的TCP客户端,用于连接到指定的服务器地址和端口,发送一条消息,然后关闭连接。客户端是单次交互的,即它发送一条消息后就关闭了连接。想要使其成为一个完整的聊天客户端,它需要能够持续接收和发送消息,直到用户决定退出。

  1. import socket
    # 1.创建socket
    tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    # 2. 链接服务器
    server_addr = ("10.88.34.234", 7002)
    tcp_socket.connect(server_addr)
    
    # 3. 发送数据
    send_data = input("请输入要发送的数据:")
    tcp_socket.send(send_data.encode("gbk"))
    
    # 4. 关闭套接字
    tcp_socket.close()

根据提供的代码进行改进,可以持续发送和接收消息,直到用户输入特定的命令(例如"exit")来关闭连接,同时,在多线程环境中,确保线程安全性是非常重要的,特别是涉及到共享资源(如 socket)时。当多个线程同时试图发送消息,可能导致发送的数据混乱,或者产生异常。

使用锁(threading.Lock)来保护共享资源,确保在某一时刻只有一个线程能够访问这个资源。在代码中添加一个锁,以确保在发送消息时不会被其他线程中断:

  1. import socket
    import threading
    
    # 添加一个锁
    send_lock = threading.Lock()
    
    def receive_messages(client_socket):
        try:
            while True:
                data = client_socket.recv(1024)
                if not data:
                    break
                print(data.decode('gbk'))
        except Exception as e:
            print(f"Error receiving messages: {e}")
    
    def send_message(tcp_socket, message):
        # 使用锁确保发送操作的原子性
        with send_lock:
            tcp_socket.send(message.encode('gbk'))
    
    def start_client(server_addr):
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as tcp_socket:
            tcp_socket.connect(server_addr)
    
            # 启动接收消息的线程
            receive_thread = threading.Thread(target=receive_messages, args=(tcp_socket,))
            receive_thread.start()
    
            while True:
                send_data = input("请输入要发送的数据:")
                if send_data.lower() == "exit":
                    break
    
                # 调用发送消息的函数
                send_message(tcp_socket, send_data)
    
            receive_thread.join()
    
    if __name__ == "__main__":
        server_addr = ("10.88.34.234", 7002)
        start_client(server_addr)

建立TCP Server

在程序中,如果想要完成一个TCP服务器的功能,需要以下流程:1. socket创建一个套接字;2. bind绑定ip和port;3. listen使套接字变为可以被动链接;4. accept等待客户端的链接,5. recv接收数据

基本的TCP服务器,它可以监听指定的IP地址和端口,接受客户端的连接请求,并接收来自客户端的数据。

  1. import socket
    
    # 创建socket
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    # 本地信息
    address = ('10.88.34.234', 7002)
    
    # 绑定
    tcp_server_socket.bind(address)
    
    tcp_server_socket.listen(128)
    
    while True:
        # 等待新的客户端连接
        client_socket, clientAddr = tcp_server_socket.accept()
    
        # 接收对方发送过来的数据
        recv_data = client_socket.recv(1024)  # 接收1024个字节
        print('接收到的数据为:', recv_data.decode('gbk'))
    
        client_socket.close()
    
    tcp_server_socket.close()

修改后的代码可以支持多客户端,并且使客户端之间可以互相通讯:使用多线程处理每个客户端连接。保存所有活跃的客户端连接,以便可以将消息转发给所有客户端。

可以处理多个客户端的连接,并将任何客户端发送的消息转发给所有其他客户端。当客户端断开连接时,服务器将从列表中移除该客户端,并关闭相应的socket。当服务器接收到来自客户端的消息时,它会在消息前添加发送者的地址信息,然后将这个消息转发给所有其他客户端。

  1. import socket
    import threading
    
    def handle_client(client_socket, client_addr, all_clients):
        while True:
            try:
                # 接收数据
                recv_data = client_socket.recv(1024)
                print(f"接收到来自 {client_addr} 的数据: {recv_data.decode('gbk')}")
                if recv_data:
                    # 构建带有发送者地址的消息
                    message = f"接收到来自 {client_addr} 的数据: {recv_data.decode('gbk')}"
                    # 转发数据给所有客户端
                    broadcast(message.encode('gbk'), client_socket, all_clients)
                else:
                    # 如果接收的数据为空,则表示客户端已经断开
                    remove_client(client_socket, all_clients)
                    break
            except ConnectionResetError:
                # 处理客户端异常断开的情况
                remove_client(client_socket, all_clients)
                break
    
    def broadcast(message, sender_socket, all_clients):
        for client in all_clients:
            if client is not sender_socket:
                try:
                    client.send(message)
                except Exception as e:
                    print(f"Error sending message to client: {e}")
    
    def remove_client(client_socket, all_clients):
        client_socket.close()
        all_clients.remove(client_socket)
        print(f"Removed client: {client_socket}")
    
    def start_server():
        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_socket.bind(('10.88.34.234', 7002))
        server_socket.listen(128)
    
        all_clients = []
    
        while True:
            client_socket, client_addr = server_socket.accept()
            print(f"Accepted connection from {client_addr}")
            all_clients.append(client_socket)
    
            client_thread = threading.Thread(target=handle_client, args=(client_socket, client_addr, all_clients))
            client_thread.start()
    
    if __name__ == "__main__":
        start_server()

建立通信

打开四个进程,第一个进程用于运行TCP Server程序,其余三个作为不同的三个TCP Client端。分别运行并建立连接并通信。

当Client1发送消息时

服务器和其他两个Client均接收到消息

client在接收到其他client发送过来的消息后,也可以再次发送消息,直至发送exit后退出

当客户端断开连接时,服务器将从列表中移除该客户端,并关闭相应的socket。

  • 27
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值