其实发送一个包就这么多.
/* UDP 缓冲区申请 */
udpbuf = netbuf_new();
/* 申请内存 */
netbuf_alloc(udpbuf, strlen((char *)udpdemo_buf));
/* 把数据复制到payload里去. */
memcpy(udpbuf->p->payload, (void *)udpdemo_buf, strlen((const char *)udpdemo_buf));
/* payload 其实也可以直接修改. */
((uint32_t *)udpbuf->p->payload)[0] = xTaskGetTickCount();
/* 这一步把数据发送出去. */
err = netconn_send(udpconn, udpbuf);
/* UDP总会发成功的. */
netbuf_delete(udpbuf);
其中netbuf_new跟进去,发现其实就是一个calloc.
netbuf_alloc就是申请到内存,指针指向申请到的位置.
memcpy是标准函数,不用多说.
netbuf_delete就是删除calloc到的东西.
那么其实就剩下了netconn_send,这个函数看起来应该比较复杂,因为其他地方什么都没干.进去一看,还是apimsg的安全调用.不得不说安全性十足,而我们一开始就知道,直接看fn就行,其他都是安全需要.
1396行的错误判断,我们一开始就见过.就是如果这个conn有问题,就不要往下继续了.
接下来判断PCB是有效的,再跳转到NETCONN_UDP,明显这个函数只适合UDP.LWIP_CHECKSUM_ON_COPY又刚好是打开的,那么就是要校验.而后续一个是udp_send_chksum,一个是udp_sendto_chksum.
如果是本机发,就走到上面得判断,下面的判断就是有目标端口,目标地址什么的,其实最终是调用udp_sendto_chksum的.
进去就是ip_route,因为IP_IS_ANY_TYPE_VAL恒定为0,所以没被编译进去.
实际上,包就是从ip_route这一段,数据就出去了.这就是到达了IP层,记得七层模型里面,TCP还在比较靠上的一层.
可见,ip_route是查找到源网卡,以便后续构建.接着在sendto里面再获取了本地的src_ip(发送者IP).
到了udp_sendto_if_src_chksum,里面很多判断,这个代码尤其长.
/** Same as udp_sendto_if_src(), but with checksum */
err_t
udp_sendto_if_src_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip,
u16_t dst_port, struct netif *netif, u8_t have_chksum,
u16_t chksum, const ip_addr_t *src_ip)
{
#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
struct udp_hdr *udphdr;
err_t err;
struct pbuf *q; /* q will be sent down the stack */
u8_t ip_proto;
u8_t ttl;
if ((pcb == NULL) || (dst_ip == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, src_ip) ||
!IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) {
return ERR_VAL;
}
#if LWIP_IPV4 && IP_SOF_BROADCAST
/* broadcast filter? */
if (!ip_get_option(pcb, SOF_BROADCAST) &&
#if LWIP_IPV6
IP_IS_V4(dst_ip) &&
#endif /* LWIP_IPV6 */
ip_addr_isbroadcast(dst_ip, netif)) {
LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
("udp_sendto_if: SOF_BROADCAST not enabled on pcb %p
", (void *)pcb));
return ERR_VAL;
}
#endif /* LWIP_IPV4 && IP_SOF_BROADCAST */
/* if the PCB is not yet bound to a port, bind it here */
if (pcb->local_port == 0) {
LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send: not yet bound to a port, binding now
"));
err = udp_bind(pcb, &pcb->local_ip, pcb->local_port);
if (err != ERR_OK) {
LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: forced port bind failed
"));
return err;
}
}
/* not enough space to add an UDP header to first pbuf in given p chain? */
if (pbuf_header(p, UDP_HLEN)) {
/* allocate header in a separate new pbuf */
q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM);
/* new header pbuf could not be allocated? */
if (q == NULL) {
LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: could not allocate header
"));
return ERR_MEM;
}
if (p->tot_len != 0) {
/* chain header q in front of given pbuf p (only if p contains data) */
pbuf_chain(q, p);
}
/* first pbuf q points to header pbuf */
LWIP_DEBUGF(UDP_DEBUG,
("udp_send: added header pbuf %p before given pbuf %p
", (void *)q, (void *)p));
} else {
/* adding space for header within p succeeded */
/* first pbuf q equals given pbuf */
q = p;
LWIP_DEBUGF(UDP_DEBUG, ("udp_send: added header in given pbuf %p
", (void *)p));
}
LWIP_ASSERT("check that first pbuf can hold struct udp_hdr",
(q->len >= sizeof(struct udp_hdr)));
/* q now represents the packet to be sent */
udphdr = (struct udp_hdr *)q->payload;
udphdr->src = lwip_htons(pcb->local_port);
udphdr->dest = lwip_htons(dst_port);
/* in UDP, 0 checksum means 'no checksum' */
udphdr->chksum = 0x0000;
/* Multicast Loop? */
#if (LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS) || (LWIP_IPV6 && LWIP_IPV6_MLD)
if (((pcb->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) && ip_addr_ismulticast(dst_ip)) {
q->flags |= PBUF_FLAG_MCASTLOOP;
}
#endif /* (LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS) || (LWIP_IPV6 && LWIP_IPV6_MLD) */
LWIP_DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %"U16_F"
", q->tot_len));
#if LWIP_UDPLITE
/* UDP Lite protocol? */
if (pcb->flags & UDP_FLAGS_UDPLITE) {
u16_t chklen, chklen_hdr;
LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE packet length %"U16_F"
", q->tot_len));
/* set UDP message length in UDP header */
chklen_hdr = chklen = pcb->chksum_len_tx;
if ((chklen < sizeof(struct udp_hdr)) || (chklen > q->tot_len)) {
if (chklen != 0) {
LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE pcb->chksum_len is illegal: %"U16_F"
", chklen));
}
/* For UDP-Lite, checksum length of 0 means checksum
over the complete packet. (See RFC 3828 chap. 3.1)
At least the UDP-Lite header must be covered by the
checksum, therefore, if chksum_len has an illegal
value, we generate the checksum over the complete
packet to be safe. */
chklen_hdr = 0;
chklen = q->tot_len;
}
udphdr->len = lwip_htons(chklen_hdr);
/* calculate checksum */
#if CHECKSUM_GEN_UDP
IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_UDP) {
#if LWIP_CHECKSUM_ON_COPY
if (have_chksum) {
chklen = UDP_HLEN;
}
#endif /* LWIP_CHECKSUM_ON_COPY */
udphdr->chksum = ip_chksum_pseudo_partial(q, IP_PROTO_UDPLITE,
q->tot_len, chklen, src_ip, dst_ip);
#if LWIP_CHECKSUM_ON_COPY
if (have_chksum) {
u32_t acc;
acc = udphdr->chksum + (u16_t)~(chksum);
udphdr->chksum = FOLD_U32T(acc);
}
#endif /* LWIP_CHECKSUM_ON_COPY */
/* chksum zero must become 0xffff, as zero means 'no checksum' */
if (udphdr->chksum == 0x0000) {
udphdr->chksum = 0xffff;
}
}
#endif /* CHECKSUM_GEN_UDP */
ip_proto = IP_PROTO_UDPLITE;
} else
#endif /* LWIP_UDPLITE */
{ /* UDP */
LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP packet length %"U16_F"
", q->tot_len));
udphdr->len = lwip_htons(q->tot_len);
/* calculate checksum */
#if CHECKSUM_GEN_UDP
IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_UDP) {
/* Checksum is mandatory over IPv6. */
if (IP_IS_V6(dst_ip) || (pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) {
u16_t udpchksum;
#if LWIP_CHECKSUM_ON_COPY
if (have_chksum) {
u32_t acc;
udpchksum = ip_chksum_pseudo_partial(q, IP_PROTO_UDP,
q->tot_len, UDP_HLEN, src_ip, dst_ip);
acc = udpchksum + (u16_t)~(chksum);
udpchksum = FOLD_U32T(acc);
} else
#endif /* LWIP_CHECKSUM_ON_COPY */
{
udpchksum = ip_chksum_pseudo(q, IP_PROTO_UDP, q->tot_len,
src_ip, dst_ip);
}
/* chksum zero must become 0xffff, as zero means 'no checksum' */
if (udpchksum == 0x0000) {
udpchksum = 0xffff;
}
udphdr->chksum = udpchksum;
}
}
#endif /* CHECKSUM_GEN_UDP */
ip_proto = IP_PROTO_UDP;
}
/* Determine TTL to use */
#if LWIP_MULTICAST_TX_OPTIONS
ttl = (ip_addr_ismulticast(dst_ip) ? udp_get_multicast_ttl(pcb) : pcb->ttl);
#else /* LWIP_MULTICAST_TX_OPTIONS */
ttl = pcb->ttl;
#endif /* LWIP_MULTICAST_TX_OPTIONS */
LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP checksum 0x%04"X16_F"
", udphdr->chksum));
LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,0x%02"X16_F",)
", (u16_t)ip_proto));
/* output to IP */
NETIF_SET_HWADDRHINT(netif, &(pcb->addr_hint));
err = ip_output_if_src(q, src_ip, dst_ip, ttl, pcb->tos, ip_proto, netif);
NETIF_SET_HWADDRHINT(netif, NULL);
/* @todo: must this be increased even if error occurred? */
MIB2_STATS_INC(mib2.udpoutdatagrams);
/* did we chain a separate header pbuf earlier? */
if (q != p) {
/* free the header pbuf */
pbuf_free(q);
q = NULL;
/* p is still referenced by the caller, and will live on */
}
UDP_STATS_INC(udp.xmit);
return err;
}
看起来是不是又长又头疼.稍微把一些安全性判断去掉.代码就很短了.
/** Same as udp_sendto_if_src(), but with checksum */
err_t
udp_sendto_if_src_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip,
u16_t dst_port, struct netif *netif, u8_t have_chksum,
u16_t chksum, const ip_addr_t *src_ip)
{
struct udp_hdr *udphdr;
err_t err;
struct pbuf *q; /* q will be sent down the stack */
u8_t ip_proto;
u8_t ttl;
/* pbuf_header是给添加头信息的.给p增加UDP_HLEN长的头,成功返回0,非0都是失败. */
if (pbuf_header(p, UDP_HLEN))
{
/* 申请新的pbuf */
q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM);
/* new header pbuf could not be allocated? */
if (q == NULL)
{
return ERR_MEM;
}
if (p->tot_len != 0)
{
/* 把新的pbuf放到chain里,就是q的next是p.先发q,然后q是个头,发送完q就发p,数据. */
pbuf_chain(q, p);
}
}
else
{
/* adding space for header within p succeeded */
/* 因为他是IP头,所以他在pbuf的最开始处. */
q = p;
}
/* q now represents the packet to be sent */
udphdr = (struct udp_hdr *)q->payload;
udphdr->src = lwip_htons(pcb->local_port);
udphdr->dest = lwip_htons(dst_port);
/* in UDP, 0 checksum means 'no checksum' */
udphdr->chksum = 0x0000;
/* Multicast Loop? */
if (((pcb->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) && ip_addr_ismulticast(dst_ip))
{
q->flags |= PBUF_FLAG_MCASTLOOP;
}
udphdr->len = lwip_htons(q->tot_len);
/* calculate checksum */
IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_UDP)
{
/* Checksum is mandatory over IPv6. */
if (IP_IS_V6(dst_ip) || (pcb->flags & UDP_FLAGS_NOCHKSUM) == 0)
{
u16_t udpchksum;
if (have_chksum)
{
u32_t acc;
udpchksum = ip_chksum_pseudo_partial(q, IP_PROTO_UDP,
q->tot_len, UDP_HLEN, src_ip, dst_ip);
acc = udpchksum + (u16_t)~(chksum);
udpchksum = FOLD_U32T(acc);
}
else
{
udpchksum = ip_chksum_pseudo(q, IP_PROTO_UDP, q->tot_len,
src_ip, dst_ip);
}
/* chksum zero must become 0xffff, as zero means 'no checksum' */
if (udpchksum == 0x0000)
{
udpchksum = 0xffff;
}
udphdr->chksum = udpchksum;
}
}
ip_proto = IP_PROTO_UDP;
/* Determine TTL to use */
ttl = pcb->ttl;
/* output to IP */
err = ip_output_if_src(q, src_ip, dst_ip, ttl, pcb->tos, ip_proto, netif);
/* did we chain a separate header pbuf earlier? */
if (q != p)
{
/* free the header pbuf */
pbuf_free(q);
q = NULL;
/* p is still referenced by the caller, and will live on */
}
return err;
}
当我们断点在这个位置的时候,udphdr已经写入(udp头)
查看变量看到.
注意这里是0x781E其实是0x1E78,因为是我们的CPU是LE,但是以太网是BE.正是7800端口,目标端口.
进来后,需要判断have_chksum,这个参数又是超级早传递进来的.当NETBUF_FLAG_CHKSUM是有效时候,这个参数就有效.而默认这个是没的,所以就是全校验(否则,有效就是部分校验).
我们知道有校验就行,不关心怎么校验的.然后继续跑就到ip_output_if_src,这个总是是发包了吧.(确实这样,但是这里还有很多层.).
进入后立马进入ip4_output_if_opt_src函数,又继续用同样方法,添加一个IP头大小的内容.
IP头总算是一帧的最开始,然后到iphdr = (struct ip_hdr *)p->payload就是把iphdr定义为pbuf的开始数据.关于IP头,可以看看定义.(实际上还是不对,还有个叫以太网帧的玩意).
随后960行判断帧有没有大于MTU大小,大于的话还得拆分帧.
记得这个发送其实是到etharp_output,还不是终点,是不是有点恐怖,这深度.不,这还没到,还要封装以太网帧.可见LwIP给我们干了好多啊.