使用UDP进行广播

如果通信只能在两个单体之间完成,这样的方式是没有效率的。另一方面,广播允许同时要多个接收者传播信息。

在这一章,我们将会学习下列内容:
建立一个UDP广播套接口
使用套接口发送广播信息
使用套接口接收广播信息

在学完这一章之后,我们就会知道如何使用IPv4套接口广播程序来编写程序。

理解广播地址

要使用广播,我们必须了解IPv4的特定广播地址编写。我们可以记起IP地址可以分为左边的网络ID部分以及右边的主机ID部分。广播地址所用的约定就是主机ID位全部设置为1。

当我们的网卡正确配置以后,我们可以用下面的命令来显示我们所选用接口的广播地址:
# ifconfig eth0
eth0 Link encap:Ethernet HWaddr 00:A0:4B:06:F4:8D
      inet addr:192.168.0.1 Bcast:192.168.0.255 Mask:255.255.255.0
      UP BROADCAST RUNNING PROMISC MULTICAST MTU:1500 Metric:1
      RX packets:1955 errors:0 dropped:0 overruns:0 frame:31
      TX packets:1064 errors:0 dropped:0 overruns:0 carrier:0
      collisions:0 txqueuelen:100
      Interrupt:9 Baseaddress:0xe400
#
输出的第二行表明eth0接口的广播地址为192.168.0.255。这个地址的网络ID为前三个字节192.168.0,而这个地址的主机ID部分为255。这个值255为是表示主机ID全为1的十进制数。

在255.255.255.255上进行广播

特殊的广播地址255.255.255.255也可以用于广播。仅管这种格式的广播地址也许表示向全世界进行广播,但是却有更多的限制。这种广播类型绝不会被路由器路由,而一个更为特殊的广播地址,例如192.168.0.255也许会被路由,而这取决于路由器的配置。

一个通常的广播地址,如255.255.255.255,并没有被很好的进行定义。例如,一些UNIX会将其解析来表明一个广播应发生在主机的所有网络接口上,而其他的一些UNIX内核只会选择其中的一个接口,通常是第一个定义的。当一个主机有多个网卡时,这就会成为一个问题。正是由于这个原因,并不鼓励通用广播地址的使用。

如果必须向每个网络接口广播,那么我们的软件在广播之前应执行下面的步骤:
1 确定下一个或第一个接口名字
2 确定接口的广播地址
3 使用这个广播地址进行广播
4 对于系统中其余的活动网络接口重复执行步骤1到步骤3

在执行完这些步骤以后,我们就可以认为已经对我们软件中的每一个接口进行广播。

这一章的其余内容会专注于如何对一个网络接口进行广播。在我们掌握了这些概念以后,如果我们必须对第一个接口进行广播,我们就可以应用前面的步骤。

加强mkaddr.c子函数

我们在第10章所提供的mkaddr.c子程序的一个限制就是他不可以很好的处理255.255.255.255广播地址的情况。在这一部分,我们将会看到修正这个问题的原因。

下面的diff输出显示了为修正这个问题而在mkaddr.c源码中所做出的改变。应用这个改变可以允许我们使用255.255.255.255广播地址进行试验。
$ diff ../ch.11/mkaddr.c mkaddr.c
99,102c99,100
<             ap->sin_addr.s_addr =
<                  inet_addr(host_part);
<             if ( ap->sin_addr.s_addr
<                  == INADDR_NONE )
---
>             if ( !inet_aton(host_part,
>                  &ap->sin_addr) )
$

前面的例子显示了inet_aton函数替换了有更多限制的inet_addr函数。inet_addr函数的问题在于当地址不可用时会返回INADDR_NONE值。当255.255.255.255转换为一个32位值时,他的返回值与INADDR_NONE相同。

因而就不能区分一个错误的IP地址输入与一个通用的广播地址。使用inet_aton函数可以避免这个问题。

由一个服务器广播

