未加星标

TCP带外数据详解

字体大小 | |
[系统(linux) 所属分类 系统(linux) | 发布者 店小二05 | 时间 2017 | 作者 红领巾 ] 0人收藏点击收藏

传输层协议使用带外数据(out-of-band,OOB)来发送一些重要的数据 ,如果通信一方有重要的数据需要通知对方时,协议能够将这些数据快速地发送到对方.为了发送这些数据,协议一般不使用与普通数据相同的通道,而是使用另外的通道.linux系统的套接字机制支持低层协议发送和接受带外数据.但是TCP协议没有真正意义上的带外数据.为了发送重要协议,TCP 提供了一种称为紧急模式(urgentmode)的机制.TCP协议在数据段中设置URG 位,表示进入紧急模式.接收方可以对紧急模式采取特殊的处理.很容易看出来,这种方式数据不容易被阻塞,可以通过在我们的服务器端程序里面捕捉SIGURG 信号来及时接受数据或者使用带OOB标志的recv函数来接受.

定义带外数据
想 像一下在银行人们排起队等待处理他们的帐单。在这个队伍中每个人最后都会移到前面由出纳员进行服务。现在想像一下一个走入银行,越过整个队伍,然后用枪抵 住出纳员。这个就可以看作为带外数据。这个强盗越过整个队伍,是因为这把枪给了他凌驾于众人的权力。出纳员也会集中注意力于这个强盗身上,因为他知道当前 的形势是很紧急的。
相应的,一个连接的流式套接口上的带外数据的工作原理也与此类似。通常情况下,数据由连接的一端流到另一端,并且认为 数据的所有字节都是精确排序的。晚写入的字节绝不会早于先写入的字节到达。然而套接口API概念性的提供了一些实用程序,从而可以使得一串数据无阻的先于 通常的数据到达接收端。这就是所谓的发送带外数据。
从技术上来说,一个TCP流不可以发送带外数据。而他所支持的只是一个概念性的紧急数据,这些紧急数据作为带外数据映射到套接口API。 这就带来了许多限制,这些我们会在后面进行讨论。
尽管我们可以立刻享受到在银行中越过整个队伍的利益,但是我们也会认识到使用枪来达到这样的目的是反社会的行为。一个TCP流通常希望以完美的队列来发送数据字节,那么乱序的发送数据就似乎与流的概念相违背。 那么为什么要提供带外数据的套接口方法呢?
也 许我们已经意识到了,有时数据会以一定的方式变得紧急。一个流套接口会有一个大量的数据队列等待发送到网络。在远程端点,也会有大量已接收的,却还没有被 程序读取的数据。 如果发送客户端程序由于一些原因需要取消已经写入服务器的请求,那么他就需要向服务器紧急发送一个标识取消的请求。如果向远程服务器发送 取消请求失败,那么就会无谓的浪费服务器的资源。
使 用带外数据的实际程序例子就是telnet,rlogin,ftp命令。前两个程序会将中止字符作为紧急数据发送到远程端。这会允许远程端冲洗所有未处理 的输入,并且丢弃所有未发送的终端输出。这会快速中断一个向我们屏幕发送大量数据的运行进程。ftp命令使用带外数据来中断一个文件的传输。
套接口与带外数据
重新强调套接口接口本身并不是限制因素是很重要的。带外数据的概念实际上映射到 TCP/IP通信的紧急数据模式。在今天,TCP流对于网络是很重要的,而在这一章我们仅专注于带外数据适应于TCP紧急数据的套接口使用。
实现上的变化
很不幸,TCP的实现在紧急数据就如何处理上有两种不同的解释。这些区别我们将会本章的后面进行详细的讨论。这些不同的解释是:
TCP紧急指针的RFC793解释
TCP紧急指针的BSD解释
现 在已经出现了平分的状态, 因为原始的TCP规格允许两种解释。从而,一个"主机需要"的RFC标识正确的解释。然而,大多数的实现都基于BSD源码,而在 今天BSD方法还是一个通用的用法。从支持两种解释的角度而言,Linux处于分裂的状态。然而,Linux默认使用BSD解释。
现在我们稍做停顿,来检测一个我们Linux系统的当前设置。这决了我们这一章的例子是否可以产生同样的结果。
$ cat /proc/sys/net/ipv4/tcp_stdurg
0
$
这里显示的输出为0。这表示当前起作的为BSD解释。如果我们得到其他的输出结果(例如1),那么如果我们希望得到也本章的例子相同的结果,我们应将其改为0。
下面列出了tcp_stdurg设置可能的取值。tcp_stdurg值可以在Shell脚本中进行查询和设置,包括启动与关闭脚本。
/proc/sys/net/ipv4_stdurg的设置值:
0 BSD解释(Linux默认)
1 RFC793解释
如果我们需要将其设置改为0,我们需要root权限,然后输入下面的命令:
# echo 0 >/proc/sys/net/ipv4/tcp_stdurg
#
进行双重检测总是很明知的,所以在改变以后再列出其值来确定改变是否为内核所接受。我们也可以在上面的例子中使用cat命令来显示0值。
编写带外数据
一个write调用将会写入一个我们已习惯的带内数据。相应的,必须使用一个新的函数来写入带外数据。为了这个目的,在这里列出send函数地原型:
#include <sys/types.h>
#include <sys/socket.h>
int send(int s, const void *msg, int len, unsigned int flags);
这个函数需要四个参数,分别为:
1 要写入的套接口s
2 存放要写入的消息的缓冲地址msg
3 消息长度(len)
4 发送选项flags
send函数与write函数相类似,所不同的只是提供了额外的flags参数。这是实际的部分。send函数返回写入的字节数,如果发生错误则会返回-1,检测errno可以得到错误原因。
要发送带外数据,与write调用相似,使用前三个参数。如果我们为flags参数指定了C语言宏MSG_OOB,则数据是作为带外数据发送的,而不是通常的带内数据,如下面的例子代码:
char buf[64]; /* Data */
int len; /* Bytes */
int s; /* Socket */
. . .
send(s,buf,len,MSG_OOB);
如果所提供的flags参数没有MSG_OOB位,那么数据是作为通常数据写入的。这就允许我们使用同一个函数同时发送带内数据与带外数据。我们只需要简单的在程序控制中改变flags参数值来达到这个目的。
读取带外数据
带外数据可以用两种不同的方法进行读取:
单独读取带外数据
与带内数据混合读取
为了与通常数据流分开单独读取带外数据,我们需要使用recv函数。如果我们猜想recv函数与read函数相似,只是有一个额外的flags参数,那么我们的猜想是正确的。这个函数的原型如下:
#include <sys/types.h>
#include <sys/socket.h>
int recv(int s, void *buf, int len, unsigned int flags);
recv函数接受四参数,分别为:
1 要从中接收数据的套接口s(带内数据或带外数据)
2 要放置所接收的数据的缓冲区地址buf
3 接收缓冲区的最大长度
4 调用所需的flags参数
正如我们所看到的,recv函数是与send函数调用相对应的函数。为要接收带外数据,在flags参数中指定C宏MSG_OOB。没有MSG_OOB标志位,recv函数所接收的为通常的带内数据,就像通常的read调用一样。
recv函数返回所接收到的字节数,如果出错则返回-1,检测errno可以得到错误原因。
下面的代码例子演示了如何读取带外数据:
char buf[128]; /* Buffer */
int n; /* No. of bytes */

本文系统(linux)相关术语:linux系统 鸟哥的linux私房菜 linux命令大全 linux操作系统

分页:12
转载请注明
本文标题:TCP带外数据详解
本站链接:http://www.codesec.net/view/532674.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 系统(linux) | 评论(0) | 阅读(15)