数据解释带外数据
在写这篇文章之前,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会维持高利润,也会维持竞争局面,国外的竞争不是打死对方的那种。你看日本有尼康,佳能,索尼,都做相机,大家都过得很滋润。别看一堆厂,其实真正控制的是后面的那几个财团——有些竞争对手,后面其实是一家人。
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号