Я пишу серверное приложение UDP, которое служит бэкендом для автомобильных устройств Teltonika FMB630.
Я уже позаботился о специфике и декодировании протокола, проблема, с которой я сталкиваюсь, связана с используемым сокетом UDP.
Мой UDP-сервер должен отправить подтверждение клиентскому устройству после получения сообщения (это протокол), однако, если я отправлю эти ACK, сокет сервера через некоторое время прекратит прием данных.
Объект сокета UDP сервера передается в concurrent.futures.ThreadPoolExecutor
, который запускает функцию (send_ack
), которая отправляет ACK, однако это не проблема, потому что я попытался вызвать send_ack
в главном потоке после получения данных и возникает та же проблема.
Я подозреваю, что проблема в том, что удаленное устройство каким-то образом разрывает соединение, или ISP или MNO не маршрутизируют ответный пакет (это устройство GPRS), а затем метод socket.send()
, который используется для отправки подтверждения, как-то останавливает другие операции с сокетами, в частности recvfrom_into
, вызываемые в цикле основного потока.
Я написал два сценария, чтобы проиллюстрировать ситуацию:
udp_test_echo.py:
#!/usr/env/bin python
import socket
import concurrent.futures
def send_ack(sock, addr, ack):
print("Sending ACK to {}".format(addr))
sock.connect(addr)
print("connected to {}".format(addr))
sock.send(ack)
print("ACK sent to {}".format(addr))
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("127.0.0.1", 1337))
data = bytearray([0] * 10)
executor = concurrent.futures.ThreadPoolExecutor(max_workers=4)
while True:
print("listening")
nbytes, address = s.recvfrom_into(data)
print("Socket Data received {} bytes Address {}".format(nbytes, address))
print("Data received: ", data, " Echoing back to client")
executor.submit(send_ack, s, address, data[:nbytes])
udp_test_client.py:
#!/usr/env/bin python
import socket
import time
import random
def get_random_bytes():
return bytearray([random.randint(0,255) for b in range(10)])
ip = "127.0.0.1"
port = 1337
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect((ip, port))
while True:
stuff_to_send = get_random_bytes()
print("Sending stuff", stuff_to_send)
s.sendall(stuff_to_send)
print("reply: ", s.recvfrom(10))
time.sleep(0.1)
Запустив udp_test_echo.py в одном терминале и udp_test_client.py в другом, мы увидим нормальную работу, но если вы нажмете Ctrl + C на тестовом клиенте и повторно запустите его, вы увидите, что сервер не отвечает, пока он не будет перезапущен.
Есть ли способ для тайм-аута определенной операции отправки от определенного вызова к socket.send()
методу без влияния на другие вызовы? (Я хочу мой socket.recvfrom_into
вызов для блокировки в главном потоке)
Если я settimeout
для всего объекта сокета, мне придется иметь дело со многими исключениями при ожидании данных в основном потоке, и мне не нравится полагаться на исключения для правильной работы программы.