在写这篇文章之前,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所发生的事件队列为:

1 数据 放入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会维持高利润,也会维持竞争局面,国外的竞争不是打死对方的那种。你看日本有尼康,佳能,索尼,都做相机,大家都过得很滋润。别看一堆厂,其实真正控制的是后面的那几个财团——有些竞争对手,后面其实是一家人。