全部博文(153)
分类: 系统运维
2010-01-15 11:26:10
UDP层:
看了这么久的网络协议代码,发现在这几层的处理大同小异,主要就是对数据包协议处理,一层层剥离、判断,分析,辨别,因为即使到了udp层依旧是不可靠的无连接的交付服务,所以在udp这一层的代码还是相对比较简单的。先说一下udp层的几个特点:
1. ip层其实是处理了网络中一台主机到另外一台主机之间的通信,而udp则更深入一步,它通过利用端口的概念处理了一个应用程序到另外一个应用程序之间的通信,因此udp的通信需要两个参数:目的ip地址,协议端口号。
2. 由于ip协议的checksum只是对ip数据首部,并不对ip数据区的,所以在udp部分还需要一次校验和。而udp校验和的计算部分除了udp数据报本身还有ip地址,ip协议等(因为udp首部仅仅指明了端口号,但是要正确验证一个udp通信,还需要知道ip地址),因此引入了udp伪首部的概念。也正是因为伪首部的校验的原因,会发现在lwip对数据帧头部移动的处理对于udp这一层有特殊处理,在之前的几层都是在进入下一层之前已经把本层的数据首部去除,而在ip层并没有移动ip的首部指针,而是到了udp层再动的(tcp层也应该如此),因为udp(tcp)层还需要ip首部的一些信息。
3. 对于checksum范围的不同,产生了一个udp的变异udp_lite,具体可以参见之前转载的一篇文章中http://blog.chinaunix.net/u3/99423/showart_2148628.html,主要的目的就是在一些网络差错率比较大,但是应用对轻微差错不敏感的应用中,典型的就是在线视频。毕竟因为一位错误就丢失整个udp数据包对于这种应用来说代价太大,也没有意义。Udp_lite可以指定校验多少范围,但是至少超过8个字节(也就是udp首部是一定要被checksum的),长度为0时表明校验整个udp数据包。8>length>0认为是一个非法值。
4. udp对于ip层多了端口的概念,对于一个应用一定有具有一个端口号,tcpip协议中使用了一种叫混合端口分配的方法(兼具了统一分配和动态绑定),即把低端口号分配给一些通用的协议和应用,把比较大的整数留给动态分配。因此在lwip中除了在其他层中常见的input,output函数外,还有一个bind函数。
Udp_input:
不想再啰嗦说一大堆废话了,与之前不同的是,这里有一个udp_pcb的会话链表,每一个UDP话路(session)的状态都被保留在一个PCB 结构中,如图7所示 。UDP PCBs 保存在一个链表中,当UDP datagram 到达,则搜索该链表并进行匹配。
UDP PCB 结构中包含一个指向全局UDP PCB链表中的下一个 PCB的指针。UDP话路(session)由IP地址和端口号来定义,并且被存放在local_ip, dest_ip,local_port,dest_port域中。 Flags域指出这一话路(session)将使用什么样的UDP 校验和策略。这可能既没关掉UDP checksumming 完全,或者使用UDP 轻便在哪个检验数字盖住只数据报的部分。
在每一个数据包进入到udp层,需要搜索这个链表,通过ip地址和端口号得到匹配结果,也就知道这个udp数据包是属于哪个应用(pcb)的。然后再调用这个pcb所对应的recv函数:pcb->recv(pcb->recv_arg, pcb, p, &iphdr->src, src);这里需要注意的是,udp已经是数据包的顶层了,它无法再把这个包递交给上层协议了,只能递交给应用程序。
Udp_output:
主要的实现函数是udp_sendto_if。当中有几段相对比较重要:
1. 分配空间:
/* 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\n"));
return ERR_MEM;
}
/* chain header q in front of given pbuf p */
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\n", (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\n", (void *)p));
}
接着还是填充udp数据首部,然后调用ip层的output函数ip_output_if