收藏本站 
广告服务 
网站地图 
>> 本频道近100000余篇各类电脑技术、网络技术、软件技术、网页及平面设计等方面的电脑教程,我们的原则:不是精华拒不收录!
先飞电脑技术网技术文章程序开发C/C++ 编程
网络编程 | 网站建设 | 网络技术 | 设计教程 | 软件教学 | 程序开发 | 数据库开发 | 教育认证 | 硬件维护 | 媒体动画 | 机械电子 |

在未连接的Socket上发送UDP数据报的方法

[ 作者:佚名    转贴自:网络转载    阅读次数:46    更新时间:2007-10-4 14:02:00   录入:刘光勇 ]        
    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>

int main()
{
        int i;
        struct sockaddr_in dest;
        dest.sin_family = MY_PF_INET;
        dest.sin_port = htons(16000);
        dest.sin_addr.s_addr = 0x013010AC;  //目的地址是172.16.48.1(网络字节序)

        //创建UDP数据报服务的socket。
        int fd = socket( MY_PF_INET, SOCK_DGRAM, MY_IPPROTO_UDP );
        if( fd < 0 ){
            perror("socket: ");
            return -1;
        }
        int bwrite = sendto( fd, "abcdefg", 7, 0, (struct sockaddr *)&dest, sizeof(dest) );
        if( bwrite == -1 ){
            perror("send: ");
            close(fd);
            return -1;
        }
        printf("sendto: %d\n", bwrite);
        close( fd );
        return 0;
    }

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

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

内核自动分配的源端口号有一个范围,这个范围段似乎是由系统的内存大小决定的(具体有待进一步分析),如果内存大(似乎是有高端内存可用),范围段是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首部的定义:

struct udphdr {
            __u16   source;     //发送端端口号。
            __u16   dest;       //目的端端口号。
            __u16   len;        //UDP长度。
            __u16   check;      //UDP检验和。
        };

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

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

文章首页【加入到收藏夹】告诉好友】【打印此文】【关闭窗口
  版权声明:本站提供的“在未连接的Socket上发送UDP数据报的方法”版权归文章所有者,转载请注明出处!
 ·上一篇文章:C语言中实现“点在多边形内”算法      ·下一篇文章:没有了
相关文章
·在未连接的Socket上发送UDP数据报的方法[46]
·中国海报人,希望就在未来50年[184]
·Borland网上调查:请告诉Borland你在未来的Delphi和C+[6]
·More Effective C++ Item M32:在未来时态下开发程序[8]
·IIS存在未明远程攻击漏洞[118]
网站主页 | 收藏本页 | 联系我们 | 广告服务 | 站点地图 | 会员注册 | 招聘信息 | 内容指正

联系QQ:先飞电脑技术网站事务联系QQ,点击可以直接留言. 32933427 电话:13710542091 [世界排名] 鄂ICP备05005890号 先飞电脑教程网