注意:2011-01, TCP的紧急数据已经是废弃的功能了。不建议新的应用程序使用,只为旧程序兼容而存在。
1. 紧急标志
当你再次看到图 1 时,相信你已经无比的亲切,再观察下面彩色的 6 个标志位,有 5 个你已经熟知了,还剩下最后一个 URG,对,就是图 1 中那个鲜红的,醒目的那个位置。
除此之外,还有一个字段—— 16 位紧急指针,它正是配合 URG 标志位一起使用的,言外之意就是这个字段只有在 URG 被置位时才有意义。因为只有一个紧急指针,这也意味着它只能标识一个字节的数据。这个指针指向了紧急数据最后一个字节的下一个字节。
我们知道 TCP 在传输数据时是有顺序的,它有字节号,URG 配合紧急指针,就可以找到紧急数据的字节号。紧急数据的字节号公式如下:
紧急数据字节号(urgSeq)=TCP报文序号(seq)+紧急指针(urgpoint)−1
比如图中的例子,如果 seq = 10, urgpoint = 5, 那么字节序号 urgSeq = 10 + 5 -1 = 14.
知道了字节号后,就可以计算紧急数据字位于所有传输数据中的第几个字节了,如果从第 0 个字节开始算起,那么紧急数据就是第 urgSeq - ISN - 1 个字节(还记得 ISN 吗,它表示初始序列号),减 1 表示不包括第一个 SYN 段,因为一个 SYN 段会消耗一个字节号。
2. 紧急数据的作用
紧急标志可以用来通知对端:我放了一个紧急数据在数据流中,你看着办吧!这个特性往往可以来达到通知的目的。
一旦 TCP 知道了你要发送紧急数据,那么在接下来的数据发送中,TCP 会将所有的 TCP 报文段中的 URG 标志置位,哪怕该报文段中不包含紧急数据,这个行为会持续到紧急数据被发送出去为止。也就是接下来发送的TCP报文的URG都被置为1,直到紧急数据被发送出去
TCP协议没有真正意义上的带外数据.为了发送重要协议,TCP提供了一种称为紧急模式(urgentmode)的机制.TCP协议在数据段中设置URG位,表示进入紧急模式.接收方可以对紧急模式采取特殊的处理.很容易看出来,这种方式数据不容易被阻塞,可以通过在我们的服务器端程序里面捕捉SIGURG信号来及时接受数据或者使用带OOB标志的recv函数来接受。
也 许我们已经意识到了,有时数据会以一定的方式变得紧急。一个流套接口会有一个大量的数据队列等待发送到网络。在远程端点,也会有大量已接收的,却还没有被 程序读取的数据。如果发送客户端程序由于一些原因需要取消已经写入服务器的请求,那么他就需要向服务器紧急发送一个标识取消的请求。如果向远程服务器发送 取消请求失败,那么就会无谓的浪费服务器的资源。
使用带外数据的实际程序例子就是telnet,rlogin,ftp命令。前两个程序会将中止字符作为紧急数据发送到远程端。这会允许远程端冲洗所有未处理 的输入,并且丢弃所有未发送的终端输出。这会快速中断一个向我们屏幕发送大量数据的运行进程。ftp命令使用带外数据来中断一个文件的传输。
3. 紧急数据的发出
3.1 接口:send(sockfd,"X", 1, MSG_OOB);
设当前的sockfd的发送缓冲区为: Send_Buff: 1 2 3 4 5 6 7 8
- 调用send(sockfd,"X", 1,MSG_OOB);发出紧急数据X,那么发送缓冲区为:
Send_Buff: 1 2 3 4 5 6 7 8 X - Tcp->IP:
-
情况1: 发送窗口大于等于9,那么紧急数据刚好在这次被发出,tcp包头部URG被置位,紧急指针值为10(seq)。
-
情况2:
- 发送窗口小于9,那么由流式机制可知,先要将X前面的字节发出。假设窗口为6,第一个tcp包发出,URG被置位,紧急指针值为10(seq)。数据部分为:1 2 3 4 5 6。
- 第二个tcp包发出,URG被置位,紧急指针值为10(seq)。数据部分为:7 8 X。
对端收到第一个tcp包时,检测URG可知进入了紧急状态,但是有当前的seq和接受的数据量即之和小于紧急指针的值,那么说明紧急数据还未到来。下一个包到来时,判定紧急指针与前一包是相同的值,说明是同一个紧急指针,同理通过判定指针和当前接受的数据量来确定紧急数据是否到来。
假设当前的紧急指针和前一个包的紧急指针不是一个值,那么说明有新的紧急数据到来了。 总结:URG指定程序进入紧急状态,紧急数据的发出还是在对端的发送缓冲中依据流形式发出。所以可能紧急数据要推迟到来。
3.2 奇葩的:send(sockfd,"90X", 1,MSG_OOB);
发送缓冲区为:1 2 3 4 5 6 7 8 9 0 X
由于紧急指针只能标识一个字节的数据,所以只有X为紧急数据,90将被视为正常数据。
4. 紧急数据的接收端:
- 接收端接受到带有URG标志的数据段时:
判断当前的紧急指针与先前紧急指针(之前数据段)是否相同。相同则是因为tcp数据包可能被分成多个数据段,或者发送端在发出紧急数据前还有额外的正常数据发送。不同,则说明进入新的紧急状态,从而通知用户进程(sigurg),即:新的第一个紧急指针才会通知用户进程。 - 用户进程中,SIGURG信号被传递给套接字的宿主。套接字的宿主设置有:
fcntl(sockfd,F_SETOWN, getpid());
- 紧急指针所指向的带外数据到达接收端后,带外数据被放置的位置分为两种情况:
3.1 套接字选项SO_OOBINLINE未被设置(默认)
这一字节的带外数据被放置在套接字带外数据缓冲区中,这个缓冲区大小为一个字节。进程读取方法:recv(sockfd,&ch,1,MSG_OOB);
在紧急状态下,上述调用对于:带外数据仍未到达,函数返回回EWOULDFBLOCK。 在非紧急状态下,调用上述函数,将返回EINVAL。
3.2 套接字选项SO_OOBINLINE被设置,则由紧急指针所指的,代表带外数据的那个字节将会被放在正常套接字缓冲区中的最左边。在这种情况下,进程不能指定MSG_OOB 来读取这个一个字节的带外数据。这种方式在读取数据前需要提前判定下一字节是否为带外数据。ioctl (fd,SIOCATMARK,&flag); if (flag) read(sockfd,&ch,1);
SO_OOBINLINE模式原理:当带外数据到达接受缓冲区后,套接字有特殊字段记录该紧急数据在接受缓冲区中与缓冲区头部的偏移量。当应用程序读取前面的正常数据时,该偏移量将作出修正。当偏移量为0时,表示即前被读取的字节是带外数据,此时套接字状态字段的SIOCATMARK置1。
参考
作者:YDDMAX_Y
链接:https://www.jianshu.com/p/65a4b8c059d4
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。