单片机Lwip协议, UDP数据超过1472时,收包处理

本文详细介绍了在LWIP协议栈中处理UDP分包和重组的过程。当发送超过MTU限制的UDP数据时,IP内核会自动分包,接收端需要通过lwipudp接收函数进行数据重组。文章展示了如何开启分包选项,定义接收函数,以及如何利用pbuf结构体重组数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

前言

开启分包

收包处理

lwip udp接收函数

接收函数处理

数据组包合成


前言

总所周知,lwip协议中 UDP 报文数据受限于以太网帧的MTU1500字节,UDP数据包一次只能发1500-20-8=1472字节(IP数据报的首部为20字节,UDP数据报的首部8字节),但是我们有些应用需要发送超过1472的数据,此时tcp/ip内核会自动分包,接收端一般为单片机,其lwip协议接收也需要相应组包的处理,才能拿到完整数据。

开启分包

确保opt.h内IP_FRAG  IP_REASSEMBLY两个宏是打开的,一般默认是打开的

收包处理

lwip内核会自己将,超长的udp数据包进行拆分,发包时不用进行处理,

收包时要将拆分的数据考到自己的数组中,通过消息队列发出来,让其他线程进行处理。

lwip udp接收函数

在lwip一直中,一般都会设置一个接收函数,不再赘述,主要将udp_user_recv函数指定为IP_ADDR_ANY地址下,端口为UDP_USER_IN_PORT的接收函数。

void Net_Init( uint32_t Ipaddr, uint32_t Netmask, uint32_t Gateway, uint8_t* Mac )
{
    Netif_Config(Ipaddr,Netmask,Gateway,Mac);
	
#if (India_Project == 1)
	czUsartBuff_init();
#endif
	
#if LWIP_UDP
    //UDP Initialization
    udp_user_pcb = udp_new();
    if( udp_user_pcb != NULL )
    {
        udp_recv( udp_user_pcb, ( udp_recv_fn )udp_user_recv, ( void* )UDP_USER_IN_PORT );
        udp_bind( udp_user_pcb, IP_ADDR_ANY, UDP_USER_IN_PORT );
    }
#endif
	
#if LWIP_TCP   
    TCPServer_PCB = tcp_new_ip_type( IPADDR_TYPE_ANY );
    if( TCPServer_PCB )
    {
        tcp_bind( TCPServer_PCB, IP_ANY_TYPE, TCP_SERVER_IN_PORT );
        TCPServer_PCB = tcp_listen( TCPServer_PCB );
        tcp_accept( TCPServer_PCB, tcp_user_accept );
    }
#endif
}

接收函数处理

接收函数一般用一个结构体数组进行接收,保存受到的数据的相关信息,,数据接收一般会进行循环接收的设置,,防止出现处理不过来,覆盖掉原来数据。

#define MAX_NET_QUENE      			         10    			//网络接收包队列大小               
#define MAX_NICPKT                           1600              



typedef struct
{
	uint16_t Channel;
	uint16_t DataSize;	
	uint16_t remote_port;	
	uint16_t local_port;	
	uint32_t addr;
	struct netif *netif;
	void  *pcb;
}_CommDataNet;


/*
static _CommDataNet CDataNet;
*/
 static _CommDataNet udpDataNet[MAX_NET_QUENE];
 static _CommDataNet tcpDataNet[MEMP_NUM_TCP_PCB];

static void udp_user_recv( void* arg, struct udp_pcb* pcb, struct pbuf* p, const ip_addr_t* addr, u16_t port )
{
    static uint16_t lastuser = 0;
    if ((pcb != NULL) && ((pcb == udp_user_pcb) || (pcb == pmm_udp_user_pcb)))
    {	 
        //communicaton process  
        lastuser %= MAX_NET_QUENE;
        udpDataNet[lastuser].Channel     = (UDP_TYPE | (lastuser << 8));
        udpDataNet[lastuser].pcb         = pcb;
        udpDataNet[lastuser].remote_port = port;
        udpDataNet[lastuser].addr        = addr->addr;
        
		PutMsgToQsQ_net(p, udpDataNet[lastuser].Channel);
        lastuser = lastuser+1;
    }
    pbuf_free(p);

}