这一章将会演示一个简单的广播服务器程序与一个相对应的客户端程序。我们首先将会提供并解释服务器程序。

在这里所提供的服务器将会提供一个股票市场的索引模拟。服务器程序将会由外部的交易提供者处得到数据反馈,然后向所有感兴趣的客户广播股票市场索引交易。下面显示了stksrv.c服务器程序:
/*stksrv.c
 *
 * Example Stock Index Broadcast:
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif

extern int mkaddr(void *addr,
    int *addrlen,
    char *str_addr,
    char *protocol);

#define MAXQ 4

static struct
{
    char *index;
    int start;
    int volit;
    int current;
}quotes[] =
{
    {"DJIA",1030330,375},
    {"NASDAQ",276175,125},
    {"S&P 500",128331,50},
    {"TSE 300",689572,75}
};

/*
 * Initialize:
 */
static void initialize(void)
{
    short x;
    time_t td;

/*
     * seed the random number generator:
     */
    time(&td);
    srand((int)td);

for(x=0;x<MAXQ;++x)
    quotes[x].current = quotes[x].start;
}

/*
 * randomly change one index quotation:
 */
static void gen_quote(void)
{
    short x;    /*index*/
    short v;    /* volatility of index */
    short h;    /* half of v */
    short r;    /* random change */

x = rand() % MAXQ;
    v = quotes[x].volit;
    h = (v/2) -2;
    r = rand() % v;

if(r<h)
    r = -r;
    quotes[x].current += r;
}

/*
 * This function report the error and
 * exits back to the shell:
 */
static void bail(const char *on_what)
{
    fputs(strerror(errno),stderr);
    fputs(": ",stderr);
    fputs(on_what,stderr);
    fputc('/n',stderr);
    exit(1);
}

int main(int argc,char **argv)
{
    short x;        /* index of stock indexes */
    double I0;        /* initial index value */
    double I;        /* index value */
    char bcbuf[512],*bp;/* buffer and ptr */
    int z;        /* status return code */
    int s;        /* socket */
    struct sockaddr_in adr_srvr;    /* AF_INET */
    int len_srvr;    /* length */
    struct sockaddr_in adr_bc;    /* AF_INET */
    int len_bc;        /* length */
    static int so_broadcast = TRUE;
    static char
    *sv_addr = "127.0.0.1:*",
    *bc_addr = "127.255.255.255:9097";

/*
     * Form a server address:
     */
    if(argc >2)
    /* server address */
    sv_addr = argv[2];

if(argc >1)
    /* broadcast address */
    bc_addr = argv[1];

/*
     * Form the server address:
     */
    len_srvr = sizeof adr_srvr;

z = mkaddr(
        &adr_srvr,    /* returned address */
        &len_srvr,    /* returned length */
        sv_addr,    /* input string addr */
        "udp");    /* udp protocol */

if(z==-1)
    bail("bad server address");

/*
     * Form the broadcast address:
     */
    len_bc = sizeof adr_bc;

z = mkaddr(
        &adr_bc,    /* returned address */
        &len_bc,    /* returned length */
        bc_addr,    /* input string addr */
        "udp");    /* udp protocol */

if(z==-1)
    bail("bad broadcast address");

/*
     * Create a UDP socket to use:
     */
    s = socket(AF_INET,SOCK_DGRAM,0);
    if(s==-1)
    bail("socket()");

/*
     * Allow broadcasts:
     */
    z = setsockopt(s,
        SOL_SOCKET,
        SO_BROADCAST,
        &so_broadcast,
        sizeof so_broadcast);

if(z==-1)
    bail("setsockopt(SO_BROADCAST)");

/*
     * bind an address to our socket,so that
     * client programs can listen to this
     * server
     */
    z = bind(s,(struct sockaddr *)&adr_srvr,len_srvr);

if(z==-1)
    bail("bind()");

/*
     * Now start serving quotes:
     */
    initialize();

for(;;)
    {
    /*
     * update on quote in the list:
     */
    gen_quote();

/*
     * Form a packet to send out:
     */
    bp = bcbuf;
    for(x=0;x<MAXQ;++x)
    {
        I0 = quotes[x].start / 100.0;
        I = quotes[x].current /100.0;
        sprintf(bp,
            "%-7.7s %8.2f %+.2f/n",
            quotes[x].index,
            I
            I-I0);
        bp += strlen(bp);
    }

/*
     * broadcast the updated info:
     */
    z = sendto(s,bcbuf,strlen(bcbuf),0,(struct sockaddr *)&adr_bc,len_bc);
    if(z==-1)
        bail("sendto()");
    sleep(4);
    }
    
    return 0;
}

