向所有客户发送消息

时间:2018-11-27 15:33:48

标签: python python-3.x sockets server

这可能是非常简单的套接字消息交换编程,但是由于我对套接字和所有套接字的脆弱想法,我无法真正使其正常工作,因此我正在寻求帮助。

方案:两个客户端发送消息。第一个发送HaloSeeya 之后,第一个发送了这些消息,第二个发送了HelloBye(此客户端将仅睡眠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())

client1

    ### 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()

client2-首先连接,但等待6秒消息顺序

    ### 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

1 个答案:

答案 0 :(得分:2)

您在这里遇到了几个问题。第一个也是主要的一个是服务器的主循环混乱了。每次循环时,您的服务器都希望accept建立连接。因此,第一个连接的客户端将被接受,并立即收到其第一条消息。但是其他客户端尚未被接受,因此不会收到第一条消息。然后,第二个客户端连接被接受,并且第一条消息被发送到两个客户端,但是随后循环再次迭代,直到第三个​​客户端

不再从服务器发送消息。 em>连接。等等

因此,您需要将接受连接和接收消息分开。这可以通过几种方式来完成。最简单的方法是使用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模块的packunpack函数。)因此,当前的客户端代码可能无法按您期望的顺序正确排列消息。

而且-尽管不太可能引起问题-send也是这样:您不应该假设send(N)发送的是N个字节。它可能发送1、2、3或N-1字节。您可以使用sendall确保发送所有字节。