我正在编写UDP服务器应用程序,该应用程序充当Teltonika FMB630车载设备的后端。
我已经处理了协议的细节和解码,我面临的问题与所使用的UDP套接字有关。
我的UDP服务器必须在收到消息(即协议)后向客户端设备发送确认,但是,如果我发送这些ACK,则服务器套接字会在一段时间后停止接收数据。
服务器的UDP套接字对象被传递到concurrent.futures.ThreadPoolExecutor
,后者触发发送发送ACK的函数(send_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
,则在等待主线程中的数据时我将不得不处理许多异常,并且我不想为了正常的程序操作而不得不依赖于异常
答案 0 :(得分:0)
罪魁祸首是socket.connect()
中的send_ack
调用,当在服务器的套接字对象上被调用时,它将导致套接字不再被绑定,并监听程序开头指定的端口。
相反,send_ack
函数被更改为:
def send_ack(sock, addr, ack):
print("Sending ACK to {}".format(addr))
sock.sendto(ack, addr)
print("ACK sent to {}".format(addr))
socket.sendto(数据,地址)使用现有连接,而不是启动新连接。