PythonSocket:如何在客户端同时实现recv()和send()

2024-07-05 11:57:18 发布

您现在位置:Python中文网/ 问答频道 /正文

我的环境:windows10&;python 3.6

  1. 目的:我想建立一个在线聊天室,这意味着当客户端连接到服务器并向服务器发送消息时,服务器将向与之有联系的每个客户端广播消息
  2. 关键点:因此存在一个问题,我希望客户端同时侦听服务器并等待输入。(代码发布在下面)
  3. 我的解决方案:当我试图通过python的线程化来解决这个问题时,结果是如果客户端A和客户端B都连接到服务器,并且客户端A发送多条消息,而客户端B只能接收第一条消息或接收重复的&;杂乱的信息。(错误信息发布在下方)
  4. 代码解释如果我糟糕的开发技能让你感到困惑,我想简单解释一下我的代码。
    1. 在发送每条消息之前,将首先发送消息的长度,因此recv端很容易确定应该分配多大的缓冲区
    2. 所有这些“try”和“except”都用于使系统更加健壮,以防意外断开连接
    3. 在服务器端,它接受连接请求并通过线程处理每个连接。在每个线程中,如果消息是从客户端发送的,服务器将向其他客户端广播消息
    4. 在客户端,我创建了一个线程来监听来自服务器的消息,并使用“输入”来等待输入
  5. 问题我的问题是:
    我如何认识到:任何客户端都可以自由发送尽可能多的消息,而其他客户端可以在等待输入的同时查看所有消息。(可能会更改输入方式?。例如,不使用命令“输入”?)

IMAGE

图像解释
1.服务器在左侧窗口运行,客户端A在右侧窗口运行,客户端B在底部窗口运行。
2.具体操作是:客户端B发送“a B c d e”,客户端a接收“a”。客户端A发送“A b c e d”,客户端b接收“[某些信息..]A\n[某些信息..]A”
3.我们可以从图中了解到:当客户端在其他客户端发送任何消息之前发送多条消息时,其他客户端将只接收客户端A发送的第一条消息或接收重复消息

最后,很抱歉我对套接字和线程理解不好,解释不好。谢谢你的帮助

code for clinet:

import socket
import time
import threading


SERVER_HOST = "" # server ip
LOCAL_HOST = "10.124.18.206"
HOST = LOCAL_HOST

HEADER = 64
PORT = 42366
CHECK_CLIENT = "!CHECK"
FORMAT = "utf-8"
DISCONNECT_MESSAGE = "!DISCONNECT"
KEEP_MESSAGE = "KEEP"
TIMEOUT = 50


def gettimestamp():
    return time.strftime("%Y-%m-%d %H:%M:%S",time.localtime())

def send(msg):
    message = msg.encode(FORMAT)
    msg_length = len(message)
    send_length = str(msg_length).encode(FORMAT)
    send_length += b' ' * (HEADER-len(send_length))
    client.send(send_length)
    client.send(message)

def recv():
    message = client.recv(HEADER)
    msg_length = len(message)
    message = message.decode(FORMAT)
    data = client.recv(msg_length).decode(FORMAT)
    return data

def listen():
    while True:
        data = recv()
        if data:
            print(f"[MESSAGE {gettimestamp()}] {data}" )
        else:
            break

def start():
    client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    print(f"[STARTING] Connecting to {HOST}:{PORT}")
    client.connect((HOST,PORT))
    send(CHECK_CLIENT)
    client_num = recv()
    print(f"There are {client_num} people in the chat.")
    print("Let's chat!")
    connected = True
    listen_th = threading.Thread(target=listen,args=())
    listen_th.daemon = True
    listen_th.start()
    while connected:
        time.sleep(0.3)
        data = input("[INPUT] ")

        try:
            send(data)
        except ConnectionAbortedError as e:
            connected = False

        if data == DISCONNECT_MESSAGE:
            connected = False

    print(f"[DISCONNECT {gettimestamp()}] {HOST}:{PORT}")
    client.close()

if __name__ == "__main__":
    start()


code for server

# -*- coding: utf-8 -*-

import socket
import threading
import time


SERVER_HOST = "" # server ip
LOCAL_HOST = "10.124.18.206"
HOST = LOCAL_HOST

PORT = 42366
CHECK_CLIENT = "!CHECK"
DISCONNECT_MESSAGE = "!DISCONNECT"
HEADER = 64
FORMAT = "utf-8"
TIMEOUT = 60

clients = []

def gettimestamp():
    return time.strftime("%Y-%m-%d %H:%M:%S",time.localtime())

def send(conn,msg):
    try:
        message = msg.encode(FORMAT)
        msg_length = len(message)
        send_length = str(msg_length).encode(FORMAT)
        send_length += b' ' * (HEADER-len(send_length))
        conn.send(send_length)
        conn.send(message)
    except OSError as e:
        print(f"{e} in line 33")
        clients.remove(conn)

def update_chat(msg,conn):
    global clients
    temp = []
    for client in clients:
        flag = 1
        if client != conn:
            try:
                send(client,msg)
            except BrokenPipeError as e:
                print(f"{e} in line 45")
                flag = 0
                continue
        if flag == 1:
            temp.append(conn)

    clients = temp


def handle_client(addr,conn):
    print(f"[NEW CONNECTION {gettimestamp()}] {addr} connected.")
    print(f"[ACTIVE CONNECTIONS {gettimestamp()}] {threading.activeCount()-1}")
    connected = True
    clients.append(conn)
    while connected:
        try:
            conn.settimeout(TIMEOUT)
            msg_length = conn.recv(HEADER).decode(FORMAT)
            conn.settimeout(None)
            if msg_length:
                msg_length = int(msg_length)
                try:
                    msg = conn.recv(msg_length).decode(FORMAT)
                except ConnectionResetError as e:
                    print(f"{e} in line 67")
                    connected = False
                if msg == DISCONNECT_MESSAGE:
                    connected = False
                    clients.remove(conn)
                elif msg == CHECK_CLIENT:
                    client_num = threading.activeCount()-1
                    send(conn,str(client_num))
                else:
                    update_chat(f"{msg}",conn)

                print(f"[{addr[0]+str(addr[1])} {gettimestamp()}] {msg}")

        except ConnectionResetError as e:
            print(f"{e} in line 81")
            connected = False
        except ConnectionAbortedError as e:
            print(f"{e} in line 84")
            continue
        except socket.timeout as e:
            connected = False


    print(f"[DISCONNECTED {gettimestamp()}] {addr}")

    conn.close()

def start():
    print("[STARTING] Server is starting...")
    server = socket.socket()
    print(f"[BIDING] Binding address {HOST}:{PORT}")
    server.bind((HOST,PORT))
    server.listen()
    print(f"[LISTENING {gettimestamp()}] Server is listening on {HOST}:{PORT}")
    while True:
        conn,addr = server.accept()
        thread = threading.Thread(target=handle_client,args=(addr,conn))
        thread.daemon = True
        thread.start()

if __name__ == "__main__":
    start()



Tags: clientsendformathost消息客户端messagedef