_NetDataSQ用来存储UDP数据包的相关信息,会送时,会根据这些记录的信息会送给对应的IP和端口。

数据组包合成

上位机通过UDP发送数据时,其超长的数据报文会被IP内核自动拆包,然后发出来,单片机通过LWIP协议会受到几个包,每个包会放在受到struct pbuf* Pbuf所指向的内存块链表中。该结构体的定义为

/** Main packet buffer struct */
struct pbuf {
  /** next pbuf in singly linked pbuf chain */
  struct pbuf *next;

  /** pointer to the actual data in the buffer */
  void *payload;

  /**
   * total length of this buffer and all next buffers in chain
   * belonging to the same packet.
   *
   * For non-queue packet chains this is the invariant:
   * p->tot_len == p->len + (p->next? p->next->tot_len: 0)
   */
  u16_t tot_len;

  /** length of this buffer */
  u16_t len;

  /** a bit field indicating pbuf type and allocation sources
      (see PBUF_TYPE_FLAG_*, PBUF_ALLOC_FLAG_* and PBUF_TYPE_ALLOC_SRC_MASK)
    */
  u8_t type_internal;

  /** misc flags */
  u8_t flags;

  /**
   * the reference count always equals the number of pointers
   * that refer to this pbuf. This can be pointers from an application,
   * the stack itself, or pbuf->next pointers from a chain.
   */
  LWIP_PBUF_REF_T ref;

  /** For incoming packets, this contains the input netif's index */
  u8_t if_idx;
};

我们只需要关注3个参数

 struct pbuf *next;指向下一个内存块

  u16_t tot_len;迭代之后其实是,UDP受到包的总长

  u16_t len; 当前块中,数据长度

我们只需要,通过判断pbuf *next是否存在,一个内存块一个内存块的将数据拷贝出来,就可以拿到完整数据。

#define MAX_NET_QUENE      			         10    			//网络接收包队列大小               
#define MAX_NICPKT                           1600

typedef struct
{
	UWORD         UseFlag;                                      //记录有没有记录
	UWORD         Size;
	ULONG         Channel;
	struct netif *Netif;									    //记录对应的口
	struct pbuf  *p;
	INT8U         Buf[MAX_NICPKT];								    //缓冲区
}_NetDataSQ;

extern _NetDataSQ   NetDataSQ[MAX_NET_QUENE]; 


//※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※
//※※ 函数名称:PutMsgToQsQ_net
//※※ 函数功能:把收到的数据放到队列中并发信号出去给任务,net专用
//※※ 输入参数:
//※※           p                     - 首包指针
//※※          channel                - 通道
//※※ 返 回 值:none
//※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※
void PutMsgToQsQ_net( struct pbuf* Pbuf,UWORD channel)
{
	INT32U queue;
    struct pbuf* P_next;
    INT8U  *data_buff;
	  
    queue  = CommQueue_Get();
    if(queue != GET_RECEIVE_STRUCT_ERR)
    {
	    memcpy(NetDataSQ[queue].Buf,Pbuf->payload,Pbuf->len); //先复制第一个内存块的包
		P_next = Pbuf->next;
		data_buff = NetDataSQ[queue].Buf + Pbuf->len;
		while(P_next)
		{
			if((data_buff - NetDataSQ[queue].Buf)  > MAX_NICPKT) break;
			memcpy(data_buff,P_next->payload,P_next->len);
			data_buff = data_buff + P_next->len;
			P_next = P_next->next;
		}
        NetDataSQ[queue].Size  = Pbuf->tot_len;
        NetDataSQ[queue].Channel  = channel;
        NetDataSQ[queue].UseFlag = TRUE;
        OSQPost(NetQSQ,&NetDataSQ[queue]);        
	}    
}

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

灵魂Maker

好文,该赏!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值