Использование пользовательской функции восстановления сокета работает только в том случае, если поток переведен в спящий режим

#python #sockets

#python #сокеты

Вопрос:

В моей локальной сети прослушивается следующий сокет:

 def recvall(sock):
    BUFF_SIZE = 4096 # 4 KiB
    fragments = []
    while True: 
        chunk = sock.recv(BUFF_SIZE)
        fragments.append(chunk)
        # if the following line is removed, data is omitted
        time.sleep(0.005)
        if len(chunk) < BUFF_SIZE: 
            break
        
    data = b''.join(fragments)
    return data

def main():

    pcd = o3d.geometry.PointCloud()

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(('192.168.0.22', 2525))
    print("starting listening...")
    s.listen(1)
    counter = 0
    while True:
        clientsocket, address = s.accept()
        print(f"Connection from {address} has been established!")
        received_data = recvall(clientsocket)
        clientsocket.send(bytes(f"response nr {counter}!", "utf-8"))
        counter  = 1
        print(len(received_data))

if __name__ == "__main__":
    main()
  

На этот порт я отправляю байтовые данные длиной 172800 в байты из приложения на моем мобильном телефоне.
Как можно видеть, я печатаю объем полученных данных. Количество будет правильным, только если я использую time.sleep() , как показано в приведенном выше коде. Если я не использую этот метод, принимается только часть данных.

Очевидно, что это некоторая проблема с синхронизацией, вопрос в следующем: как я могу быть уверен, что все данные будут постоянно получать без использования time.sleep() (поскольку это также не на 100% точно работает, в зависимости от установленного времени ожидания)

Ответ №1:

sock.recv() возвращает доступные данные. Соответствующая часть справочной страницы recv(2) :

 The receive calls normally return any data available, up to the requested amount,
rather than waiting for receipt of the full amount requested.
  

В вашем случае, time.sleep(0.005) похоже, все оставшиеся данные сообщения поступают и сохраняются в буфере.

Есть несколько вариантов, которые устраняют необходимость time.sleep(0.005) . Какой из них является наиболее подходящим, зависит от ваших потребностей.

  1. Если отправитель отправляет данные, но не ожидает ответа, вы можете попросить отправителя закрыть сокет после отправки данных, т. Е. sock.close() После sock.sendall() . recv() вернет пустую строку, которую можно использовать для выхода из while цикла на приемнике.
 def recvall(sock):
    BUFF_SIZE = 4096
    fragments = []
    while True: 
        chunk = sock.recv(BUFF_SIZE)
        if not chunk:
            break
        fragments.append(chunk)
    return b''.join(fragments)
  
  1. Если отправитель отправляет сообщения фиксированной длины, например, 172800 байты, вы можете использовать recv() в цикле, пока получатель не получит все сообщение.
 def recvall(sock, length=172800):
    fragments = []
    while length:
        chunk = sock.recv(length)
        if not chunk:
            raise EOFError('socket closed')
        length -= len(chunk)
        fragments.append(chunk)
    return b''.join(fragments)
  
  1. Другие варианты включают в себя: a. добавление разделителя, например, специального символа, который не может быть частью данных, в конце сообщений, которые отправляет отправитель; затем получатель может работать recv() в цикле, пока не обнаружит разделитель и b. добавление к сообщениям отправителя префикса их длины;затем получатель будет знать, сколько байтов ожидать для каждого сообщения.