关于冰盾 | 使用条款 | 网站地图
 
Socket UDP协议程序代码
Socket UDP协议程序代码
作者:冰盾防火墙 网站:www.bingdun.com 日期:2015-02-26
 

在网络传输层,我们除了常用的TCP是进行传输的,还有UDP协议也是应用于传输作用。那么这里我们主要极少一下socket发送UDP协议数据的程序。首先,我们应该理解如下概念:UDP是一个简单的面向数据报的传输层协议,我们先站在UDP客户端的角度来看看如何发送一个UDP数据报,以及协议栈为发送一个UDP数据报做了哪些事情。UDP数据报可以在未连接的socket上发送(使用sendto系统调用,指定目的地址),也可以在已连接的socket上发送(使用send系统调用,不用指定目的地址),下面我们分两种情况讨论。

下面是一个在未连接的socket上发送UDP数据的用户态程序示例(注:该程序的格式和风格相当不好,只是为临时测试使用。),该程序目前还只管发送,不处理接收,关于接收,我们后面再作分析:

#include<sys/types.h>
#include<sys/socket.h>
#include<sys/ioctl.h>
#include"my_inet.h"
#include<stdio.h>
#include<errno.h>
#include<arpa/inet.h>
#include<unistd.h>
intmain()
{
inti;
structsockaddr_indest;
dest.sin_family=MY_PF_INET;
dest.sin_port=htons(16000);
dest.sin_addr.s_addr=0x013010AC;//目的地址是172.16.48.1(网络字节序)
//创建UDP数据报服务的socket。
intfd=socket(MY_PF_INET,SOCK_DGRAM,MY_IPPROTO_UDP);
if(fd<0){
perror("socket:");
return-1;
}
intbwrite=sendto(fd,"abcdefg",7,0,(structsockaddr*)&dest,sizeof(dest));
if(bwrite==-1){
perror("send:");
close(fd);
return-1;
}
printf("sendto:%d\n",bwrite);
close(fd);
return0;
}

创建socket的操作跟RAW协议的差不多,只有极少区别,内核中表示套接字的结构上的操作集,协议名略有不同而已。我们重点看sendto操作所引发的内核代码执行。sendto所到达的my_inet模块的第一站是myinet_sendmsg,一般来讲,该函数只要调用UDP协议自己的udp_sendmsg即可,但在之前,它还有一样事情要完成,就是为这个socket执行绑定,这个绑定可能跟服务器端的bind系统调用有些区别。试想,如果我们用这个UDPsocket发送出去了一个数据报,但没有记录下这个UDPsocket,那等对端的回应数据报来的时候,我们就不知道哪个socket要接收这个数据报了。绑定就是记录这个UDPsocket。

myudp_hash是一个具有128项的哈希数组,每一项都是一个UDPsocket的链表,每个UDPsocket以自己的源地址端口号为哈希主键插入这个数组。源地址端口可以是用户自己指定的,也可以是由内核自动分配的。

内核自动分配的源端口号有一个范围,这个范围段似乎是由系统的内存大小决定的(具体有待进一步分析),如果内存大(似乎是有高端内存可用),范围段是32768-61000,否则就是1024-4999。udp_port_rover是一个全局变量,初始值为范围段的下限,每次新分配端口,记录下新分配的端口号,下一次再分配时,在前一次的基础上加1,然后查询对应的myudp_hash中的项,如果该项的链表不为空,则找下一项,直至遍历整个数组,如果为空,则分配成功。所以,当连续分配128个端口后(数组中的128项中,链表全不为空),这个查询必然失败,最后遍历数组完成时,得到的端口号必然是前一次分配的端口号加127,然后,端口号每次加128,再查询对应的数组项,看该端口号有没有被使用掉。

这个描述可能有点模糊,简单总结一下就是:每次分配一个端口号,先在前一次分配值的基础上以1为步进值递增,如果对应的哈希数组中的链表为空,则肯定没有被使用过,直接使用。如果遍历完整个哈希表都没有空的链表,则要查询链表中的每一项,以得到未使用的端口。

用户自己指定一个端口,则我们到对应的哈希数组中的链表查询,如果已被使用,并且不能重用,则分配端口号失败。对用户自己指定的端口,没有范围段的限制。这个一般用于服务端,而自动分配端口用于客户端。

绑定完成后,myinet_sendmsg会调用myudp_sendmsg,它与myraw_sendmsg所执行的操作相差并不多。先查询输出路由,然后添加协议首部,最后发送数据包。与raw相比,udp要在IP首部前添加一个UDP首部。以下是UDP首部的定义:

structudphdr{
__u16source;//发送端端口号。
__u16dest;//目的端端口号。
__u16len;//UDP长度。
__u16check;//UDP检验和。
};

UDP协议是一个传输层协议,与下层的网络层协议相比,它不仅需要知道数据传输的两端的主机,还需要知道是主机上的哪个进程在进行数据传输,端口号其实就是用于标识发送进程和接收进程的。UDP长度是UDP头加上UDP数据的长度(不包括IP首部)。UDP检验和覆盖UDP首部和UDP数据。

由于UDP数据报在未连接的socket上进行发送,所以每次进入myraw_sendmsg,都要进行输出路由的查询,以确定源地址和目的地址。但我们知道,路由是有缓存的,所以,并没有太多的额外开销。认为在未连接的socket上发送UDP数据报开销要大的观点并不完全正确。


 

 
最新内容:
UDP协议的面向链接问题[2015-02-26]
浅析DOS关联洪水攻击[2015-02-26]
用路由器来有效防止DoS洪水攻击[2015-02-26]
企业网对DoS攻击的防御方法[2015-02-26]
分布式反射:新一代的DDoS攻击[2015-02-26]
IPv6网络存在的攻击手段[2015-02-26]
相关内容:

合作伙伴: 黑基网 补天科技 威盾科技 站长下载 新飞金信 北京电信 ZOL应用下载
中华人民共和国增值电信业务经营许可证京ICP备14024464 公安备案号 京1081234 
版权所有©2003-2016 冰盾防火墙  www.BingDun.com 法律声明
服务热线:(010)51661195