Это, вероятно, очень простое программирование обмена сообщениями сокета, но с моей неубедительной идеей о сокете и всем остальном я не мог заставить его работать должным образом, поэтому я обращаюсь за помощью.
Сценарий: два клиента отправляют сообщения. Сначала отправляет Halo и Seeya. После первый отправляет эти сообщения, второй отправляет Hello и Bye (этот клиент просто будет спать 6 секунд, чтобы сохранить этот порядок). На все сообщения сервер отвечает (original msg), client (number)!, и ответное сообщение передается клиентам оба.
В идеале результат для обоих клиентов будет выглядеть так:
Halo, client 1!
Seeya, client 1!
Hello, client 2!
Bye, client 2 !
Я не смог пронумеровать каждого клиента, но вот мой код работает странно.
import socket
clients = []
# send msgs to every client
def broadcast(message):
for client in clients :
client.send(message)
# connection part
s = socket.socket()
s.bind(('127.0.0.1', 7070))
s.listen(2)
while True:
c, addr = s.accept()
if c:
clients.append(c)
msg = c.recv(1024).decode()
msg += ', client!'
broadcast(msg.encode())
### here goes connection part ###
s.send(("Halo").encode())
print(f"server = {(s.recv(1024)).decode()}")
# I've tried adding socket closing/connection part here
s.send(("Seeya").encode())
print((s.recv(1024)).decode())
time.sleep(3)
s.close()
### here goes connection part ###
time.sleep(6) # waits for message order
s.send(("Hello").encode())
print(f"server = {(s.recv(1024)).decode()}")
# I've tried adding socket closing/connection part here
s.send(("Bye").encode())
print((s.recv(1024)).decode())
time.sleep(3)
s.close()
В результате я получаю ...
# On client 1 side
Halo, client! # no Seeya, Hello, Bye
# connection isn't closed
# On client 2 side
Hello, client! # no Seeya, Bye
Halo, client! # connection is closed






У вас здесь несколько проблем. Первая и основная проблема заключается в том, что основной цикл вашего сервера испорчен. Каждый раз при прохождении цикла ваш сервер хочет установить соединение accept. Итак, первый подключившийся клиент принимается, и сразу же приходит его первое сообщение. Но другой клиент еще не принят и поэтому не получит это первое сообщение. Затем принимается второе клиентское соединение, и первое сообщение это затем отправляется обоим клиентам, но затем цикл повторяется снова, и больше сообщений с сервера не будет отправляться до тех пор, пока не будет установлено соединение третий клиент. И Т. Д.
Таким образом, вам нужно разделить прием соединений и получение сообщений. Это можно сделать несколькими способами. Самый простой способ - использовать функцию select для ожидания сразу нескольких сокетов. То есть, если у вас есть список сокетов, включая прослушивающий сокет и ранее принятые, вы должны сделать что-то вроде этого:
# List starts with only listening socket
list_of_sockets = [lsock]
...
while True:
# Wait until some socket becomes "readable"
rfds, _wfds, _xfds = select.select(list_of_socks, [], [])
for sock in rfds:
if sock is lsock:
# Listening socket ready. Accept new connection
c, addr = lsock.accept()
print(f"Accepted {c}")
list_of_socks.append(c)
else:
msg = sock.recv(1024)
if msg:
# Received data from connected socket. Send to all
print(f"Got {msg.decode()} from {sock}")
broadcast(msg)
else:
# Got end of file (this client closed). Remove client from list
print(f"Closed {sock}")
list_of_socks.remove(sock)
Другая проблема с вашим кодом, которая будет нет, будет решена серверным кодом выше: вы не можете предполагать, что каждое отправленное вами сообщение будет получено как отдельный блок. То есть, если сервер отправляет «Halo», а затем он отправляет «Hello» до того, как вы (клиент) выполнили recv, то, по всей вероятности, все данные будут возвращены одним махом; это "HaloHello".
Поэтому, как правило, вам нужно поместить какой-то разделитель в данные (например, новую строку [\n] - но тогда вам нужно будет проанализировать полученные данные) или, что еще лучше, поместить поле фиксированной длины перед каждым message, задающий длину последующей части переменной длины, чтобы вы могли получать и обрабатывать только одно сообщение за раз. (В Python это обычно связано с использованием функций struct и pack модуля unpack.) В результате ваш текущий клиентский код, вероятно, не будет должным образом упорядочивать сообщения, как вы хотите.
Кроме того, - хотя вероятность возникновения проблем с ним меньше - то же самое и с send: не следует предполагать, что send(N) отправляет именно байты N. Он может отправлять 1, 2, 3 или N-1 байта. Вы можете использовать sendall, чтобы гарантировать отправку всех байтов.
Много недостающего кода. Предоставьте минимальный воспроизводимый пример, чтобы воспроизвести проблему.