Python UDP客户端单次创建套接字收发数据报错咨询
UDP单套接字收发问题说明
我在编写支持同时收发数据的UDP客户端时,目前只能通过每次收发时重复创建、关闭套接字实现功能,该实现方式不合理,理想状态应该是进入循环前创建一次套接字,循环退出后再统一关闭。
测试过程中发现:注释接收相关代码时,仅在for循环前创建一次套接字、循环结束后关闭,发送功能可正常运行;恢复接收代码后,根据接收代码放置位置不同,程序要么卡在s.recvfrom()调用处,要么卡在s.bind()调用处。
可正常运行的重复创建套接字版本代码
import socket import time import struct TX_UDP_IP = '172.24.15.34' #IP address of ETH SFP TX_UDP_PORT = 4575 #TX Packet Header TX_PACKETVERSION = 1 TX_P8ADDRESSOFFSET = 0 TX_SEQUENCENUM = 0 RX_UDP_IP = '172.24.2.7' #IP address of local machine RX_UDP_PORT = 4575 BUFFER_SIZE = 1024 print("Entered Loop") start = time.perf_counter() for i_python in range(1,11): f_python = float(i_python) + 0.15 send_data_str = struct.pack('>iiiid', TX_PACKETVERSION, TX_P8ADDRESSOFFSET, TX_SEQUENCENUM, i_python, f_python) s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.sendto(send_data_str, (TX_UDP_IP, TX_UDP_PORT)) s.close() TX_SEQUENCENUM = TX_SEQUENCENUM + 1 if TX_SEQUENCENUM > 1000: TX_SEQUENCENUM = 0 print("Transmitted i_python = ",i_python," and f_python = ",f_python) time.sleep(1.0) s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.bind((RX_UDP_IP, RX_UDP_PORT)) recv_data_str = s.recvfrom(BUFFER_SIZE) s.close() recv_data_str_unpacked = struct.unpack(">iiiid",recv_data_str[0]) RX_PACKETVERSION = recv_data_str_unpacked[0] RX_P8ADDRESSOFFSET = recv_data_str_unpacked[1] RX_SEQUENCENUM = recv_data_str_unpacked[2] j_RTDS = recv_data_str_unpacked[3] t_RTDS = recv_data_str_unpacked[4] print("Recieved j_RTDS = ",j_RTDS," and t_RTDS = ",t_RTDS) print ("Exited Loop") finish = time.perf_counter() - start print ('The execution time is %fs.' %finish)
2021-07-26补充内容
报错版本代码
import socket import time import struct TX_UDP_IP = '172.24.15.34' #IP address of ETH SFP TX_UDP_PORT = 4575 #TX Packet Header TX_PACKETVERSION = 1 TX_P8ADDRESSOFFSET = 0 TX_SEQUENCENUM = 0 RX_UDP_IP = '172.24.2.7' #IP address of local machine RX_UDP_PORT = 4575 BUFFER_SIZE = 1024 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) print("Entered Loop") start = time.perf_counter() for i_python in range(1,11): f_python = float(i_python) + 0.15 send_data_str = struct.pack('>iiiid', TX_PACKETVERSION, TX_P8ADDRESSOFFSET, TX_SEQUENCENUM, i_python, f_python) s.sendto(send_data_str, (TX_UDP_IP, TX_UDP_PORT)) TX_SEQUENCENUM = TX_SEQUENCENUM + 1 if TX_SEQUENCENUM > 1000: TX_SEQUENCENUM = 0 print("Transmitted i_python = ",i_python," and f_python = ",f_python) time.sleep(1.0) s.bind((RX_UDP_IP, RX_UDP_PORT)) recv_data_str = s.recvfrom(BUFFER_SIZE) recv_data_str_unpacked = struct.unpack(">iiiid",recv_data_str[0]) RX_PACKETVERSION = recv_data_str_unpacked[0] RX_P8ADDRESSOFFSET = recv_data_str_unpacked[1] RX_SEQUENCENUM = recv_data_str_unpacked[2] j_RTDS = recv_data_str_unpacked[3] t_RTDS = recv_data_str_unpacked[4] print("Recieved j_RTDS = ",j_RTDS," and t_RTDS = ",t_RTDS) print ("Exited Loop") s.close() finish = time.perf_counter() - start print ('The execution time is %fs.' %finish)
报错信息
Exception has occurred: OSError [WinError 10022] An invalid argument was supplied File "C:\UDP_SKT_Python_R06.py", line 51, in <module> s.bind((RX_UDP_IP, RX_UDP_PORT))
问题原因与解决方法
错误原因
- 套接字的
bind()操作仅允许执行一次,你将s.bind()放在for循环内部,第一次循环绑定完成后,第二次循环重复调用就会触发无效参数错误。 - 绑定完成的UDP套接字同时支持发送和接收操作,不需要为收发分别创建套接字。
- 默认
s.recvfrom()为阻塞调用,如果对端没有返回数据会一直卡住,可根据需求设置超时时间避免永久阻塞。
修正后的代码
import socket import time import struct TX_UDP_IP = '172.24.15.34' # ETH SFP的IP地址 TX_UDP_PORT = 4575 # 发送数据包头部 TX_PACKETVERSION = 1 TX_P8ADDRESSOFFSET = 0 TX_SEQUENCENUM = 0 RX_UDP_IP = '172.24.2.7' # 本地机器的IP地址 RX_UDP_PORT = 4575 BUFFER_SIZE = 1024 # 创建套接字后直接绑定,仅执行一次 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.bind((RX_UDP_IP, RX_UDP_PORT)) # 可选配置:设置接收超时,单位为秒,超时后会抛出socket.timeout异常 s.settimeout(5.0) print("进入循环") start = time.perf_counter() for i_python in range(1,11): f_python = float(i_python) + 0.15 send_data_str = struct.pack('>iiiid', TX_PACKETVERSION, TX_P8ADDRESSOFFSET, TX_SEQUENCENUM, i_python, f_python) # 直接用已绑定的套接字发送数据 s.sendto(send_data_str, (TX_UDP_IP, TX_UDP_PORT)) TX_SEQUENCENUM = TX_SEQUENCENUM + 1 if TX_SEQUENCENUM > 1000: TX_SEQUENCENUM = 0 print("已发送i_python = ",i_python," ,f_python = ",f_python) time.sleep(1.0) # 直接用同一个套接字接收数据,不需要重复绑定 try: recv_data_str = s.recvfrom(BUFFER_SIZE) except socket.timeout: print("接收超时,跳过本次接收") continue recv_data_str_unpacked = struct.unpack(">iiiid",recv_data_str[0]) RX_PACKETVERSION = recv_data_str_unpacked[0] RX_P8ADDRESSOFFSET = recv_data_str_unpacked[1] RX_SEQUENCENUM = recv_data_str_unpacked[2] j_RTDS = recv_data_str_unpacked[3] t_RTDS = recv_data_str_unpacked[4] print("已接收j_RTDS = ",j_RTDS," ,t_RTDS = ",t_RTDS) print("退出循环") s.close() finish = time.perf_counter() - start print ('执行耗时为 %fs.' %finish)
内容的提问来源于stack exchange,提问作者user2339563
相关产品推荐
相关产品推荐

