编程接口指南

带外数据

流套接字概念包括带外数据。带外数据是一对连接的流套接字之间的逻辑上独立的传输通道。带外数据的传送独立于一般数据。带外数据功能必须支持每次至少可靠传送一条带外消息。此消息至少可以包含一个字节的数据。可以随时暂挂传送至少一条消息。

使用带内信号发送时,紧急数据将与一般数据一同按顺序传送,并从一般数据流中提取消息。提取的消息将单独存储。用户可以在顺序接收紧急数据与无序接收紧急数据之间进行选择,而不必缓冲中间数据。

使用 MSG_PEEK,可以查看带外数据。如果套接字具有进程组,则在通知协议其存在时会生成 SIGURG 信号。进程可以将进程组或进程 ID 设置为使用适当的 fcntl(2) 调用传送 SIGURG,如 SIGIO中断驱动套接字 I/O中所述。如果多个套接字都具有等待传送的带外数据,则用于例外情况的 select(3C) 调用可以确定哪些套接字具有此类数据暂挂。

逻辑标记位于数据流中发送带外数据的位置。远程登录和远程 shell 应用程序使用此功能在客户机进程与服务器进程之间传播信号。接收到信号之后,将废弃数据流中标记之前的所有数据。

要发送带外消息,请将 MSG_OOB 标志应用于 send(3SOCKET)sendto(3SOCKET)。要接收带外数据,请将 MSG_OOB 指定给 recvfrom(3SOCKET)recv(3SOCKET)。如果带外数据是内嵌数据,则不需要 MSG_OOB 标志。SIOCATMARK ioctl(2) 指示读取指针当前是否指向数据流中的标记:

int yes;

ioctl(s, SIOCATMARK, &yes);

如果返回时 yes1,则下次读取将返回标记之后的数据。否则,假设带外数据已经到达,下次读取将提供由客户机在发送带外信号之前发送的数据。以下示例给出了远程登录进程中接收中断或退出信号时刷新输出的例程。此代码读取标记之前的一般数据以废弃这些一般数据,然后读取带外字节。

进程还可以读取或查看带外数据,而无需首先读取标记之前的数据。当底层协议将紧急数据与一般数据一起带内传送,并仅提前发送其存在的通知时,很难访问此数据。此类型协议的示例为 TCP,此协议用于在 Internet 系列中提供套接字流。如果使用此类协议,则使用 MSG_OOB 标志调用 recv(3SOCKET) 时,带外字节可能尚未到达。在这种情况下,此调用会返回 EWOULDBLOCK 错误。此外,输入缓冲区中的带内数据量可能会导致正常流控制阻止对等方发送紧急数据,直到清除缓冲区为止。然后,在对等方可以发送紧急数据之前,此进程必须读取足够的排队数据以清除输入缓冲区。


示例 7–10 接收带外数据时刷新终端 I/O

#include <sys/ioctl.h>

#include <sys/file.h>

...

oob()

{

  int out = FWRITE;

  char waste[BUFSIZ];

  int mark = 0;

 

  /* flush local terminal output */

  ioctl(1, TIOCFLUSH, (char *) &out);

  while(1) {

   if (ioctl(rem, SIOCATMARK, &mark) == -1) {

    perror("ioctl");

    break;

   }

   if (mark)

    break;

   (void) read(rem, waste, sizeof waste);

  }

  if (recv(rem, &mark, 1, MSG_OOB) == -1) {

   perror("recv");

   ...

  }

  ...

}

用于保留套接字流中紧急内嵌数据位置的工具可用作套接字级别选项 SO_OOBINLINE。有关使用情况,请参见 getsockopt(3SOCKET)。使用此套接字级别选项,可以保留紧急数据的位置。但是,将返回一般数据流中标记之后的紧急数据,而不带 MSG_OOB 标志。接收多个紧急指示时将移动标记,但是不会丢失任何带外数据。