Python -Socket: Как реализовать recv () и send () одновременно на стороне клиента - PullRequest
0 голосов
/ 17 июня 2020

МОЙ СРЕДА : windows10 & python 3,6

  1. ЦЕЛЬ : Я хочу создать онлайн-чат, что означает, когда клиенты подключенный к серверам и отправляющий сообщения на сервер, сервер будет транслировать сообщения всем клиентам, которые имеют с ним соединение.
  2. КЛЮЧЕВОЙ ТОЧЕЧНИК : Итак, существует проблема, заключающаяся в том, что я хочу, чтобы клиент одновременно слушал сервер и ждал ввода. ( код размещен ниже )
  3. МОЕ РЕШЕНИЕ : когда я пытаюсь решить эту проблему с помощью многопоточности из python, результат заключается в том, что если, скажем, клиент A и клиент B оба подключены к серверу, и если клиент A отправляет несколько сообщений, а клиент B может получать только первое сообщение или получать повторяющиеся и беспорядочные сообщения. ( изображения ошибок размещены ниже )
  4. ОБЪЯСНЕНИЕ КОДА В случае, если мои плохие навыки развития смущают вас, я хочу сделать простое объяснение моего кода.
    1. Перед тем, как каждое сообщение будет отправлено, длина сообщения будет отправлена ​​первой, поэтому для стороны recv легко определить, какой размер буфера должен быть выделен.
    2. Все эти «попробовать» и «кроме» используются для повышения надежности системы в случае неожиданного отключения.
    3. На стороне сервера он принимает запросы на подключение и обрабатывает каждое подключение потоки. В каждом потоке, если сообщения были отправлены от клиентов, сервер затем будет транслировать сообщения другим клиентам.
    4. На стороне клиента я создаю поток для прослушивания сообщений с сервера и использую «ввод» для сохранения ждем ввода.
  5. ВОПРОС мои вопросы:
    Как я могу понять это: любой клиент может отправлять как можно больше сообщений, а другие клиенты могут раскрыть все сообщения во время ожидания ввода. (может быть, изменить способ ввода? например, не использовать команду "input"?)

image

ОБЪЯСНЕНИЕ К ИЗОБРАЖЕНИЮ
1. Сервер работает в левом окне, клиент A работает в правом окне, а клиент B работает в нижнем окне.
2. Подробная информация об операции: клиент B отправил "ab c de" , а клиент A получил "a" . клиент A отправил "ab c ed" , а клиент 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()


1 Ответ

1 голос
/ 17 июня 2020
                msg = conn.recv(msg_length).decode(FORMAT)

Это получит up до msg_length байт. Вы должны убедиться, что получаете ровно msg_length байт. Вероятно, вы захотите написать функцию получить все , которая вызывает recv несколько раз, пока не будет получено точно необходимое количество байтов.

...