解决Python服务器应用程序代码中挂起的UDP套接字的问题

时间:2019-05-20 11:48:58

标签: python sockets network-programming udp

我正在编写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,则在等待主线程中的数据时我将不得不处理许多异常,并且我不想为了正常的程序操作而不得不依赖于异常

1 个答案:

答案 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(数据,地址)使用现有连接,而不是启动新连接。