TCP带外数据

参考:《UNIX 网络编程 · 卷1 : 套接字联网API》
参考:《UNIX环境高级编程》

带外数据概念

带外数据(out-of-band data)是一些通信协议所支持的可选功能,与普通数据相比, 它允许更高优先级的数据传输。带外数据先行传输,即使传输队列已经有数据。TCP 支持带外数据,但是 UDP 不支持。套接字接口对带外数据的支持很大程度上受 TCP 带外数据具体实现的影响。

假设一个进程已经往一个 TCP 套接字写出 N 字节数据,而且 TCP 把这些数据排队在该套接字的发送缓冲区中,等着发送到对端,该进程接着以 MSG_OOB 标志调用 send 函数写出一个函数 ASCII 字符 a 的单字节带外数据:

send(fd, "a", 1, MSG_OOB);

TCP 把这个数据放置在该套接字发送缓冲区的下一个可用位置,并把该连接的 TCP 紧急指针设置成再下一个可用位置。如下如,展示了此时的缓冲区,并且把带外字节标记为“OOB”。

TCP带外数据

发送端 TCP 将为待发送的下一个分节在 TCP 首部中设置 URG 标志,并把紧急编译字段设置为指向带外字节之后的字节,不过该分节可能不含我们标记为 OOB 的那个字节。OOB 字节是否发送取决于在套接字发送缓冲区先于它的字节数、TCP 准备发送给对端分节大小以及对端通告的当前窗口。

一个 TCP 紧急模式的一个重要的特点:即使发送端 TCP 因为流量控制而暂停发送数据,紧急通知照样不伴随任何数据地发送,紧急通知总是无障碍地发送到对端 TCP。

如下示例:

如果我们发送多个字节,情况又会怎么样:

send(fd, "abc", 3, MSG_OOB);

其中字母 c 被认为是带外数据,下面从接收端来看:

  1. 当收到一个设置了 URG 标志地分节时,接收端 TCP 检查紧急指针,确定它是否指向新的带外数据。发送端通常在一小段时间内往往发送多个含有 URG 标志且紧急指针指向同一个数据字节地分节,但只有第一个到达地会导致通知接收进程有新的带外数据到达。
  2. 当有新的紧急指针到达时,接收进程被通知到。首先内核给接受套接字地属主进程发送 SIGURG 信号,前提是接收进程曾调用了 fcntl 或 ioctl 为这个套接字建立了属主,而且已经为这个信号建立了信号处理函数。如果接受进程阻塞在 select 中等待这个套接字描述符出现一个异常条件,select 调用就返回。一旦新的紧急指针到达,不论由紧急指针指向的实际数据字节是否已经到达接收端 TCP,这两个潜在通知进程的手段就发生动作。只有一个 OOB 标记,如果新的 OOB 字节在旧的 OOB 字节被读取之前就到达,旧的 OOB 字节就会被丢弃。
  3. 当紧急指针指向实际数据字节到达接收端 TCP 时,该数据字节即可能被拉出带外,也可能留在带内。SO_OOBINLINE 套接字选项默认情况下是禁止的,对于这样的接收端套接字,该数据字节并不放入套接字接收缓冲区。而是放入该连接的一个独立的单字节带外数据缓冲区。接收进程从这个单字节缓冲区读入数据地唯一方法是是指定 MSG_OOB 标志调用 recv、recvfrom、recvmsg。

然而如果接收进程开启了 SO_OOBINLINE 套接字选项,那么由 TCP 紧急指针指向的实际数据字节将被留在通常地套接字接受缓冲区中。这种情况下,接收进程不能指定 MSG_OOB 标志读入该数据字节。

简单的说,AUPE 中将其描述如下:

TCP 将带外数据称为紧急数据(urgent data)。TCP 仅支持一个字节的紧急数据,但是允许紧急数据在普通数据传递机制数据流之外传输。为了产生紧急数据,可以在 3 个 send 函数中的任何一个里指定 MSG_OOB 标志。如果带 MSG_OOB 标志发送的字节数超过一个时,最后一个字节将被视为紧急数据字节。

如果通过套接字安排了信号的产生,那么紧急数据被接收时,会发送 SIGURG 信号。 在 fcntl 中使用 F_SETOWN 命令来设置一个套接字的所有权。如果 fcntl 中的第三个参数为正值,那么它指定的就是进程 ID。如果为非 -1 的负值,那么它代表的就是进程组 ID。因此,可以通过调用以下函数安排进程接收套接字的信号:

fcntl(sockfd, F_SETOWN, pid);

F_GETOWN 命令可以用来获得当前套接字所有权。对于 F_SETOWN 命令,负值代表进程组 ID,正值代表进程 ID。因此,调用:

owner = fcntl(sockfd, F_GETOWN, 0);

将返回 owner,如果 owner 为正值,则等于配置为接收套接字信号的进程的 ID。如果 owner为负值,其绝对值为接收套接字信号的进程组的ID。

TCP支持紧急标记(urgent mark)的概念,即在普通数据流中紧急数据所在的位置。 如果采用套接字选项SO_OOBINLINE,那么可以在普通数据中接收紧急数据。为帮助判断是否已经到达紧急标记,可以使用函数sockatmark。

#include <sys/socket.h>
int sockatmark(int sockfd);

返回值:若在标记处,返回 1;若没在标记处,返回 0;若出错,返回 -1。当下一个要读取的字节在紧急标志处时,sockatmark 返回 1。

当带外数据出现在套接字读取队列时,select 函数会返回一个文件描述符并且有一个待处理的异常条件。可以在普通数据流上接收紧急数据,也可以在其中一个 recv 函数中采用 MSG_OOB 标志在其他队列数据之前接收紧急数据。TCP 队列仅用一个字节的紧急数据。如果在接收当前的紧急数据字节之前又有新的紧急数据到来,那么已有的字节会被丢弃。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值