Socket UDP协议程序代码

网络 网络管理
下文摘要:文章中,我们对UDP协议的socket的编程进行了分析。那么大家可以通过文章中的代码来进行一下参考。希望对您有用。

在网络传输层,我们除了常用的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。#p#

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数据报开销要大的观点并不完全正确。

责任编辑:佟健 来源: 互联网
相关推荐

2010-07-17 00:55:48

PHP Telnet

2011-11-09 13:59:27

代码腐烂

2013-07-29 14:28:43

JQueryJQuery实现分页分页程序代码

2011-11-03 15:44:10

程序员

2009-06-03 14:42:21

Eclipse调试调试Java程序

2009-10-26 11:04:36

VB.NET UDP协

2010-06-29 12:42:05

UDP协议Java

2010-01-22 15:09:11

VB.NET下载程序

2009-06-17 14:29:50

java程序代码

2010-07-12 10:15:47

WinSock APIUDP协议

2014-06-13 13:47:31

UDP

2009-08-24 18:06:36

源程序代码C#读取XML文件

2010-01-15 10:48:29

C++程序代码

2010-03-23 14:12:43

Python开发Win

2010-07-12 15:40:24

2010-07-08 12:58:03

UDP协议

2010-07-06 15:28:57

UDP协议基础

2010-07-12 14:41:35

UDP协议

2010-07-06 15:16:34

UDP协议

2014-01-16 13:36:17

点赞
收藏

51CTO技术栈公众号