Как эффективно использовать `select.select`, когда клиент и сервер находятся внутри одного файла для отправки и получения с эхо-сервера?

#python #sockets #networking #udp

#python #сокеты #сеть #udp

Вопрос:

Предположим, у нас есть эхо-сервер и приложение для передачи файлов.

  • Существует отправитель ( Client ) для отправки файлов и получатель ( Server ) для получения файлов. Эхо-сервер будет отображать все, что получено от Client и Server .
  • Однако, Client и Server не может обмениваться данными напрямую, т.Е. Все пакеты должны проходить через эхо-сервер. Например, Client отправляет UDP-пакет на эхо-сервер, а эхо-сервер передает этот пакет на Server эхо-сервер и Server отправляет подтверждение на эхо-сервер, а эхо-сервер передает этот пакет подтверждения на Client .
  • Цель состоит в том, чтобы реализовать надежный UDP для передачи файлов. И у нас есть только один сокет UDP.

На этом рисунке показано, что такое настройка клиента, сервера и эхо-сервера

Я пытался использовать многопоточность, select.select и оба они работают не идеально

  • Проблема с многопоточностью заключается в том, что, поскольку Client and Server не может взаимодействовать внутри, и у нас есть только один сокет, трудно выбрать, кто должен отправлять или получать сейчас.
  • Проблема в select.select том, что возвращаемый список всегда writeable непустой, из-за чего клиент продолжает отправлять кучу пакетов до readable готовности.

Вот реализация для обоих Client и Server внутри одного файла (скажем transceiver.py ), что я не использую select.select (вместо этого используя send переменную bool), но, похоже, работает нормально. Но я считаю, что это плохая практика, поэтому мне интересно, что я могу сделать, чтобы улучшить свой дизайн.

 def create_transceiver(ip, port):
    address = (ip, port)
    udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    udp.settimeout(1)
    inout = [udp]
    client = Client(udp, address)
    server = Server(udp, address)
    client_to_server = True
    send = True
    while True:
        # infds, outfds, errfds = select.select(inout, inout, [])
        if not send: # len(infds) != 0
            if client_to_server:
                server.start_recv()
                client_to_server = False
            else:
                client.start_recv()
                client_to_server = True
            send = True
        elif send: # len(outfds) != 0
            if client_to_server:
                if client.has_ack_all():
                    print(server.write_content())
                    break
                client.start_send()
                client_to_server = True
            else:
                server.start_send()
                client_to_server = False
            send = False
  

Вот реализация эхо-сервера:

 import socket
ip = "10.10.1.100"
port = 8888
address = (ip, port)

udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp_socket.bind(address)

while True:
   data, address = udp_socket.recvfrom(2048)
   udp_socket.sendto(data, address)
  

Ответ №1:

И у нас есть только один сокет UDP.

Насколько я понимаю эту цель, у вас где-то есть серверный процесс (вы называете его эхо-сервером), который прослушивает определенный порт. Также есть клиент, который хочет отправить данные на какой-то сервер.

Предоставленный вами код показывает реализацию так называемого конечного автомата, который (в вашем случае) переключается между получением и отправкой.

Однако клиент и сервер не могут взаимодействовать напрямую

Сценарий, который вы описываете, делает ваш эхо-сервер классическим сервером, который обрабатывает разные типы клиентов. В вашем случае это будет ваш «клиент» и ваш «сервер». Я хотел бы назвать их просто client-A и client-B. Я думаю, большинство руководств в Интернете назвали бы их Алисой и Бобом.

Цель состоит в том, чтобы реализовать надежный UDP для передачи файлов.

Итак, вы хотите передавать файлы между разными клиентами, используя UDP в качестве базового протокола. UDP не очень хорошо подходит для этой цели. Это не гарантирует доставку каждого переданного пакета. Возможно, что пакеты поступают в другом порядке, чем они были отправлены. Обычно вы используете TCP для такого рода передачи. UDP обычно используется для потоковой передачи данных в реальном времени, таких как аудио / видео звонки и тому подобное. Для получения дополнительной информации о различиях между UDP и TCP вы можете проверить страницы Википедии для каждого:

https://en.wikipedia.org/wiki/User_Datagram_Protocol

https://en.wikipedia.org/wiki/Transmission_Control_Protocol

Для ваших передач можно использовать UDP, но вам придется самостоятельно реализовать все меры безопасности, предоставляемые TCP.

Я предполагаю, что ваш клиент и ваш сервер на самом деле разные программы. В противном случае был бы способ, которым они могли бы общаться напрямую. Если это так, этот учебник может дать вам отправную точку: https://pythonprogramming.net/server-chatroom-sockets-tutorial-python-3 /

Комментарии:

1. Спасибо за ваш ответ! Ваше понимание действительно правильное. Я пытаюсь изучить TCP и думаю, что реализовать управление потоком TCP, контроль ошибок через UDP был бы полезен. В любом случае, считаете ли вы, что текущий конечный автомат является хорошим дизайном? Обычно я видел, как люди либо используют select.select , либо многопоточные, конечные машины кажутся немного наивными.

2. Кроме того, клиент A и клиент B существуют в одном файле, transceiver.py . Проблема для меня заключается в том, что, поскольку клиент A и клиент B существуют в одном файле и совместно используют сокет с эхо-сервером, я тогда не знаю, как переключить сокет на правильный клиент.