Python 向所有客户端发送消息
这可能是一个非常简单的套接字消息交换编程,但由于我对套接字和其他方面的想法不充分,我无法使它真正正常工作,所以我寻求帮助 场景:两个客户端发送消息。第一个发送Python 向所有客户端发送消息,python,python-3.x,sockets,server,Python,Python 3.x,Sockets,Server,这可能是一个非常简单的套接字消息交换编程,但由于我对套接字和其他方面的想法不充分,我无法使它真正正常工作,所以我寻求帮助 场景:两个客户端发送消息。第一个发送Halo和Seeya在第一个发送这些消息之后,第二个发送Hello和Bye(此客户端只需睡眠6秒即可保持此顺序)。对于所有消息,服务器会回复(原始消息),客户端(编号)和回复消息将广播到两个客户端 因此,理想情况下,两个客户端上的结果如下所示: Halo, client 1! Seeya, client 1! He
Halo
和Seeya
在第一个发送这些消息之后,第二个发送Hello
和Bye
(此客户端只需睡眠6秒即可保持此顺序)。对于所有消息,服务器会回复(原始消息),客户端(编号)代码>和回复消息将广播到两个
客户端
因此,理想情况下,两个客户端上的结果如下所示:
Halo, client 1!
Seeya, client 1!
Hello, client 2!
Bye, client 2 !
我无法为每个客户编号,但这是我的代码,工作起来很奇怪
服务器
客户1
client2-第一次连接,但等待消息顺序6秒
我得到的结果是
# 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
这里有几个问题。第一个也是最主要的一个问题是,服务器的主循环出错了。每次通过循环时,服务器都希望
接受连接。因此,第一个连接的客户机被接受,并立即收到其第一条消息。但另一个客户端尚未被接受,因此不会收到第一条消息。然后,第二个客户端连接被接受,它的第一条消息被发送到两个客户端,但随后循环再次迭代,在第三个客户端连接之前,不会从服务器发送更多消息。等等
因此,您需要将接受连接和接收消息分开。这可以通过几种方式实现。最直接的方法是使用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”,然后在您(客户机)执行recv
之前发送“Hello”,那么很可能所有数据都会一次性返回;那是“你好”
因此,通常需要在数据中放置某种分隔符(如换行符[\n
]——但随后需要解析接收到的数据),或者最好在每条消息前面放置一个固定长度字段,给出后续可变长度部分的长度,这样您一次只能接收和处理一条消息。(在python中,这通常涉及使用struct
模块的pack
和unpack
函数。)因此,您当前的客户端代码可能无法按照您的意愿对消息进行正确排序
此外,虽然不太可能引起问题,send
:您不应该假设send(N)
只发送N
字节。它可能发送1、2、3或N-1
字节。您可以使用sendall
确保发送所有字节。大量缺少的代码。提供一个复制问题的方法。
### 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
# 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)