在传输层,当连接的一端有很重要的情况发生时,会产生所谓out-of-band数据,这些数据的优先级最高,而不受block和flow control的影响,也不管正在排队的数据,要优先发送。
在UDP中没有实现。这里只看TCP。
TCP没有实现真实的out-of-band数据,而是提供一种urgent模式,如:send(fd, "a", 1, MSG_OOB);
此时的send buffer如下:
TCP's urgent pointer的序列号比OOB大1,这是历史因素。此时TCP要发送的下一个数据包的TCP头部的URG flag要设置,同时TCP头部的urgent offset field指向OOB的后一个字节。但是这个包有可能不包含OOB。OOB是否被发送受几个因素影响:OOB前面的字节数,包的尺寸,当前window大小。
在TCP层,urgent pointer和urgent offset是不同的。在TCP头中,urgent offset为16位,sequence number field加上urgent offset来包含32位的urgent pointer。只有URG被设置,TCP才处理urgent offset。这一点对程序是透明的。
当TCP进入urgent模式时,这种状态一定被发送,但是OOB数据不一定被发送。
send(fd, "abc", 3, MSG_OOB);
此时,TCP's urgent pointer指向数据的最后字节的后一个字节,"c"被当作out-of-band字节。
- 当对方收到一个设置了URG的包,会检查urgent pointer看是否包含out-of-band。有可能同时发送多个设置了URG的包,但是这些包的urgent pointer指向同一位置,所有这些包只有第一个会引发out-of-band到达的信号。
- 首先SIGURG信号被发送,然后如果select在block,select返回。如果新的OOB数据在老的数据之前到达,老的被丢弃。
- 如果SO_OOBINLINE被设置(默认不设置),OOB数据在receive buffer的左边,此时,不能设置MSG_OOB去读取数据。通过检查此连接的out-of-band mark,进程会知道OOB数据。
当SO_OOBINLINE没有设置,单独的字节不会放入receive buffer,此数据会放入one-byte out-of-band buffer,可以通过设置了MSG_OOB的recv,recvfrom,recvmsg来读取。当新的OOB到来时,在buffer中的老数据被替换。
可能有以下错误:
- 进程读取out-of-band数据,但是对方没有发送,EINVAL
- 进程读取out-of-band数据,但是数据还没发送过来,EWOULDBLOCK
- 进程多次读取同一份out-of-band数据,EINVAL
- 进程设置了SO_OOBINLINE,但通过MSG_OOB读取,EINVAL
#include <sys/socket.h>
int sockatmark(int sockfd) ;
设置了 out-of-band mark返回1,否则返回 0,出错返回-1
判断是否设置了out-of-band mark
int sockatmark(int fd)
{
int flag;
if (ioctl(fd, SIOCATMARK, &flag) < 0)
return (-1);
return (flag != 0);
}
out-of-band有以下几点:
- out-of-band mark总是指向数据最后一个字节的下一个字节。如果out-of-band数据被收到,并且是inline接收,如果读取的下一个字节是通过MSG_OOB发送的,sockatmark返回TRUE。如果SO_OOBINLINE没有被设置,读取的下一个字节是跟在OOB后面的第一个字节。
- 读操作在out-of-band mark会停止。如在receive buffer有100个字节,5个字节后跟着out-of-band mark,一个读取100字节的操作返回5个字节。这一特性使得进程可调用sockatmark得知是否buffer pointer指向mark。
- 即使被flow control阻止,TCP也会发送out-of-band通知
- 一个接收进程在OOB到达之前就可能被通知有out-of-band发生。如果此时调用MSG_OOB读取,EWOULDBLOCK返回