Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1333826
  • 博文数量: 284
  • 博客积分: 3251
  • 博客等级: 中校
  • 技术积分: 3046
  • 用 户 组: 普通用户
  • 注册时间: 2012-04-26 17:23
文章分类

全部博文(284)

文章存档

2019年(2)

2018年(5)

2015年(19)

2014年(13)

2013年(10)

2012年(235)

分类: LINUX

2012-12-21 16:45:07

 前面一篇文章中提到的两个示例程序,它们虽然对外发送了组播数据报,但它们实际上调用的是协议栈中的单播发送的代码。一般情况下,它们不会有什么问题,但是它们不是标准的组播程序,下面我们看看协议栈究竟是如何发送组播数据报的。
    我们还是以发送UDP的组播数据报为例。前面已经讲过,IP选项IP_MULTICAST_IF确定组播发送的接口,在通过系统调用设置该选项时,参数只 需要一个本地网络接口的IP地址即可,myudp_sendmsg函数在发送组播数据报时,会以该选项设定的IP地址作为输出路由查询的源地址。
    对于一个输出组播数据报,协议栈也要做检查,检查该组播发送的接口是否也加入了同一个组播组(即检查 net_device->in_device->mc_list链表,查看是否存在跟输出组播数据报目的地址相同的组),如果检查结果确实加 入了同一个组(本机可能有其它进程在同一网络设备口上,在该组中接收数据报),则把组播输出函数指定为myip_mc_output,该函数与普通的IP 数据报输出函数相比,多了一个判断,如果启用了组播环路,则先向loopback接口发送一个组播数据报,确保本机需要接收该组中的数据的进程能收到数 据。组播环路缺省是打开的,可以通过IP选项IP_MULTICAST_LOOP进行设置。
    组播数据报的TTL的缺省值是1,这在很多情况下,显然是不适用的,我们必须能够修改它,IP选项IP_MULTICAST_TTL可用来修改这个TTL 值,该选项把它的参数值赋给套接字结构体的成员mc_ttl。协议栈在为待发送数据报构建IP首部时,发现该数据的目的地址是一个组播地址时,就会把 mc_ttl的值填入IP首部的ttl域。
    下面是一个组播客户端和一个组播服务端程序,让它们运行在同一台主机上,试着修改一些参数,你就能得到各种不同的行为。我在调试中发现一个问题,就是现有 的Linux TCP/IP协议栈代码在往组播环路发数据报时,本机接收进程会收到两个数据包,也就是本示例中,服务端开启了环路以后,发一个数据报,客户端会收到两 个,很有趣的问题,暂时没想通,欢迎大家探讨。
   服务端:
    #include
    #include
    #include
    #include
    #include
    #include "my_inet.h"

    #define MAXBUF 256
    #define PUERTO 5000
    #define GROUP "224.0.1.1"
    int main(void)
    {
        int fd, mc_loop = 1;
        struct sockaddr_in srv,local;
        struct in_addr if_req;
        char buf[MAXBUF];

        srv.sin_family = MY_AF_INET;
        srv.sin_port = htons(PUERTO);
        inet_aton(GROUP, &srv.sin_addr);

        if( (fd = socket( MY_AF_INET, SOCK_DGRAM, MY_IPPROTO_UDP) ) < 0 ){
            perror("socket");
            return -1;
        }
        inet_aton("172.16.48.2", &(if_req) );
        if( setsockopt( fd, SOL_IP, IP_MULTICAST_IF, &if_req, sizeof(struct in_addr) ) < 0 ){
            perror("setsockopt:");
            return -1;
        }
        if( setsockopt( fd, SOL_IP, IP_MULTICAST_LOOP, &mc_loop, sizeof(int) ) < 0 ){
            perror("setsockopt:");
            return -1;
        }
        while( fgets(buf, MAXBUF, stdin) ){
            if( sendto(fd, buf, strlen(buf), 0, (struct sockaddr *)&srv, sizeof(srv)) < 0 ){
                perror("sendto");
            }else{
                fprintf(stdout, "Enviado a %s: %s", GROUP, buf);
            }
        }
    }
    客户端程序:
    #include
    #include
    #include
    #include
    #include "my_inet.h"
    #include

    #define MAXBUF 256
    #define PUERTO 5000
    #define GROUP "224.0.1.1"

    int main(void)
    {
        int fd, n, r;
        struct sockaddr_in srv, cli;
        struct ip_mreq mreq;
        char buf[MAXBUF];

        srv.sin_family = MY_AF_INET;
        srv.sin_port = htons(PUERTO);
        inet_aton(GROUP, &srv.sin_addr );

        if( (fd = socket( MY_AF_INET, SOCK_DGRAM, MY_IPPROTO_UDP) ) < 0 ){
            perror("socket");
            return -1;
        }
        if( bind(fd, (struct sockaddr *)&srv, sizeof(srv)) < 0 ){
            perror("bind");
            return -1;
        }
/*
   inet_aton( GROUP, &mreq.imr_multiaddr );
   inet_aton( "172.16.48.2", &mreq.imr_interface );
   if( setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP, &mreq,sizeof(mreq)) < 0 ){
   perror("setsockopt");
   return -1;
   }

   if( setsockopt(fd, SOL_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0 ){
   perror("setsockopt");
   return -1;
   }
*/
        n = sizeof(cli);
        while(1){
            if( (r = recv(fd, buf, MAXBUF, 0)) < 0 ){
                perror("recv:");
            }else{
                buf[r] = 0;
                fprintf(stdout, "Mensaje desde: %s", buf);
            }
        }
    }
阅读(881) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~