接收广播

我们所提供的客户端程序必须监听我们的股票市场索引程序所发布的广播。下面是客户端程序的源码:
/*gquotes.c
 *
 * Get datagram stock market
 * quotes from udp broadcast:
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif

extern int mkaddr(
    void *addr,
    char *addrlen,
    char *str_addr,
    char *protocol);

/*
 * This function report the error and
 * exits back to the shell:
 */
static void bail(const char *on_what)
{
    fputs(strerror(errno),stderr);
    fputs(": ",stderr);
    fputs(on_what,stderr);
    fputc('/n',stderr);
    exit(1);
}

int main(int argc,char **argv)
{
    int z;
    int x;
    struct sockaddr_in adr;    /* AF_INET */
    int len_inet;        /* length */
    int s;            /* socket */
    char dgram[512];        /* recv buffer */
    static int so_reuseaddr = TRUE;
    static char
    *bc_addr = "127.255.255.255:9097";

/*
     * use a server address from the command
     * line,if one has been provided.
     * otherwise,this program will default
     * to using the arbitrary address
     * 127.0.0.23:
     */
    if(argc>1)
    /* broadcast address: */
    bc_addr = argv[1];

/*
     * Create a UDP socket to use:
     */
    s = socket(AF_INET,SOCK_DGRAM,0);
    if(s==-1)
    bail("socket()");

/*
     * Form the broadcast address:
     */
    len_inet = sizeof adr;

z = mkaddr(&adr,
        &len_inet,
        bc_addr,
        "udp");

if(z==-1)
    bail("bad broadcast address");

/*
     * Allow multiple listeners on the
     * broadcast address:
     */
    z = setsockopt(s,
        SOL_SOCKET,
        SO_REUSEADDR,
        &so_reuseaddr,
        sizeof so_reuseaddr);

if(z==-1)
    bail("setsockopt(SO_REUSEADDR)");

/*
     * bind out socket to the broadcast address:
     */
    z = bind(s,
        (struct sockaddr *)&adr,
        len_inet);

if(z==-1)
    bail("bind(2)");

for(;;)
    {
    /*
     * wait for a broadcast message:
     */
    z = recvfrom(s,    /*socket */
        dgram,    /* receiving buffer */
        sizeof dgram,    /* max rcv buf size */
        0,    /* flags:no options */
        (struct sockaddr *)&adr,    /* addr */
        &x);    /* addr len */

if(z<0)
        bail("recvfrom(2)");

fwrite(dgram,z,1,stdout);
    putchar('/n');
    fflush(stdout);
    }

return 0;
}

