UDP接收异常:Socat/NetCat/Python收不到包但Wireshark可捕获
UDP数据包捕获异常排查问题
环境与异常情况
- 设备A(Windows系统)运行Python网卡状态检测脚本,向设备B(Ubuntu 20.04系统)发送UDP数据
- 5台硬件配置完全一致的测试机器中,4台运行正常,仅1台出现异常:
- Wireshark可正常捕获UDP数据包,但
nc -lup <port>、socat - udp-l:<port>,reuseaddr,fork及Python监听脚本均无法接收数据包
- Wireshark可正常捕获UDP数据包,但
已做排查操作
- 关闭ufw防火墙
- 设置网卡广播地址为192.168.255.255
- 问题仍未解决
相关脚本代码
tsend.py(Windows端发送脚本)
import socket, psutil from time import sleep def getst(nic): stat = psutil.net_if_stats() # print(stat[nic].isup) if stat[nic].isup == True: return 'Up and ok\n' else: return 'Down, bad\n' def main(): UDP_IP = "192.168.100.10" UDP_PORT = 2275 count = 0 exstate = "bababooey" state = bytes(getst('Ethernet'), 'utf-8') while True: count += 1 exstate = state state = bytes(getst('Ethernet'), 'utf-8') if (exstate != state) or (count >= 3): count = 0 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.bind(("192.168.100.20", 0)) # print(state) sock.sendto(state, (UDP_IP, UDP_PORT)) sleep(1) sock.sendto(state, (UDP_IP, UDP_PORT)) sleep(1) sock.sendto(state, (UDP_IP, UDP_PORT)) sock.close() sleep(10) main()
trecv.py(Ubuntu端接收脚本)
from http.server import BaseHTTPRequestHandler, HTTPServer import argparse import cgi import socket import json import threading import requests import re import time from datetime import datetime import sys from urllib import parse import signal ###########----全局变量----########### ServerIP = "1.1.1.1" ServerPort = 1234 ServerThread = "" ##########----/全局变量----########### class HTTPRequestHandler(BaseHTTPRequestHandler): def do_POST(self): if re.search('/api/set/*', self.path): ctype, pdict = cgi.parse_header(self.headers.get('content-type')) if ctype == 'application/json': length = int(self.headers.get('content-length')) rfile_str = self.rfile.read(length).decode('utf8') data = parse.parse_qs(rfile_str, keep_blank_values=1) record_id = self.path.split('/')[-1] LocalData.records[record_id] = data log(f"Server: setstatus {record_id}: {data} " + datetime.now().strftime("%d/%m/%Y %H:%M:%S.%f")[:-3]) # HTTP 200: 请求成功 self.send_response(200) else: # HTTP 400: 错误请求 log("Server: empty POST; ERR400" + datetime.now().strftime("%d/%m/%Y %H:%M:%S.%f")[:-3]) self.send_response(400, "Bad Request: must give data") else: # HTTP 403: 禁止访问 self.send_response(403) self.end_headers() def do_GET(self): if re.search('/api/shutdown', self.path): # 必须在另一个线程中关闭,否则会阻塞 def kill_me_please(): self.server.shutdown() # 先返回200再关闭 self.send_response(200) threading.Thread(target=kill_me_please).start() elif re.search('/api/get/*', self.path): record_id = self.path.split('/')[-1] if record_id in LocalData.records: self.send_response(200) self.send_header('Content-Type', 'application/json') self.end_headers() # 以JSON格式返回数据,尽管POST用的是URL参数 data = json.dumps(LocalData.records[record_id]) print(f"getstatus {record_id}: {data}") self.wfile.write(data.encode('utf8')) else: self.send_response(404, 'Not Found: record does not exist') else: self.send_response(403) log("Server: GET:" + datetime.now().strftime("%d/%m/%Y %H:%M:%S.%f")[:-3]) self.end_headers() def Quits(reason="KeyboardInterrupt"): r = requests.get(f"http://{ServerIP}:{str(ServerPort)}/api/shutdown") log(f"{reason}; exiting: " + datetime.now().strftime("%d/%m/%Y %H:%M:%S.%f")[:-3]) exit(1) def log(msg="-----" + datetime.now().strftime("%d/%m/%Y %H:%M:%S.%f")[:-3] + "-----"): f = open("/var/log/TServer.log", "a") f.write(msg + '\n') f.close() def UDPListener(sock): global ServerIP, ServerPort msg = '' headers = {"Content-Type": "application/json"} url = f"http://{ServerIP}:{str(ServerPort)}/api/set/txlink" while ServerThread.is_alive(): data, addr = sock.recvfrom(96) data = data.decode("utf-8") if data == "Up and ok": msg = 'state=0' elif data == "Down, bad": msg = "state=1" r = requests.post(url, headers=headers, data=msg) print(f"updated: " + datetime.now().strftime("%d/%m/%Y %H:%M:%S.%f")[:-3]) sock.close() def main(): global ServerIP, ServerPort, ServerThread signal.signal(signal.SIGINT, signal.default_int_handler) signal.signal(signal.SIGTERM, signal.default_int_handler) parser = argparse.ArgumentParser(description='TServer Process') parser.add_argument('ip', help='HTTP服务器IP') parser.add_argument('port', type=int, help='HTTP服务器监听端口') parser.add_argument('Tx_ip', help='TX网卡IP') parser.add_argument('Tx_port', type=int, help='TX监听端口') args = parser.parse_args() sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP sock.bind((args.Tx_ip, args.Tx_port)) ServerIP = args.ip ServerPort = args.port ServerThread = threading.Thread(target=run_srv) ReceiverThread = threading.Thread(target=UDPListener, args=[sock,]) try: ServerThread.start() ReceiverThread.start() ServerThread.join() ReceiverThread.join() except KeyboardInterrupt: sock.close() Quits() except RuntimeError: sock.close() Quits("RuntimeError") except OSError: sock.close() Quits("OSError") sock.close() main()
排查建议
- 检查Ubuntu系统的
rp_filter配置:
执行sysctl -a | grep rp_filter,若值为1(严格模式),内核可能丢弃来源IP与路由表不匹配的数据包。可临时执行sysctl -w net.ipv4.conf.all.rp_filter=0和sysctl -w net.ipv4.conf.<网卡名>.rp_filter=0测试,永久修改需编辑/etc/sysctl.conf。 - 检查网卡多播/广播配置:
执行ip link show <网卡名>确认是否开启多播功能;同时检查net.ipv4.conf.all.broadcast_igmp等相关内核参数。 - 检查进程绑定地址:
用netstat -ulpn查看UDP端口的绑定情况,确认监听脚本是否绑定了正确的网卡IP(而非127.0.0.1)。 - 检查内核数据包过滤规则:
即使关闭ufw,仍可能存在iptables/nftables规则残留,执行iptables -L -n和nft list ruleset检查是否有丢弃UDP数据包的规则。 - 检查网卡驱动:
因硬件一致仅单台异常,可能是网卡驱动版本或状态问题,尝试重新加载网卡驱动或更新驱动版本测试。
内容的提问来源于stack exchange,提问作者Roman M.
相关产品推荐
相关产品推荐

