这可能是非常简单的套接字消息交换编程,但是由于我对套接字和所有套接字的脆弱想法,我无法真正使其正常工作,因此我正在寻求帮助。
方案:两个客户端发送消息。第一个发送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
答案 0 :(得分:2)
您在这里遇到了几个问题。第一个也是主要的一个是服务器的主循环混乱了。每次循环时,您的服务器都希望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”,然后在您(客户端)完成recv
之前发送“ Hello”,则很可能 all 将返回数据一口气那就是“ HaloHello”。
因此,通常,您将需要在数据中放入某种分隔符(例如换行符[{\n
],但是您需要解析接收到的数据),或者更好的是,放置一个固定的每个消息前面的-length字段,给出后续可变长度部分的长度,以便您一次可以接收和处理一条消息。 (在python中,这通常涉及使用struct
模块的pack
和unpack
函数。)因此,当前的客户端代码可能无法按您期望的顺序正确排列消息。
而且-尽管不太可能引起问题-send
也是这样:您不应该假设send(N)
发送的是N
个字节。它可能发送1、2、3或N-1
字节。您可以使用sendall
确保发送所有字节。