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