Linux Socket学习(十三)相关推荐

  1. Linux Socket学习(十八)--完

    一个实际的网络工程 不论我们的头脑是否在由上一章的学习中清醒过来,现在我们需要休息一下了.在这一章我们并不讨论新的内容,而是用我们所学到的这些东西来实现一些有趣的事情.在学习了这么多的东西之后来一些有 ...

  2. Linux Socket学习--套接口的类型和协议

    我们首先来说一下PF_INET和AF_INET,虽然标准提倡在指定demain参数的时候,优先使用PF_INET,但是大量已经编写的c代码遵循旧的协议.目前情况是AF_UNIX=PF_UNIX,AF_ ...

  3. Linux Socket学习--为套接口绑定地址

    当我们使用socket函数创建一个套接口之后,这个套接口就处于无名状态,虽然之前我们提到即使没有地址,套接口也能使用,但是这个只限于套接口对在同一个Linux内核中,如果位于两台不同的主机的套接口需要 ...

  4. Linux Socket学习(五)

    地址转换函数 上一章中,我们已经了可以分配与初始化各种类型的套接口.这些是由一个常量进行初始化的简单例子.由一个使用变化地址的C字符串设置一个地址需要更多的编程努力.在这一章,我们将会关注建立网络地址 ...

  5. Linux Socket学习(十二)

    套接口选项 在前面的几章中,我们讨论了使用套接口的基础内容.现在我们要来探讨一些可用的其他的特征.在我们掌握了这一章的概念之后,我们就为后面的套接口的高级主题做好了准备.在这一章,我们将会专注于下列主 ...

  6. Linux socket学习

    1 //服务器端 server.c 2 #include<stdio.h> 3 #include<stdlib.h> 4 #include<string.h> 5 ...

  7. Linux Socket学习--面向非连接的协议

    套接口的通信有2中基本方式:面向连接和面向非连接的通信. 面向非连接指的是通信之前不需要建立连接,我们生成了一个非连接的套接口,就可以向任何愿意接受我们消息的套接口发送消息,而且每一个消息都可以被重定 ...

  8. linux中ftp的工作原理,Linux系统学习 十二、VSFTP服务—简介与原理

    1.简介与原理 互联网诞生之初就存在三大服务:WWW.FTP.邮件 FTP主要针对企业级,可以设置权限,对不同等级的资料针对不同权限人员显示. 但是像网盘这样的基本没有权限划分. 简介: FTP(Fi ...

  9. linux操作系统学习网站整理(100个)

    linux操作系统学习网站整理(100个) 评选出的这100个优秀站点,将按照下述20个类别作以评介: (一) 文件下载 (二) 幽默娱乐 (三) 相关新闻 (四) 通用硬体 (五) 专用硬体 (六) ...

最新文章

  1. 软件工程的实践项目课程的自我目标
  2. 以太坊代币标准: ERC20、ERC223的介绍与比较
  3. 【SpringMVC学习11】SpringMVC中的拦截器
  4. winform npoi 将execl转换成datatable,导入数据库
  5. 拓端tecdat|R语言逻辑回归(对数几率回归,Logistic)分析研究生录取数据实例
  6. FastStone Capture—截图功能
  7. opendir和readdir
  8. matlab电路元件二极管,tvs二极管有正负极吗?TVS管正负极区分方法
  9. uva10105 - Polynomial Coefficients(多项式系数)
  10. 金句: 對比MBA學位,我們更需要PSD學位的人! Poor, Smart and Deep Desire to… | consilient_lollapalooza on Xanga...
  11. 用div和css制作网页,DIVCSS网页设计总结:有用的3个网页制作_css
  12. 05.局域网_NAT
  13. 南半球被烧了20多天! 西方媒体集体沉默! 这是全世界最残忍的一幕...
  14. 无脑三步走解决 The service already exists
  15. stata联立方程组笔记代码
  16. 数的进制转换(任意两个进制之间的转换)
  17. cass实体编码列表
  18. Spatial Attention model
  19. NYOJ T485 A*B Problem
  20. 北斗导航 | 航空所需导航性能与完好性监测(理论):PBN、NPA、APV、CAT、RNAV、XPL、XAL

热门文章

  1. 指针也是一种数据类型
  2. 【mysql】--MHA+Atlas
  3. django基本操作
  4. SQL学习之组合查询(UNION)
  5. 分析Cocos2d-x横版ACT手游源码 1、公共
  6. 三月新增电脑病毒400万 被感染计算机多达2000万
  7. 06年编写的E阳指源码V1.7大放送
  8. 两个linux之间拷贝文件及文件夹
  9. 书评:实战Apache JMeter
  10. 网络高可用性解决方案