目录
前言
总所周知,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]);
}
}