1.概述
带外数据被认为具有比普遍数据更高的优先级。带外数据并不要求在客户和服务器之间在使用一个连接,而是被映射到已有的连接中
2.TCP带外数据
TCP没有真正的带外数据,而是提供了一个我们要讨论的紧急模式(urgent mode)。假设一个进程已向一个TCP套接口写入了N字节数据,并且这些数据被TCP放入套接口发送缓冲区等待发送给对方。下图展示了这种状态,并且标记了从1到N的数据字节。
进程现在使用send函数和MSG_OOB标志发送一个包含ASCII字符a的带外数据字节:
send(fd, "a", 1, MSG_OOB);
TCP将数据放置在套机口发送缓冲区的下一个可用位置,并设置这个连接的TCP紧急指针(urgent pointer)为下一个可用位置。下图中展示了这种状态,并且标记带外字节为“OOB”
给定如图所示的TCP套接口发送缓冲区状态,由TCP发送的下一个分节将会在TCP头部中设置URG标志,并且头部中的紧急偏移(urgent offset)字段也将指向带外字节后的字节。但是这个分节可以含有也可以不含有我们标记的OOB字节。是否发送OOB字节取决于在套接口发送缓冲区它前面的字节数、TCP发送给对方的分节长度以及对方通告的当前窗口
3.sockatmark函数
每当接收到带外数据时,就有一个相关联的带外标记。这是发送进程发送带外字节时在发送方普通数据流中的位置。接收进程读套接口时通过调用sockatmark函数确定是否在带外标记上。
#include <sys/socket.h>
int sockatmark(int sockfd) ;
//返回值:如果在带外标记上为1, 不在标记上为0, 出错为-1
下面给出了使用的SIOCATMARK ioctl完成本函数的一个实现
#include "unp.h"
int sockatmark(int fd)
{
int flag;
if (ioctl(fd, SIOCATMARK, &flag) < 0)
return (-1);
return (flag != 0);
}
不管接收进程在线(SO_OOBINLINE套接口选项)或是带外(MSG_OOB标志)接收带外数据,带外标记都能使用。
4.两个特性
- 带外标记总是指向刚好越过普通数据最后一个字节的地方。这意味着,如果带外数据在线接收,那么如果下一个要读的字节是被用MSG_OOB标志发送的,sockatmark就返回真。相反,如果SO_OOBINLINE套接口选项没有设置,那么如果下一个字节是跟在带外数据后发送的第一个字节,sockatmark就返回真。
- 读操作总是会停在带外标记上,也就是,如果在套接口接收缓冲区中有100个字节,但带外标记前只有5个字节,进程执行read请求100字节,则只有标记前的5个字节返回。这种在标记处的强制停止允许进程调用sockatmark确定是否缓冲区指针在标记处。
5.TCP带外数据小结
当我们考虑可能出现的定时问题时,带外数据会变得繁杂。要考虑的第一点是带外数据的概念实际上传递给接收者三条不同的信息。
- 发送者进入紧急模式的事实,接收进程可以用SIGURG信号或者select得到通知。这种通知在发送者发送带外数据后立即传输,因为在图21.11中我们看到即使发送数据给接收者因TCP的流控而停止了,TCP仍发送这种通知。这种通知可能导致接收者进入某种特殊处理模式,以处理收到的后继数据。
- 带外字节的位置,也就是说相对于发送者的其余数据,它是在哪儿发出的带外标记。
- 带外字节的实际值。由于TCP是一个字节流协议,它不解释应用进程发送的数据,因此这可以是任何8位值
对于TCP的紧急模式,我们可以认为URG标志是通知,紧急指针是标记,数据字节是其本身。
和这种带外数据相关的问题是
(a)每个连接只有一个TCP紧急指针
(b)每个连接只有一个带外标记
(c)每个连接只有一个字节的带外缓冲区