zoukankan      html  css  js  c++  java
  • 数据解释带外数据

    在写这篇文章之前,xxx已写过了几篇关于改数据解释主题的文章,想要了解的朋友可以去翻一下之前的文章

         定义带 外 数据 

    想 像一下在银行人们排起队待等处置他们的帐单。在这个步队中每个人最后会都移到面前由出纳员行进服务。在现想像一下一个走入银行,超出个整步队,然后用枪抵 住出纳员。这个就可以看作为  数据 。这个盗强超出个整步队,是因为这把枪给了他凌驾于世人的权利。出纳员也集会中注意力于这个盗强身上,因为他晓得后以 的势形是很紧迫的。

    应相的,一个连接的流式套口接上的  数据 的作工理原也与此相似。常通情况下,数据 由连接的一端流到另一端,并且为认 数据 的全部节字都是确精排序的。晚写入的节字绝不会早于先写入的节字到达。然而套口接API念概性的供给了一些实用程序,从而可以使得一串数据 无阻的先于 常通的数据 到达收接端。这就是所谓的发送  数据 。

    从技术上来讲,一个TCP 流不可以发送带 外 数据 。而他所持支的只是一个念概性的紧迫数据 ,这些紧迫数据作为带 外 数据 映射到套口接API。 这就 来了多许制限,这些我们会在面前行进探讨。
    尽管我们可以立刻享受到在银行中超出个整步队的好处,但是我们也会认识到用使枪来到达这样的的目是反社会的为行。一个TCP 流常通希望以完善的队列来发送数据 节字,那么乱序的发送数据 就仿佛与流的念概相违背。那么为什么要供给  数据 的套口接方法呢?

    也 许我们已意识到了,偶然数据 会以定一的式方变得紧迫。一个流套口接会有一个量大的数据 队列待等发送到网络。在程远端点,也会有量大已收接的,却还没有被 程序取读的数据 。如果发送客户端程序由于一些原因须要消取已写入服务器的请求,那么他就须要向服务器紧迫发送一个识标消取的请求。如果向程远服务器发送 消取请求失败,那么就会无谓的费浪服务器的资源。 
    使 用  数据 的际实程序例子就是telnet,rlogin,ftp命令。前两个程序会将中断字符作为紧迫数据 发送到程远端。这会答应程远端冲刷全部未处置 的入输,并且弃丢全部未发送的终端出输。这会倏地中断一个向我们屏幕发送量大数据 的行运进程。ftp命令用使  数据 来中断一个件文的输传。

    套口接与带 外 数据 
    新重调强套口接口接本身并非制限因素是很主要的。  数据 的念概际实上映射到 TCP /IP信通的紧迫数据模式。在明天,TCP 流对于网络是很主要的,而在这一章我们仅专一于  数据 适应于TCP 紧迫数据 的套口接用使。

    实现上的化变 

    很可怜,TCP 的实在现紧迫数据 就如何处置上有两种不同的解释。这些别区我们将会本章的面前行进具体的探讨。这些不同的解释是:

    TCP 紧迫指针的RFC793解释
    TCP 紧迫指针的BSD解释

    现 在已涌现了中分的状态,因为原始的TCP 规格答应两种解释。从而,一个"主机须要"的RFC识标准确的解释。然而,大多数的实现都基于BSD源码,而在 明天BSD方法还是一个通用的用法。从持支两种解释的角度而言,Linux处于决裂的状态。然而,Linux认默用使BSD解释。 
    在现我们稍做顿停,来检测一个我们Linux系统的后以设置。这决了我们这一章的例子是不是可以发生一样的结果。

    $ cat /proc/sys/net/ipv4/tcp_stdurg
    0
    $

    这里示显的出输为0。这示表后以起作的为BSD解释。如果我们失掉其他的出输结果(例如1),那么如果我们希望失掉也本章的例子同相的结果,我们应将其为改0。

    上面列出了tcp_stdurg设置可能的取值。tcp_stdurg值可以在Shell本脚中行进询查和设置,包含启动与关闭本脚。

    /proc/sys/net/ipv4_stdurg的设置值:
    0   BSD解释(Linux认默)
    1   RFC793解释

    如果我们须要将其设置为改0,我们须要root权限,然后入输上面的命令:
    # echo 0 >/proc/sys/net/ipv4/tcp_stdurg
    #

    行进重双检测老是很明知的,所以在变改后以再列出其值来定确变改是不是为核内所受接。我们也可以在上面的例子中用使cat命令来示显0值。

    编写带 外 数据 

    一个write调用将会写入一个我们已习气的 内数据 。应相的,必须用使一个新的函数来写入  数据 。为了这个的目,在这里列出send函数地型原:
    #include <sys/types.h>
    #include <sys/socket.h>
    int send(int s, const void *msg, int len, unsigned int flags);

    这个函数须要四个参数,分别为:
    1 要写入的套口接s
    2 存放要写入的消息的缓冲地址msg
    3 消息长度(len)
    4 发送选项flags

    send函数与write函数相相似,所不同的只是供给了额外的flags参数。这是际实的部分。send函数返回写入的节字数,如果发生错误则会返回-1,检测errno可以失掉错误原因。
    要发送  数据 ,与write调用相似,用使前三个参数。如果我们为flags参数指定了C语言宏MSG_OOB,则数据 是作为  数据 发送的,而不是常通的 内数据 ,如上面的例子代码:

    char buf[64]; /* Data */
    int len;      /* Bytes */
    int s;        /* Socket */
    . . .
    send(s,buf,len,MSG_OOB); 

    如果所供给的flags参数没有MSG_OOB位,那么数据 是作为常通数据 写入的。这就答应我们用使同一个函数同时发送 内数据 与  数据 。我们只须要简单的在程序控制中变改flags参数值来到达这个的目。

    取读带 外 数据 

    带 外 数据 可以用两种不同的方法行进取读:
    单独取读带 外 数据 
     内数据 混合取读
     

    为了与常通数据 流分开单独取读  数据 ,我们须要用使recv函数。如果我们猜想recv函数与read函数相似,只是有一个额外的flags参数,那么我们的猜想是准确的。这个函数的型原如下:

    #include <sys/types.h>
    #include <sys/socket.h>
    int recv(int s, void *buf, int len, unsigned int flags);

    recv函数受接四参数,分别为:
    1 要从中收接数据 的套口接s( 内数据 或  数据 )
    2 要放置所收接的数据 的缓冲区地址buf
    3 收接缓冲区的最大长度
    4 调用所需的flags参数

    正如我们所看到的,recv函数是与send函数调用相对应的函数。为要收接  数据 ,在flags参数中指定C宏MSG_OOB。没有MSG_OOB标志位,recv函数所收接的为常通的 内数据 ,就像常通的read调用一样。

    recv函数返回所收接到的节字数,如果出错则返回-1,检测errno可以失掉错误原因。

    上面的代码例子演示了如何取读  数据 :
    char buf[128];   /* Buffer */
    int n;      /* No. of bytes */
    int s;             /* Socket */
    int len;         /* Max bytes */
    . . .
    n = recv(s,buf,len,MSG_OOB);

    尽管指出  数据 可以与常通数据 相混合还为时尚早,但是我们会在面前行进相关的探讨。

    理解SIGURG 信号 

      数所到在时,收接进程须要收到通知。如果须要与常通数据 流分开取读时更是如此。这样做的一个方法就是当  数据 到达时,使Linux核内向我们的进程发送一个SIGURG 信号。

    用使SIGURG 信号通知须要两个先决条件:
    我们必须拥有套口接
    我们必须为SIGURG 创建一个信号处置器
     

    要收接SIGURG 信号,我们的进程必须为套口接的全部者。要建立这样的拥有关系,我们可以用使fcntl函数。其函数型原如下: 

    #include <unistd.h>
    #include <fcntl.h>
    int fcntl(int fd, int cmd, long arg);

    函数参数如下:
    1 要在其上执行控制函数的件文描述符fd(或是套口接)
    2 要执行的控制函数cmd
    3 要设置的值arg

    函数的返回值依赖于fcntl所执行的控制函数。对于课外阅读感兴趣的读者,fcntl的Linux man手册页具体的描述了cmd的F_SETOWN操作。

    要将我们的进程创建为套口接的全部者,收接程序须要用使上面的代码:

    int z; /* Status */
    int s; /* Socket */
    z = fcntl(s,F_SETOWN,getpid());
    if ( z == -1 ) {
        perror("fcntl(2)");
        exit(1);
    }

    F_SETOWN操作会使得fcntl函数成功时返回0,失败时返回-1。

    另外一个先决条件是程序必须准备好收接SIGURG 信号,这是通过为信号创建一个信号处置器来做到的。我们很快就会看到这样的一个例子。


    收接SIGURG 信号 

    移开了这些烦琐的作工后以,在现我们可以来探索有趣的  数据 的念概了。上面所列的程序代码就是我们用来收接数据 和当  数据 到达时处置  数据 的程序。他设计用使BSD解释来处置  数据 ,而这也正是Linux的认默情况。
    /*
    * oobrec.c
    *
    * Example OOB receiver:

        */

        

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <errno.h>
    #include <unistd.h>
    #include <signal.h>
    #include <fcntl.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    
    extern void bail(char *on_what);
    extern int BindAccept(char *addr);
    
    static int s = -1;   /* Socket */
    
    /*
    * SIGURG signal handler:
    */
    static void sigurg (int signo)
    {
       int n;
       char buf[256];
    
       n = recv(s,buf,sizeof buf,MSG_OOB);
       if(n<0)
           bail("recv(2)");
    
       buf[n] = 0;
       printf("URG ''%s'' (%d) /n",buf,n);
    
       signal(SIGURG ,sigurg );
    }
    
    int main(int argc,char **argv)
    {
       int z;   /* Status */
       char buf[256];
    
       /*
       * 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.1:
       */
       s = BindAccept(argc >=2 ?argv[1] :"127.0.0.1:9011");
    
       /*
       * Establish owership:
       */
       z = fcntl(s,F_SETOWN,getpid()); 
       if(z==-1)
           bail("fcntl(2)");
    
       /*
       * Catch SIGURG :
       */
       signal(SIGURG ,sigurg );
    
       for(;;)
       {
           z = recv(s,buf,sizeof buf,0);
           if(z==-1)
               bail("recv(2)");
           if(z==0)
               break;
           buf[z] = 0;
    
           printf("recv ''%s'' (%d) /n",buf,z);
       }
    
       close(s);
       return 0;
    }

        然而,在我们将收接程序投入用使之前,我们还须要一个发送程序。

        

        

        发送带 外 数据 

        

        

        

        上面列出的程序演示了一个简短的发送程序,他只可以输传一些小的字符串,然后停止发送

        

         

        

         

        数据

         。这个程序为了在收接端管理传送块用使了多许的sleep(3)调用。

        

    /*
    * oobsend.c
    *
    * Example OOB sender:
    */
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    
    extern void bail(char *on_what);
    extern int Connect(char *addr);
    
    /*
    * Send in-band data:
    */
    static void iband(int s,char *str)
    {
       int z;
    
       z = send(s,str,strlen(str),0);
       if(z==-1)
           bail("send(2)");
    
       printf("ib: ''%s'' (%d) /n",str,z);
    }
    
    /*
    * Send out-of-band data:
    */
    static void oband(int s,char *str)
    {
       int z;
    
       z = send(s,str,strlen(str),MSG_OOB);
       if(z==-1)
           bail("send(2)");
    
       printf("OOB ''%s'' (%d)/n",str,z);
    }
    
    int main(int argc,char **argv)
    {
       int s = -1;
    
       s = Connect(argc >=2
               ? argv[1]
               : "127.0.0.1:9011");
    
       iband(s,"In the beginning");
       sleep(1);
    
       iband(s,"Linus begat Linux,");
       sleep(1);
    
       iband(s,"and the Penguins");
       sleep(1);
    
       oband(s,"rejoiced");
       sleep(1);
    
       iband(s,"exceedingly.");
       close(s);
    
       return 0;
    }
        每日一道理
    共和国迎来了她五十诞辰。五十年像一条长河,有急流也有缓流;五十年像一幅长卷,有冷色也有暖色;五十年像一首乐曲,有低音也有高音;五十年像一部史诗,有痛苦也有欢乐。长河永远奔流,画卷刚刚展开,乐曲渐趋高潮,史诗还在续写。我们的共和国正迈着坚定的步伐,跨入新时代。

        编译程序:
    $ make oobrecv oobsend
    gcc -c -D_GNU_SOURCE -Wall -Wreturn-type -g oobrecv.c
    gcc -c -D_GNU_SOURCE -Wall -Wreturn-type -g mkaddr.c
    gcc -c -D_GNU_SOURCE -Wall -Wreturn-type -g bindacpt.c
    gcc oobrecv.o mkaddr.o bindacpt.o -o oobrecv
    gcc -c -D_GNU_SOURCE -Wall -Wreturn-type -g oobsend.c
    gcc oobsend.o mkaddr.o bindacpt.o -o oobsend
    $

    在编译完成后以,我们失掉两个可执行程序:
    oobrecv 是收接程序(一个服务器)
    oobsend 是发送程序(一个客户端)

    在现我们已准备好来调用这两个程序了。

    测试oobrecv与oobsend程序

    最好是在两个不同的终端会话上行运这两个程序。用使两个不同的xterm窗口,或是两个不同的终端会话。首先在第一个终端会话中启动收接程序:
    $ ./oobrecv

    如果我们希望指定我们的以太网地址而不是用使认默的回环地址,那么这两个程序都收接一个地址与端口号对。例如,上面的将会作工在一个NIC卡地址为192.168.0.1的系统上:
    $ ./oobrecv 192.168.0.1:9023

    这会启动服务器在192.168.0.1的9023端口上监听。然而,为了演示,我们可以不指定参数来行运这个程序。

    在现在第二个终端会话中启动发送程序:
    $ ./oobsend
    ib: ''In the beginning'' (16)
    ib: ''Linus begat Linux,'' (18)
    ib: ''and the Penguins'' (16)
    OOB ''rejoiced'' (8)
    ib: ''exceedingly.'' (12)
    $

    以ib:开始的行表明写入的 内数据 。以OOB开始的行表明''rejoiced''是作为  数据 写入套口接的。

    如果我们可以同时观察两个终端,我们就会发现收接程序报告数据 稍晚于发送程序发送数据 。其会话出输相似于上面的样子:
    $ ./oobrecv
    rcv ''In the beginning'' (16)
    rcv ''Linus begat Linux,'' (18)
    rcv ''and the Penguins'' (16)
    URG ''d'' (1)
    rcv ''rejoice'' (7)
    rcv ''exceedingly.'' (12)
    $

    在这个终端会话中示显的以rcv开始的行表明收接到的常通的 内数据 。以URG开始的行表明收接到SIGURG 信号,并且信号处置程序被调用。在信号处置器中,紧迫数据 被取读并报告。我们应注意到一个很奇怪的事情--只有d节字被作为带 外 数据 收接。为什么是这样? 

    理解紧迫指针 

    在这一章面前,套口接口接供给了一个常通的网络口接。这就包含他如何处置带 外 数据 。然而,紧迫数据TCP 实现却达不到带 外 数据 所包含的常通念概。 尽管个整字符串''rejoiced''用使send作为  数据 发送,但是在收接端我们可以观察到下列内容:

    只有d字符作为  数据 被收接
    d字符在其余的''rejoice''之前到达

    d字符在''rejoice''之前被收接的事实确实演示了d字符更为紧迫的事实。他表明节字顺序已被一个紧迫元素所打乱。
    理解TCP 紧迫模式 

    只有一个节字被作为带 外 数据 被收接的事实与一个TCP 协议念概到一个套口接念概的映射有关。TCP 紧迫模式被映射到更为常通的带 外 数据 的套口接念概。 

    TCP 协议本身并不供给  数据 程序。最接近于这个套接式方的念概就是信通的TCP 紧迫模式。为了使得我们理解紧迫模式是如何作工,在这里有必要行进一些TCP 协议的探讨。

    当设置了MSG_OOB位用使send套口接口接函数时,数据 写入了TCP 的外行队列,并且建立了一个紧迫指针。这个指针的确切位置是由我们在面前所说的tcp_stdurg来决定的。 下表列出回顾了我们面前所说的两种解释,并且表明了紧迫指针的位置:

    值       解释       紧迫指针
    0       BSD解释       紧迫节字之后
    1       RFC793解释   紧迫节字之前

    下图示显了send调用在将字符串''rejoiced''排列为  数据 返回之后,TCP 发送缓冲区的可视化情况。尽管我们并不对BSD解释感兴趣,但是在这个图中同时示显了两种解释的情况。

    对于BSD解释,用使MSG_OOB标志调用send所发生的事件队列为:

    数据 放入TCP 的外行队列(在这种情况为空TCP 缓冲区的开始处)
    2 开启TCP 紧迫模式(一个TCP URG位设置为真)
    3 计算紧迫指针,指向入输外行TCP 队列的最后一个节字之后。

    在例子程序oobsend.c中,send调用之后跟随着了一个sleep调用。这个动作会使得Linux核内执行下列操作:

    1 发送目前为止在TCP 缓冲区中已排队的数据 ,而不是待等更多的数据 。
    2 在现由TCP 协议所创建的数据 包头设置了URG位。这就表明用使TCP 紧迫模式(这是因为设置了MSG_OOB位来调用send函数)
    3 计算一个TCP 紧迫指针并且放在数据 包头中。在这个例子中(tcp_stdurg=0),这个指针指向已排队的  数据 的最后一个节字之后。
    4 包含URG位,紧迫指针以及全部待等发送的数据 包的数据 包头在现作为一个物理数据 包发送到网络口接设备。

    执行完这些步骤之后,数据 包立刻加 传递到网络的收接主机。这个数据 在程远端被收接,念概上如下图所示。
    当一个URG位被设置为真的数据 包被收接到时,Linux核内会用使信号SIGURG 通知拥有这个套接品的进程。之所以这样做,是因为数据 包包含一个紧迫指针(这也就是为什么要在TCP 头设置URG位的原因)。

    程 序oobrecv.c,一旦处置SIGURG 信号,就会设置MSG_OOB标志,通过recv调用来取读带 外 数据 。这会使得Linux核内只返回带 外 数 据 。因为TCP 并不会记录带 外 数据 的起始位置,套口接API只会返回数据 包内紧迫指针之前的一个节字(假设tcp_stdurg=0)。 应相的,在我们的 例子中,只有节字d作为  数据 返回。任何 内数据 的取读队列会取读其余的''rejoice''节字,以及紧迫节字之后的数据 (如果存在)。

    尽管带 外 数据 并非在信号处置函数中取读,只会取读''rejoice''节字以及非紧迫数据 序列。d节字会被阻止在常通的 内数据 中返回,是因为他已被识标为带 外 数据 。 

    tcp_stdurg=1的紧迫模式
    空 间并不答应我们具体探讨这种情况,但是一些小的评论还是值得的。当tcp_stdurg=1时,常通会发生一件奇怪的事情,常通会进入紧迫模式,而其应相 的紧迫指针也会被收接,但是却并不会取读应相的紧迫数据 。如果紧迫指针正如位于数据 包中最后一个数据 节字之后,那么也许在其后就会再收接到任何数据 节字。 紧迫数据也 许会在其后的一个数据 包中。正是因为这个原因,当用使这种模式时,当收到SIGURG 信号时,设置了MSG_OOB位的recv调用并不须要必须为TCP 返回一个  数据 。

    要处置紧迫数据 不可得的情况,我们必须执行上面的操作(记住,这适用于tcp_stdurg=1的情况):

    1 在一个标记中记录SIGURG 事件(也就是一个名为urg_mode=1的变量)。
    2 由我们的信号处置器中返回。
    3 继续取读我们程序中的 内数据 。
    4 当urg_mode的值为真时,试着用使设置了MSG_OOB标记位的recv函数来取读  数据 。
    5 如果步骤4失掉数据 ,那么设置urg_mode=0,并且返回常通的处置。重复步骤3。
    6 如果步骤4没有失掉任何  数据 ,将urg_mode设置为真继续处置。重复步骤3。

    再一次,必须调强我们也许不会为Linux代码执行这些步骤,除非Linux变改了方向。Linux认默用使BSD(tcp_stdurg=0)紧迫数据 模式,而这是较为容易处置的。
     

    收接内联带 外 数据 

    在面前,我们已谈到,也可以在常通的 内数据 混合收接  数据 。偶然用这样的式方来处置会更为方便。要为一个特定的套口接打开这种操作模式,我们必须设置SO_OOBINLINE套口接选项:

    例如

    int z;                   /* Status */
    int s;                   /* Socket */
    int oobinline =1;      /* TRUE */
    z = setsockopt(s,
        SOL_SOCKET,        /* Level */
        SO_OOBINLINE,      /* Option */
        &oobinline,       /* ptr to value */
        sizeof oobinline); /* Size of value */

    警告

    如果我们为一个套口接打开了这个选项,我们就不可以用使设置了MSG_OOB标记位的recv函数。如果我们这样做了,我们就会返回一个错误,而变量errno会被设置为EINVAL。

    注意

    如果我们觉得有用,也可以用使SIGURG 信号。这是通过一个用使F_SETOWN的fcntl函数来建立了。

    定确紧迫指针 

    无论我们是不是正在用使内联数据 的式方行进收接,当我们收接到后以数据 流中的数指针时,我们都可以自由的用使一个函数来通知我们。这可以通过准确的参数来调用ioctl(2)来定确。

    例如

    #include <sys/ioctl.h>
    . . .
    int z;     /* Status */
    int s;     /* Socket */
    int flag; /* True when at mark */
    z = ioctl(s, SIOCATMARK,&flag);
    if ( z == -1 )
         abort();         /* Error */
    if ( flag != 0 )
         puts("At Mark");
    else
         puts("Not at mark.");

    在现我们已了解了面前所介绍地功能,上面我们将用使一个修改的oobrecv程序来演示收接内联数据 ,并且在收接到数据 时测试紧迫数据 标记。

    用使内联带 外 数据 

    上面演示的是一个新版本的oobinline.c程序,他会同时收接 内数据 与  数据 。同时他包含一个经过修改的SIGURG 信号处置器,这样他就会在紧迫数据 到达时报告。这就会答应我们观察多许事件。

        

    /*
    * oobinline.c
    *
    * OOB inline receiver:
    */
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <errno.h>
    #include <signal.h>
    #include <fcntl.h>
    #include <sys/ioctl.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    
    extern void bail(char *on_what);
    extern int BindAccept(char *addr);
    
    /*
    * SIGURG signal handler:
    */
    static void sigurg (int signo)
    {
       write(1,"[SIGURG ]/n",9);
       signal(SIGURG ,sigurg );
    }
    
    /*
    * Emulate the IEEE Std 1003.1g
    * standard function sockatmark(3):
    */
    static int Sockatmark(int s)
    {
       int z;
       int flag;
    
       z = ioctl(s,SIOCATMARK,&flag);
       if( z == -1 )
           return -1;
       return flag ? 1 : 0;
    }
    
    int main(int argc,char **argv)
    {
       int z;       /* Status */
       int s;       /* Socket */
       int oobinline= 1;   /* oob inline */
       char buf[256];
    
       /*
       * 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.1;
       */
       s = BindAccept(argc >= 2
               ? argv[1]
               : "127.0.0.1:9011");
    
       /*
       * Establish ownership:
       */
       z = fcntl(s,F_SETOWN,getpid());
       if(z==-1)
           bail("fcntl(2)");
    
       /*
       * Catch SIGURG :
       */
       signal(SIGURG ,sigurg );
    
       /*
       * Receive the OOB data inline:
       */
       z = setsockopt(s,
               SOL_SOCKET,
               SO_OOBINLINE,
               &oobinline,
               sizeof oobinline);
    
       if(z==-1)
           bail("setsockopt(2)");
    
       for(;;)
       {
           printf("/n[%s]/n",
                   Sockatmark(s)
                   ? "AT MARK"
                   : " NO MARK");
    
           z = recv(s,buf,sizeof buf,0);
           if(z==-1)
               bail("recv(2)");
           if(z==0)
               break;
           buf[z]=0;
    
           printf("rcv ''%s''(%d)/n",
                   buf,z);
       }
    
       close(s);
       return 0;
    }
    

        
    在现编译这个程序:

                                             M
    $ make oobinline
    gcc -c -D_GNU_SOURCE -Wall -Wreturn-type -g oobinline.c
                                              FL
    gcc oobinline.o mkaddr.o bindacpt.o -o oobinline
    $

    执行下列步骤来行进测试:

    1 在第一个终端会话中,启动oobinline程序。
    2 在第二个终端会话中,启动我们面前所用的oobsend程序。

    发送程序的终端会话出输如下所示:

    $ ./oobsend
    ib: ''In the beginning'' (16)
    ib: ''Linus begat Linux,'' (18)
    ib: ''and the Penguins'' (16)
    OOB ''rejoiced'' (8)
    ib: ''exceedingly.'' (12)
    $

    这个终端会话与面前的例子同相。然而,收接终端会话的出输如下所示:

    $ ./oobinline
    [No Mark]
    rev In the beginning (16)
    [No Mark]
    rev ''Linus begat Linux, (18)
    [No Mark]
    rev ''and the Penguins'' (16)
    [No Mark]
    [SIGURG ]
    rev ''rejoice'' (7)
    [AT MARK]
    rev ''d'' (1)
    [No Mark]
    rev ''exceedingly.'' (12)
    [No Mark]
    $

    注意,当收接字符串''rejoiced''时,与面前的例子相似也会启动SIGURG 信号。然而注意,标记在直到先取读''rejoice''节字才到达。然后到达标记,并且收接到额外的内联节字(d)。在这里须要注意几点:

    与没有用使内联紧迫数据 取读时一样,SIGURG 信号要尽早到达。
     内数据 必须在取读  数据 之前顺序取读。
    尽管所传送的数据 包作为一个整体包含个整''rejoiced''字符串,而recv函数会在紧迫数据 节字所处的位置停止(收接在d节字处停止)。
    接下来须要调用recv函数取读紧迫数据 。对于TCP ,在这个例子中只是一个单一节字。

    常通,数据 由一个流式套口接中取读,而不必指定信息边界。然而,我们会发现,当紧迫数据 由内联取读时,确实形成了一个边界。取读会在紧迫数据 处停止。如果不是这样,我们就会很容易读过标记。

    紧迫指针的制限 

    到在现为止,我们已演示了TCP 确实只供给了一个  数据 节字。这是因为他是用使协议的TCP 紧迫模式特性来实现的。

    我们很容易会为认TCP 紧迫模式及其紧迫指针应使得他可以标记紧迫数据 的边界。然而,际实上并非这样的,因为紧接着的带 外 数据 的发送会覆盖收接者原始的紧迫数据 标记,而这个标记也许还没有行进处置。 

    如果我们修改oobsend.c程序就可以行进演示。移除全部的sleep(3)函数调用,在oband(s,"rejoiced")之后插入一个oband(s,"very")调用。主程序看起来如下所示:

    int
    main(int argc,char **argv) {
        int s = -1;      /* Socket */
        s = Connect(argc >= 2
            ? argv[1]
            : "127.0.0.1:9011");
        iband(s,"In the beginning");
        iband(s,"Linus begat Linux,");
        iband(s,"and the Penguins");
        oband(s,"rejoiced");
        oband(s,"very");
        iband(s,"exceedingly.");
        close(s);
        return 0;
    }

    当再一次行运这个测试时,在一个倏地的系统上,我们会收到如下的结果:

    $ ./oobinline
    [No Mark]
    rcv ''In the beginning'' (16)
    [No Mark]
    rcv ''Linus begat Linux,'' (18)
    [No Mark]
    [SIGURG ]
    rcv ''and the Penguinsrejoicedver'' (27)
    [AT MARK]
    rcv ''yexceedingly.'' (13)
    [No Mark]

    在这里须要的注意的几点:
    只收接到一个SIGURG 信号。
    只有一个紧迫数据 标记,尽管在发送端写入了两个  数据 。
    在字符串''yexceedingly''中第一个y是一个  数据 节字。接下来的节字只是简单的随后发送的 内数据 节字。

    面前的测试依赖于sleep(3)所供给的到物理数据 控制集合的延迟。

    正 如我们面前的注意所指出的,我们的结果与许会与我们例子出输略有不同。当由一个低速的486系统向一个倏地的Pentium III 系统发送时会示显出更多的不同。当由一个倏地的CPU向一个慢速的CPU发送时会观察到另外一个收接模式,其他的因素会决定数据 包如何行进分割。

    用使select(2)处置带 外 数据 

    在这一章还有一些空间来探讨这个特殊的话题,但只是一些简单的建议看起来也许会更合适。

    对于select函数调用,  数据 的念概是一个异常。我们也许可以记起第11章,"并发客户端服务器",select会阻塞,直到上面的一个或是多个事件发生:

    一个读事件(要取读的数据 已到达)
    一个写事件(数据 在现可以写入)
    一个异常(  数据 到达)

    我们的程序可以在select调用中捕获这个异常。然后,可以在必要时用使设置了MSG_OOB标记位的recv函数来处置  数据 。

    文章结束给大家分享下程序员的一些笑话语录: 不会,Intel会维持高利润,也会维持竞争局面,国外的竞争不是打死对方的那种。你看日本有尼康,佳能,索尼,都做相机,大家都过得很滋润。别看一堆厂,其实真正控制的是后面的那几个财团——有些竞争对手,后面其实是一家人。

  • 相关阅读:
    孔曰成仁,孟曰取义
    mysql索引&实现原理
    MySQL存储引擎
    HashMap原理
    反射
    list对象属性排序
    mysql数据表操作&库操作
    mysql字段类型
    java线程池初步理解
    java四种内部类
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3041098.html
Copyright © 2011-2022 走看看