Отправка сообщений всем клиентам - PullRequest
0 голосов
/ 27 ноября 2018

Это, вероятно, очень простое программирование обмена сообщениями в сокете, но с моей неубедительной идеей о сокете и всем остальном я не смог заставить ее работать должным образом, поэтому я обращаюсь за помощью.

Сценарий: два клиента отправляют сообщения.Первый отправляет Halo и Seeya. После первое отправляет эти сообщения, второе отправляет Hello и Bye (этот клиент просто просидит 6 секунд, чтобы сохранить этот порядок).На все сообщения сервер отвечает (original msg), client (number)!, а ответное сообщение передается обоим клиентам.

Так что в идеале результат на обоих клиентах должен выглядеть следующим образом:

    Halo, client 1!
    Seeya, client 1!

    Hello, client 2!
    Bye, client 2 !

Я не смог сделать нумерацию каждого клиента, но вот мой код, который работает странно.

server

    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 голосов
/ 27 ноября 2018

У вас здесь несколько проблем.Первый и основной из них заключается в том, что основной цикл вашего сервера испорчен.Каждый раз в цикле ваш сервер хочет 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, то, по всей вероятности, all данные будут возвращены за один размах;это «HaloHello».

Как правило, поэтому вы захотите поместить какой-либо разделитель в данные (например, новую строку [\n] - но тогда вам нужно будет проанализировать полученные данные) или, что еще лучше, поместитьполе фиксированной длины перед каждым сообщением, задающее длину последующей части переменной длины, чтобы вы могли получать и обрабатывать ровно одно сообщение за раз.(В python это обычно включает использование функций struct модуля pack и unpack.) В результате ваш текущий клиентский код, вероятно, не будет должным образом упорядочивать сообщения, как вы хотите.

Также -хотя это менее вероятно, вызовет проблему - то же самое касается send: вы не должны предполагать, что send(N) отправляет ровно N байтов.Может отправлять 1, 2, 3 или N-1 байтов.Вы можете использовать sendall, чтобы обеспечить отправку всех байтов.

...