UNP学习 广播

一、概述

虽然UDP支持各种形式的地址,但TCP只支持单播地址。

上图要点是:

  • IPv4对多播的支持是可选的,而IPv6则时必须的。
  • IPv6没有提供对广播的支持:当使用广播的IPv4应用程序一直到IPv6时,必须使用IPv6的多播方式进行重新编码。
  • 广播和多播要使用UDP,二者都不能使用TCP

广播的用途:

1.假定服务器主机在本地子网上,但不知道它的单播IP地址时,对它进行定位,这就是资源发现。

2.当有多个客户和单个服务器通信时,减少局域网上数据流量。

常见的实例

1.ARP:ARP是IPv4的一个i额基本组成部分,而不是一个用户应用程序。

2.BOOTP(引导协议,Bootstrap Protocol):客户假定有一台服务器主机在本地子网上。

3.NTP(网络时间协议,Network Timer Protocol):一种常见的情形是:一个NTP客户主机可能配置成使用一个或多个服务器主机IP地址,其上面的NTP客户于是以某个频率轮询这些服务器。

4.路由后台进程。

 

二、广播地址

如果用{netid,subnetid,hostid}({网络ID,子网ID,主机ID})表示IPv4地址,那么有四种类型的广播地址。

1.子网广播地址:{netid,subnetid,-1}。这类地址编排指定子网上的所有接口。

2.全部子网广播地址:{netid,-1,-1}。这类广播地址编排指定网络上的所有子网。

3.网络广播地址:{netid,-1}。这类地址用于不进行子网划分的网络。

4.受限广播地址:{-1,-1,-1}或255.255.255.255。路由器从不转发目的地址为255.255.255.255的IP数据报

 

三、竞争状态

  多个进程访问共享数据,但正确结果依赖于进程的执行顺序,这种情况我们称之为竞争状态。

竞争状态通常是线程编程中时钟要注意的一个重要问题,因为在线程中有非常多的数据需要共享。

   在进行信号处理时,通常会出现各种类型的竞争状态。这是因为在我们的程序执行过程中,内核随时都会递交信号。

例子1:

 1 #include "unp.h"
 2 static void recvfrom_alarm(int);
 3 void
 4 dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
 5 {
 6     int n;
 7     const int on = 1;
 8     char sendline[MAXLINE], recvline[MAXLINE+1];
 9     sigset_t sigset_alarm;
10     socklen_t len;
11     struct sockaddr *preply_addr;
12     preply_addr = Malloc(servlen);
13     Setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
14     Sigemptyset(&sigset_alarm);
15     Signal(SIGALRM, recvfrom_alarm);
16     while(Fgets(sendline, MAXLINE, fp) != NULL) {
17         Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
18         alarm(5);
19         for( ; ;) {
20             len = servlen;
21             Sigprocmask(SIG_UNBLOCK, &sigset_alrm, NULL);
22             n = recvfrom(sockfd, recvline, MAXLINE, 0, preply_addr, &len);
23             Sigprocmask(SIG_BLOCK, &sigset_alrm, NULL);
24             if(n < 0) {
25                 if(errno == EINTR)
26                     break;              /* waited long enough for replies */
27                 else
28                     err_sys("recvfrom error");
29             } else {
30                 recvline[n] = 0;        /* null terminate */
31                 printf("from %s: %s", Sock_ntop_host(preply_addr, len), recvline);
32             }
33         }
34     }
35 }
36 
37 static void 
38 recvfrom_alarm(int signo)
39 {
40     return;     /* just nterrupt the recvfrom() */
41 }
example1

解阻塞信号、调用recvfrom和阻塞信号都是互相独立的系统调用。假定recvfrom返回了最后一个应答数据报并且SIGALRM信号在recvfrom和阻塞信号之间递交,下一次recvfrom的调用将永远阻塞。错误

例子2:

#include "unp.h"
#include <setjmp.h>
static void recvfrom_alarm(int);
static sigjmp_buf jmpbuf;
void
dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
{
    int n;
    const int on = 1;
    char sendline[MAXLINE], recvline[MAXLINE+1];
    socklen_t len;
    struct sockaddr *preply_addr;
    preply_addr = Malloc(servlen);
    Setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
    Signal(SIGALRM, recvfrom_alarm);
    while(Fgets(sendline, MAXLINE, fp) != NULL) {
        Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
        alarm(5);
        for( ; ; ) {
            if(sigsetjmp(jmpbuf, 1) != 0)
                break;
            len = servlen;
            n = Recvfrom(sockfd, recvline, MAXLINE, 0, preply_addr, &len);
            recvline[n] = 0;
            printf("from %s: %s", Sock_ntop_host(preply_addr, len), recvline);
        }
    }
}
static void
recvfrom_alarm(int signo)
{
    siglongjmp(jmpbuf, 1);
}
example2

 pselect的关键点是设置信号掩码、测试描述字及恢复信号掩码等操作都是原子操作。错误

例子3:

#include "unp.h"
static void recvfrom_alarm(int);
static int pipefd[2];
void
dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
{
    int n, maxfdp1;
    const int on = 1;
    char sendline[MAXLINE], recvline[MAXLINE + 1];
    fd_set rset;
    socklen_t len;
    struct sockaddr *preply_addr;
    preply_addr = Malloc(servlen);
    Setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
    Pipe(pipefd);
    maxfdp1 = max(sockfd, pipefd[0]) + 1;
    FD_ZERO(&rset);
    Signal(SIGALRM, recvfrom_alarm);
    while(Fgets(sendline, MAXLINE, fp) != NULL) {
        Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
        alarm(5);
        for( ; ; ) {
            FD_SET(sockfd, &rset);
            FD_SET(pipefd[0], &rset);
            if((n = select(maxfdp1, &rset, NULL, NULL, NULL)) < 0) {
                if(errno == EINTR)
                    continue;
                else
                    err_sys("select error");
            }
            if(FD_ISSET(sockfd, &rset)) {
                len = servlen;
                n = Recvfrom(sockfd, recvline, MAXLINE, 0, preply_addr, &len);
                recvline[n] = 0;
                printf("from %s: %s", Sock_ntop_host(preply_addr, len), recvline);
            }
            if(FD_ISSET(pipefd[0], &rset)) {
                Read(pipefd[0], &n, 1);
                break;
            }
        }
    }
}
static void
recvfrom_alarm(int signo)
{
    Write(pipefd[1], "", 1);
    return;
}
example3

 另一个解决同一问题的正确方法。不是让信号处理简单的返回,期望这样能够中断被阻塞的rrecvfrom相反,我们让信号黑醋栗程序使用IPC通知dg_cli函数到抵制其到时。

posted @ 2018-03-17 16:54  习惯就好233  阅读(145)  评论(0编辑  收藏  举报