尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

FPGA网络通信实战:从RGMII接口到UDP协议栈的完整实现

FPGA网络通信实战:从RGMII接口到UDP协议栈的完整实现 从零基础开始碰FPGA网络通信的那些事这一篇我尽量讲点实在的。先说明一下背景我是做嵌入式出身接触FPGA大概一年半之前一直在写STM32和Linux驱动对硬件描述语言的认识基本停留在“能点亮LED”的水平。这个项目是给一块我们自己画的板子做网络通信FPGA用的是Xilinx Artix-7系列PHY芯片是RTL8211接口类型是RGMII。刚开始查资料的时候说实话看什么都是懵的什么千兆MAC、DDR接口、时序约束、CRC校验每一个词都认识组合到一起就完全不知道从哪儿下手。但这篇文章想告诉你的核心结论就是FPGA做网络通信没有你想象中那么难关键是先把框架拆开一部分一部分去吃掉它。这篇博客会从整体方案设计、接口时序、MAC层处理、UDP协议栈实现、上板调试几个维度把我在实际开发中的完整思路和踩坑过程写下来给那些跟我一样从近似0基础开始、想自己做FPGA网络通信的朋友一个可参考的路径。1. 搞明白FPGA做网络通信到底在做什么1.1 先别急着写代码把数据从网口到板子的路径理清楚很多新手拿到FPGA网口项目第一反应就是去搜“FPGA UDP例程”然后复制粘贴结果工程编译不过、上板跑不通最后卡在两三个星期都出不了结果。我自己就经历过这个阶段后来发现问题的根源在于对数据路径没有全局概念。网络通信数据的流动大概是这样的外部网线进来的模拟信号经过网络变压器到PHY芯片PHY负责把模拟信号转成数字信号同时完成时钟恢复和串并转换然后通过RGMII接口把数据交给FPGA。FPGA这一侧要做的事情分为三层最底层的MAC层负责处理帧的收发时序、CRC校验、帧间隙中间层是网络协议层一般我们需要处理ARP和UDP/IP最上层才是用户应用逻辑比如你要把ADC采到的数据通过UDP发出去或者接收上位机发来的控制指令。这个路径听起来简单但每一层都有非常多的细节。比如RGMII接口上数据是在时钟的双沿采样的也就是上升沿和下降沿各采4 bit一个时钟周期就能传8 bit所以千兆模式下时钟是125MHz数据接口却只有4根线。而百兆模式下时钟被拉到25MHz数据仍然在双沿采样那数据速率就是100Mbps。很多人第一次看到RGMII时序图的时候会蒙圈其实就是这个原因——一根线上同时包含了两个bit的信息需要有一个机制把这两个bit拆分出来。1.2 MCU做网络和FPGA做网络到底差在哪儿在我拿到FPGA网口项目之前我用STM32H743做过以太网通信用的是STM32自带的MAC控制器跑的是LwIP协议栈整体开发体验其实是不错的应用层写个socket就能收发。但是FPGA完全没有现成的协议栈也没有操作系统所有的事情都要用硬件逻辑去实现这就会带来一个思维转变的问题。我自己的体会是MCU的思路是“配置外设、跑协议栈、处理中断”FPGA的思路是“设计状态机、规划数据流、控制时序”。MCU里面网络协议栈是别人写好的你只需要调用接口而FPGA里面整个MAC和IP层都是你自己写的每一个字节的收发、每一个校验的计算都由硬件电路完成。但FPGA也有MCU没法比的优点。MCU实现网络通信CPU占用率高中断频繁数据拷贝多真正做到线速收发是很困难的。FPGA不一样整个网络收发通路是纯硬件流水线数据从PHY进来之后一直到用户逻辑中间不需要CPU参与可以实现真正的线速处理和极低延迟。在需要高速数据传输的场合比如高速数据采集板上送512路ADC数据或者视频图像实时传输FPGA的方案无论是带宽还是延迟表现都远优于MCU加软件协议栈的组合。1.3 技术选型为什么我选了RGMII UDP而不是PCIe或者TCP先说接口选型。RGMII是Reduced Gigabit Media Independent Interface的缩写就是把原来GMII的8根数据线减少到4根用双沿采样来保持带宽不变。通常FPGA的GMII接口和MAC核之间还会有一个转换不过对于绝大多数板卡来说PHY芯片都支持RGMIIFPGA侧引脚数量也更紧张所以RGMII成了最常见的方案。然后是协议选型。TCP在FPGA里实现起来非常痛苦因为TCP有状态机管理、滑动窗口、重传机制、拥塞控制这些在软件里都是很常规的逻辑用硬件实现代码量和复杂度都会陡增。UDP就简单得多无连接、无状态、没有确认重传只需要拼好IP头和UDP头算好校验和就能发出去。对于数据采集、图像传输、工业控制这类应用UDP丢几个包问题不大或者说可以在应用层做简单的重传所以UDP是FPGA网络通信的主流选择。我自己实现的方案里MAC层自己写RGMII收发逻辑数据链路层实现了ARP和UDPIP层实现了基本的IPv4收发上层给用户一个简单的FIFO接口。整条链路用纯Verilog实现没有调用Xilinx的MAC硬核这样做的好处是代码完全可控方便移植也方便理解。2. 核心细节拆解RGMII接口和时钟到底怎么处理2.1 RGMII引脚定义和接口时序一图看懂收发关系RGMII接口的信号其实不多主要就是以下几根TXC发送时钟、TX_CTL发送控制、TXD[3:0]发送数据、RXC接收时钟、RX_CTL接收控制、RXD[3:0]接收数据。发送方向是从FPGA到PHY接收方向是从PHY到FPGA两边是独立时钟域。这里的重点在于TX_CTL和RX_CTL在双沿采样下承担了两个信号的功能上升沿是TX_EN发送使能下降沿是TX_ER发送错误。数据也一样TXD上升沿发送的是低4bit下降沿发送的是高4bit。所以FPGA发送侧内部必须先做一个“双沿合并”的操作在逻辑里维护一个4bit的DDR输出寄存器时钟上升沿送出低4bit时钟下降沿送出高4bit。// RGMII 发送侧双沿数据组合示例 // 将8bit数据拆成高4bit和低4bit分别在时钟上升沿和下降沿输出 reg [3:0] rgmii_td_hi; reg [3:0] rgmii_td_lo; always (posedge tx_clk) begin rgmii_td_hi tx_data[7:4]; rgmii_td_lo tx_data[3:0]; end // 使用ODDR原语分别在上升沿和下降沿输出数据 ODDR #( .DDR_CLK_EDGE(SAME_EDGE) ) oddr_txd0_inst ( .Q(rgmii_txd[0]), .C(tx_clk), .CE(1b1), .D1(rgmii_td_hi[0]), .D2(rgmii_td_lo[0]), .R(1b0), .S(1b0) );这个ODDR原语在Xilinx的7系列里是必须的因为内部逻辑默认是单沿采样如果想利用DDR输出就得靠原语把双沿数据送到IOB上。同样的道理接收侧则要用IDDR原语把双沿数据恢复成8bit总线。2.2 时钟方案125MHz和125MHz 90度偏移必须分开处理RGMII千兆模式下的TX时钟是125MHz但PHY芯片要求FPGA提供这个时钟。这里有个很多新手都会踩的坑RGMII发送时钟需要90度相移。为什么要做90度相移因为RGMII接口规定数据和时钟的对齐方式。在发送数据的时候PHY是在时钟的上升沿和下降沿采样数据但实际PCB走线会有延迟如果时钟沿刚好在数据跳变沿上采样就会不稳定。所以协议要求源同步时钟相对数据偏移90度也就是让时钟的跳变沿落在数据位的中间位置这样才能保证采样的可靠性。在Vivado里实现这个90度相移有几种方法最简单可靠的是调用MMCM或PLL生成一个相位偏移的时钟。MMCM 配置要点 - 输入时钟125MHz通常由板载晶振或PHY的CLK_OUT提供 - CLKOUT0125MHz相位偏移90度 - 时钟资源BUFG输出到发送时钟引脚要注意的是不能直接把偏移时钟接到逻辑时钟网络上因为FPGA里逻辑时钟需要经过BUFG走全局时钟网络而RGMII的TXC引脚需要的是普通的IOB输出。我是这样处理的125MHz偏移时钟先经过BUFG进入全局时钟网络然后从内部逻辑驱动一个ODDR原语ODDR的输出经过OBUF接到TXC引脚这样既保证了时钟偏移又保证了信号质量。2.3 接收侧时钟和数据的对齐以及IDELAY怎么调接收方向是PHY把RXC和RXD同时送给FPGAPHY发送数据时会同样做90度偏移所以数据相对RXC是中心对齐的。但是在实际板卡上由于PCB走线长度不同、温度变化等因素数据相对时钟的相位会漂移这时候就需要FPGA侧做动态调整。7系列FPGA在IOB里集成了IDELAYE2原语可以对输入信号进行可编程的延迟调整延迟步进大概是78ps。我调试的时候是把ILA接在RXD和RXC上然后用Vivado的Hardware Manager在线调整IDELAY的tap值观察采样到的数据是否正确。当时调的RXD[3:0]是10个tap左右RX_CTL是8个tap左右不同板子可能不同所以这部分调试经验很重要。3. MAC层协议处理的实操CRC校验、帧间隙、状态机设计3.1 发送MAC组帧、CRC、FIFO节拍一个FIFO一个状态机搞定完成RGMII接口后就要开始处理MAC层的帧格式。以太网帧的结构是前导码7字节0x55帧起始符1字节0xD5目的MAC6字节源MAC6字节长度/类型2字节数据46~1500字节FCS4字节CRC32。发送的时候FPGA内部需要维护一个发送FIFO用户逻辑把完整的一帧数据包括MAC地址和IP头写入FIFO然后MAC层按节拍读出并通过RGMII发送。这样做的好处是用户逻辑不用关心时序细节只要把数据准备好MAC层会自己处理前导码和FCS。发送状态机的大致结构是这样的IDLE状态等待FIFO非空一旦有数据就开始发送前导码PREAMBLE状态发送0x55七次发0xD5一次DATA状态从FIFO读数据一个时钟一个字节往RGMII接口送FCS状态在数据结束后发送4字节的CRC32校验结果发送完成后回到IDLE同时等待帧间隙帧间隙是指两帧之间的最小间隔以太网规定最短是96bit时间千兆模式下就是12个时钟周期。如果不满足帧间隙交换机可能丢弃你的包我一开始没注意这个问题抓包能看到连续的包被丢弃后来加了一个简单的计数器才解决。CRC32的计算是最容易写错的地方。以太网用的是CRC32多项式是0x04C11DB7初始值是0xFFFFFFFF结果要取反后按字节序发送。Verilog实现CRC32有两种方式一种是查表法每个时钟处理一个字节另一种是逐bit计算一个时钟处理一个bit。在千兆模式下逐bit肯定不行因为一个时钟就得处理8bit数据所以我用的是字节型CRC模块网上有现成代码但要注意字节序和取反逻辑。3.2 接收MAC缓存、解帧、错帧丢弃别忘了一个关键标志接收MAC比发送要麻烦一点因为你需要处理各种异常情况。我的接收模块主要做以下几件事检测前导码锁定帧头接收完整帧数据并写入FIFO同时计算CRC一帧结束后检查CRC是否正确不正确就把FIFO里的这一帧丢弃检查帧长度小于64字节的碎片帧丢弃大于1518字节的超长帧按截断处理把接收到的字节数、帧状态作为一个侧信道信息送给上层这里最容易被忽略的是“长度不对”的情况。有些PHY在链路异常时会产生短帧如果不丢弃上层协议栈会把它当成有效数据来处理很容易解析出错。我一开始就是有这个问题抓包工具里能看到CRC错误包但FPGA内部还是把它送进了FIFO导致UDP校验老是不对。接收MAC内部的FIFO我用的是Xilinx FIFO IP核配置成标准模式写侧时钟是125MHz读侧时钟用用户逻辑的时钟。要注意的是因为FIFO的数据宽度是8bit而UDP包最大可以到1500字节所以FIFO深度至少要4096我直接配了8192这样在突发接收时不会丢数据。3.3 时序约束和跨时钟域的处理经验FPGA开发的灵魂在于时序约束和跨时钟域设计网络通信模块尤其明显。RGMII接收侧的时钟RXC是PHY提供的和FPGA内部系统时钟完全异步需要做跨时钟域处理。我的做法是把RXC时钟域的数据先经过FIFO同步到系统时钟域所有跨时钟域的握手信号都用两级同步寄存器打拍确保metastability不会传递到逻辑里。发送侧相对简单因为TXC就是我们自己生成的时钟和系统时钟同源直接使用时序约束保证路径收敛就行。Vivado里我会把125MHz和125MHz偏移90度这两个时钟设置成不同时钟组避免工具在RGMII接口上做过多的时序优化导致布局布线问题。3.4 一个实际的发送FIFO写接口代码用户逻辑和MAC层之间的发送接口我设计成AXI4-Stream风格简化版大概长这样module mac_tx_user_if ( input wire clk, input wire rst_n, input wire [31:0] user_tdata, input wire [3:0] user_tkeep, input wire user_tvalid, output wire user_tready, input wire user_tlast, output reg [31:0] fifo_wr_data, output reg [3:0] fifo_wr_keep, output reg fifo_wr_en ); // 这里做数据宽度转换把32bit的AXI4-Stream转成MAC层需要的8bit字节流 // 用一个小状态机或者位宽转换FIFO来实现 endmodule因为这个接口是拿给上层逻辑用的所以一定要用简洁的握手协议不然应用工程师用起来会很痛苦。我自己是把Mac层的发送接口定义成“数据使能结束标志”三个信号不搞复杂的AXI协议让上层逻辑一看到发送使能就往里送数据。4. 网络层和传输层UDP协议栈自己写其实没有想象中复杂4.1 ARP协议板子能ping通的关键如果你只想自己发数据出去不做ARP也能跑但要真正和电脑通信、通过交换机收发ARP就绕不开。ARP的作用是把IP地址解析成MAC地址你向电脑发UDP包之前必须知道电脑网卡的MAC地址。我的做法是做一个ARP请求发送模块和一个ARP响应解析模块当FPGA需要发送数据给某个IP但不知道它的MAC时先广播发送一个ARP请求问“谁是192.168.1.100请告诉你的MAC地址”电脑收到ARP请求后会自动回复ARP应答里面包含它的MAC地址FPGA解析出MAC地址并缓存到一个寄存器里后续发送UDP包就用这个MAC作为目的地址同时FPGA还要能响应别人发来的ARP请求这样电脑也能通过ping来获取FPGA的MAC地址这里有一个细节FPGA的MAC地址和IP地址要提前固化在代码里方便调试的话也可以做成寄存器让上位机配置。我的板子固定设置成MAC地址00:11:22:33:44:55IP地址192.168.1.10子网掩码255.255.255.0方便测试。ARP协议的Verilog实现其实就是一个状态机加一些计数器。请求状态机包括发送以太网广播帧头、ARP头、填充字段然后等待应答应答解析逻辑检测到ARP包的目的IP是自己时提取源MAC和源IP存入寄存器。4.2 IP协议头封装和校验和计算UDP包外面套的是IP包IP头固定20字节头部结构如下版本4bit IPv4、首部长度4bit固定5、服务类型TOS1字节置0、总长度2字节、标识2字节、标志和片偏移2字节置0、TTL1字节一般64、协议1字节UDP是17、首部校验和2字节、源IP4字节、目的IP4字节。IP首部校验和的计算方法是把IPv4头部按16bit一组进行二进制反码求和然后对结果取反。这个计算可以放在发送端一个字节一个字节算好然后固化到包头里。接收端要做逆运算检查发现校验和错误就丢掉。很多初学者会忽略IP首部校验和因为UDP自带校验和感觉IP校验和多余。但实际上不正确的IP头校验会让很多协议栈直接丢弃你的包pc抓包工具能看到包但应用层收不到数据排查起来特别费劲。我自己就在这个问题上卡了半天后来用Wireshark一看IP头校验和标记为incorrect才发现是发送端忘算这玩意了。4.3 UDP头封装和伪头部校验和的坑UDP头只有8字节源端口2字节、目的端口2字节、UDP长度2字节包括UDP头和数据的长度、UDP校验和2字节。UDP校验和的计算方法比较特殊它除了UDP头和数据外还需要加上一个12字节的“伪头部”参与计算。伪头部的内容是源IP、目的IP、协议号17、UDP长度。这样做是为了防止IP层把包错传给其他协议。我在实现时就是用纯组合逻辑流水线做校验计算发送时把校验和字段置0然后对整个UDP包加伪头部做16bit反码求和最后把结果填回校验和字段。// UDP校验和计算伪代码逻辑 // 1. 将UDP头数据按16bit分组 // 2. 加上伪头部源IP高16bit、源IP低16bit、目的IP高16bit、 // 目的IP低16bit、协议号0x0011、UDP长度 // 3. 每组进行二进制反码累加 // 4. 对累加结果取反写入UDP校验和字段这里特别提醒一下UDP校验和是可选字段如果设置为0表示发送端没有计算校验和接收端可以选择不校验。但实际测试发现很多上位机软件对校验和为0的UDP包会有警告所以最好还是老老实实算正确。4.4 一个简单的UDP发包状态机范例我给大家展示一下我的UDP发送状态机核心部分整体逻辑不复杂主要就是把ARP解析到的MAC、IP、端口信息拼装成一个标准以太网帧localparam S_IDLE 5d0; localparam S_ARP_CHECK 5d1; localparam S_ARP_SEND 5d2; localparam S_WAIT_ARP 5d3; localparam S_ETH_PREAM 5d4; localparam S_ETH_HEAD 5d5; localparam S_IP_HEAD 5d6; localparam S_UDP_HEAD 5d7; localparam S_DATA 5d8; localparam S_FCS 5d9; always (posedge clk) begin if (!rst_n) begin state S_IDLE; end else begin case (state) S_IDLE: begin if (send_req !send_busy) begin if (arp_table_valid) state S_ETH_PREAM; else begin state S_ARP_SEND; end end end // ... 省略中间状态 S_DATA: begin // 从用户FIFO读取数据每读一字节发送一字节 if (tx_len_cnt total_len - 1) state S_FCS; end S_FCS: begin // 发送CRC32后回到IDLE同时拉高发送完成标志 state S_IDLE; end endcase end end这个状态机的核心就是“发送前的ARP检查”和“按序拼装报文”。如果ARP表里没有目标IP的MAC就先去发ARP请求等拿到应答后再继续发UDP包。整个过程对上层是透明的上层只看到“发送请求”和“发送完成”两个信号。5. 网络通信模块的上板调试与问题排查5.1 调试环境搭建ILA抓波形、Wireshark抓包、回环测试三板斧网络通信模块做完逻辑设计后真正上板调试才是最有意思的部分。我调试时用的工具组合是Vivado自带的ILA逻辑分析仪加PC上的Wireshark另外用了一块普通的交换机把FPGA板子和电脑连起来。第一步先做回环测试。我直接在FPGA内部把发送侧的FIFO接到接收侧的FIFO让数据不经过PHY直接回环用板上按键触发一次发送然后用ILA看接收FIFO里有没有数据。这一步能验证MAC层和上层逻辑的正确性。第二步做PHY回环。RTL8211的寄存器里有loopback模式可以把发送的数据在PHY内部直接环回到接收端。通过MDIO接口配置PHY的寄存器然后发送数据看接收侧能不能收到。这一步能验证RGMII接口和PHY的配置是否有问题。第三步才是真正连网线。把FPGA的IP设置成192.168.1.10电脑设置成192.168.1.100然后从电脑ping FPGA的IP。如果ping通了说明ARP和ICMP的响应逻辑没问题如果ping不同就开始在两个工具之间来回抓。ILA抓信号的时候我建议把关键信号都引出来RGMII接收的4bit数据和控制信号、MAC层的状态机状态、FIFO的读写计数、用户接口的握手信号。这样不管出问题在哪一层都能快速定位。5.2 常见问题速查表与解决方案实录问题1电脑ping不通FPGA但ILA能看到ARP请求进来了解决方案检查ARP应答逻辑。ILA里看应答包里的目的MAC是不是填对了源MAC是不是填成了自己的物理地址。我犯过的错误是在应答包里填了广播地址导致电脑丢弃了应答。问题2FPGA发UDP包Wireshark能看到包但校验和总是incorrect解决方案IP和UDP的校验和计算一定要算对IP头校验和只覆盖IP头UDP校验和要覆盖伪头部UDP头数据。有一个容易忽略的地方是UDP长度字段必须和实际长度一致不然校验和算出来永远是错的。问题3数据能收到但偶尔乱序或者丢包解决方案检查FIFO的深度和复位逻辑。如果接收侧FIFO深度不够突发数据会覆盖旧数据如果复位信号跨时钟域没有同步可能导致FIFO读写指针错乱。建议FIFO读侧和写侧的复位信号分别用各自时钟域的逻辑复位。问题4RGMII接口在高温下偶尔误码抓包看到CRC错误解决方案一是检查PCB走线的等长设计二是调整IDELAY的tap值。IDELAY需要根据温度和电压变化做动态调整如果要求高可靠性可以考虑实现一个简单的自适应校准逻辑。问题5链路能通但速率上不去只有百兆解决方案检查PHY的配置是不是工作在了百兆模式。RTL8211默认是千兆模式但上电时PHY会通过mode strap引脚来选择工作模式要检查硬件原理图上的配置电阻是否正确。问题6Vivado布局布线后时序违例特别是RGMII接口路径解决方案查看时序报告里是setup还是hold违例。如果是setup违例检查逻辑级数是否太多可以加流水线如果是hold违例检查是不是跨时钟域路径没有约束或者ODDR原语的DDR_CLK_EDGE配置不对。5.3 性能验证实测千兆线速、延迟和资源占用板子调通之后我做了个简单的性能测试。用FPGA内部产生一个计数器每计数一次就组装一个UDP包发出去目的端口是5000数据长度设定为1400字节这样整包长度接近以太网最大帧长。实测结果表明在千兆模式下FPGA发送带宽可以达到940Mbps左右几乎接近千兆以太网的线速极限。帧与帧之间的间隔是严格按照96bit的帧间隙来控制的。延迟方面从FPGA内部产生数据到电脑的Wireshark上看到包整个链路延迟在微秒量级比软件协议栈动辄上百微秒的延迟好太多。资源占用方面由于我实现的是简化的MAC加协议栈没有使用Xilinx的MAC硬核纯Verilog逻辑消耗的资源并不多。在Artix-7 35T上LUT大概用了3000个左右FF用了2000多个BRAM用了一个18K的FIFO。对于入门级FPGA芯片来说非常友好剩下的资源可以大量留给用户逻辑做数据采集和图像处理。5.4 后续可以扩展的方向完成了基础的UDP通信后整个系统的框架就搭起来了后续扩展非常方便。一是可以加入TCP/IP协议栈比如用开源的三方协议栈实现更加可靠的数据传输二是加入PCIe接口把FPGA作为一个高速数据采集卡插到主机里通过PCIe和主机通信三是加入DDR缓存解决大流量数据缓存不够的问题四是加入RGMII转GMII的接口兼容老PHY芯片。不过这些扩展方向很多都是大工程了建议先把基础UDP收发玩到滚瓜烂熟再去碰PCIe和DDR。6. 最后分享几个我认为最有价值的经验整个项目下来如果说要总结几个对新手最有帮助的具体经验我想说三点。第一点是关于调试心态的。FPGA网络通信涉及的层次很多出了问题不要一上来就怀疑硬件先确认每一层的逻辑是否正确。我的调试顺序是先用ILA确认RGMII接口有没有数据进来再用Wireshark看FPGA发的包结构对不对最后才去查协议栈的细节。这样每确认一层就能排除一半的问题。第二点是关于代码结构的。把MAC层、ARP、UDP、用户接口这几个模块严格分开用清晰的握手信号连接不要在模块内部搞各种奇怪的依赖。这样就算出了问题也能快速定位到具体模块。第三点是关于工程管理的。Vivado工程里把时序约束和引脚约束统一放在XDC文件里每个关键信号加上注释每个模块加上版本号和修改日期。网络通信项目调试周期长代码版本管理一定要做好我自己就经历过改来改去最后不记得哪个版本能用的尴尬。最后再分享一个小技巧调试UDP发送时可以先用FPGA固定往一个目的IP发特定的数据模式比如计数器的值电脑上开一个UDP接收工具或者用Python写个简单的socket脚本接收这样就能实时验证链路是否正常。Python接收代码很简单几行就行但调试效率非常高。这里我就直接把代码贴出来方便大家直接抄作业。import socket import struct # 接收UDP数据的Python脚本示例 sock socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.bind((192.168.1.100, 5000)) sock.settimeout(5) while True: try: data, addr sock.recvfrom(2048) print(fReceived {len(data)} bytes from {addr}) # 检查数据是不是计数器递增模式 first_value struct.unpack(!I, data[0:4])[0] second_value struct.unpack(!I, data[4:8])[0] if second_value first_value 1: print(Data pattern correct) else: print(fData pattern error: {first_value} - {second_value}) except socket.timeout: print(Timeout waiting for data)在调试过程中我还发现一个细节Windows防火墙默认会阻止UDP接收程序调网络通信的时候最好临时关闭防火墙或者在弹窗里允许Python通过。这个坑不分享一下真的对不起自己踩过的时间。
返回列表