带外数据OOB与紧急模式URG

A,TCP支持带外数据OOB?与紧急模式URG有什么关系?
     TCP
支持带外数据,但是只有一个OOB字节,TCP的带外数据是通过紧急模式URG实现的
.
B,
我们知道send(sendfd,"ABC",3,MSG_OOB),将发送3个字节的带外数据OOB数据.但是这里TCP又只支持一个字节的OOB,难道丢掉2个字节
?
     TCP
将把紧急模式URG 置位,紧急指针定位第三个字节("C")(这里不管它的具体位置,紧急指针的作用就是提供定位那个OOB字节的信息),前两个字节("AB")当作普通字节发送.其实TCP总是把最后一个字节当作OOB数据,其他的当作普通字节.不管你通过带MSG_OOB标志的sendxxx函数发送多少字节带外数据OOB数据,发送端只把最后一个字节当作OOB数据,接收端也只能收到一个字节的OOB数据
.
C,
如果一定要发送多字节的带外数据,让接收端能一次收到多个字节的带外数据.能不能做到
?
     
对于TCP协议,不能
!
D,
对于TCP,收到的带外数据怎么保存
?
     
两种模式
:
     1,
OOBINLINE 模式,这是套接字的默认模式,OOB字节与普通字节分开存放.存放在一个OOB缓冲区中,当然TCP只有一个字节,可以用一个字节保存OOB数据
.
     2,OOBINLINE 
模式,OOB字节和普通字节一起存放,它和普通字节本来就是一起发送,当然可以一起存放
.
E,recv(recvfd,buff,256,MSG_OOB).
会有哪些结果
?
     recvxxxx函数,在MSG_OOB模式下,将在OOB缓冲区中寻找数据。
     如果发送端没发送OOB字节,它返回错误.
     
如果发送端发送了OOB字节
:
     1,
对于非OOBINLINE 模式,它返回1字节的OOB数据
.
     2,
对于OOBINLINE 模式,它返回错误.因为OOB字节没有放到OOB缓冲区中
.
F,
如果发送端使用MSG_OOB模式,send(sendfd,sndbuff,64,MSG_OOB),发送了包含"OOB字节"64字节数据,然后用非MSG_OOB模式,send(sendfd,sndbuff,64,0)发送64字节,当接收端收到64+64字节的数据后,recv(recvfd,revbuff,256,0).会有哪些结果
?
1,
对于非OOBINLINE 模式,第一次recv(recvfd,revbuff,256,0)只返回前63字节的普通数据,接收缓冲区剩下64字节.要获得1字节的OOB数据,必须使用MSG_OOB模式的revxxx函数.再次recv(recvfd,revbuff,256,0),返回第二次发送的64字节.一次recvxxx不跨越urg-mark标记
.
2,
对于OOBINLINE 模式,第一次recv(recvfd,revbuff,256,0)只返回前63字节的普通数据,接收缓冲区剩下65字节(OOB+64字节),第二次recv(recvfd,revbuff,256,0),对于windows,只返回一字节的OOB字节,需要第三次rev才能返回最后的64字节,对于linux/unix,第二次rev 就返回65字节(OOB+64字节).总之与协议栈的实现有关
.
G,
如果OOB字节没被应用程序读取,协议栈又收到了新的OOB字节,会出现什么情况
?
    TCP
协议对每个socket保持一个URG指针,此时直接刷新URG指针,指向新的OOB字节
.
对于非OOBINLINE,旧的OOB字节直接被丢弃,被新的OOB字节覆盖
.
对于OOBINLINE,旧的OOB字节仍然在接收缓冲区中,但被当着普通数据看待,每个socket只有一个URG指针,只能定位一个OOB字节.

许多传输层都支持带外数据(Out-Of-Band data),有时候也称为快速数据(Expedited
Data).之所以有带外数据的概念,是因为有时候在一个网络连接的终端想“快速”的告诉
网络另一边的终端一些信息.这个“快速”的意思是我们的“提示”信息会在正常的网络
数据(有时候称为带内数据In-Band data)之前到达网络另一边的终端.这说明,带外数
据拥有比一般数据高的优先级.但是不要以为带外数据是通过两条套接字连接来实现的.事
实上,带外数据也是通过以有的连接来传输。
不幸的是,几乎每个传输层都有不同的带外数据的处理方法。我们下面研究的是TCP
模型的带外数据,提供一个小小的例子来看看它是怎样处理套接字的带外数据,及调用套
接字API 的方法。
流套接字的抽象中包括了带外数据这一概念,带外数据是相连的每一对流套接字间一
个逻辑上独立的传输通道。带外数据是独立于普通数据传送给用户的,这一抽象要求带外
数据设备必须支持每一时刻至少一个带外数据消息被可靠地传送。这一消息可能包含至少
一个字节;并且在任何时刻仅有一个带外数据信息等候发送。对于仅支持带内数据的通讯
协议来说(例如紧急数据是与普通数据在同一序列中发送的),系统通常把紧急数据从普
通数据中分离出来单独存放。这就允许用户可以在顺序接收紧急数据和非顺序接收紧急数
据之间作出选择(非顺序接收时可以省去缓存重叠数据的麻烦)。在这种情况下,用户也
可以“偷看一眼”紧急数据。
某一个应用程序也可能喜欢线内处理紧急数据,即把其作为普通数据流的一部分。这
可以靠设置套接字选项中的SO_OOBINLINE 来实现。在这种情况下,应用程序可能希望
确定未读数据中的哪一些是“紧急”的(“紧急”这一术语通常应用于线内带外数据)。为
了达到这个目的,在Sockets 的实现中就要在数据流保留一个逻辑记号来指出带外数据从
哪一点开始发送.
select()函数可以用于处理对带外数据到来的通知。
6.11.1 TCP 的带外数据
TCP 上没有真正意义上的“带外数据”。TCP 是由一种叫做“紧急模式”的方法来传
输带外数据的。假设一个进程向一个TCP 套接字写入了N 个字节的数据,数据被TCP 套
接字的发送缓冲区缓存,等待被发送到网络上面.我们在图6-10 可以看见数据的排列。
第6 章berkeley 套接字- 191 -
图6-10 TCP 数据的排列
现在进程使用以MSG_OOB 为参数的send()函数写入一个单字节的"带外数据",包
含一个ASCII 字符"a":
send(fd, “a”, 1, MSG_OOB);
TCP 将数据放在下一个可用的发送缓冲区中,并设置这个连接的"紧急指针"(urgent
pointer)指向下一个可用的缓冲区空间.图6-11 表示了我们描述的这个状态,并将带外数
据(Out-Of-Band)表示为"OOB"。
图6-11 ODB 数据
TCP 的紧急指针的指向的位置是在程序发送的OOB 数据的后面。
由图6-11 所表示的TCP 套接字的状态,得知下一个将要发送的数据是TCP 的URG
(Urgent pointer)标志,发送完URG 标志,TCP 才会发送下面的带外数据的那个字节。
但是TCP 所一次发送的数据中,可能只包含了TCP 的URG 标志,却没有包含我们所发送
的OOB 数据.是否会发生这种情况而取决于TCP 将要发送的数据队列中,在OOB 数据
之前的数据的多少。如果在一次发送中,OOB 前的数据已经占满了名额,则TCP 只会发
送URG 标志,不会发送OOB数据
这是一个TCP 紧急数据状态的重要特性:TCP 的信息头指出发送者进入了紧急模式(比
方说,在紧急偏移处设置了URG 标志),但是紧急偏移处的数据并没有必要一定要发送出
去.事实上,如果一个TCP 套接字的流传送停止后(可能是接收方的套接字的接收缓冲区
没有空余空间),为了发送带外数据,系统会发送不包含数据的TCP 数据包,里面标明这
是一个带外数据.这也是我们使用带外数据的一个有利点:TCP 连接就算是在不能向对方
- 192 - Linux网络编程
发送数据的时候,也可以发送出一个带外数据的信号。
如果我们像下面这样发送一个多字节的带外数据:
send(fd, “abc”, 3, MSG_OOB);
在这个例子中, TCP 的紧急指针指向了数据最后一位的后面, 所以只有最后一位数
据(“c”)才被系统认为是“带外数据”。
//
//
我们上面大致了解了发送方是怎样发送“带外数据”的了,下面我们来看一看接收方
是怎样接收“带外数据”的。
1.当TCP 收到一个包含URG 标志的数据段时,TCP 会检查“紧急指针”来验证是
否紧急指针所指的数据已经到达本地。也就是说,无论这次是否是TCP 紧急模式从发送方
到接收方的第一次传输带外数据。一般来说,TCP 传输数据会分成许多小的数据包来传输
(每个包的到达时间也不同)。可能有好几个数据包中都包含紧急指针,但是这几个包中
的紧急指针都是指向同一个位置的,也就是说多个紧急指针指向一个数据。需要注意的是,
对于这一个带外数据,虽然有多个指针指向它,但是只有第一个紧急指针会通知程序注意。
2.接收进程收到另外一个带外数据的通知的条件是:有另外一个带外数据的指针到
达.注意这里是“另外一个带外数据”的指针,不是上面的“一个带外数据”的另外一个
指针。首先, SIGURG 信号回发送给套接字的属主,这个取决于是否已经使用fcntl()函数
或ioctl()函数设定套接字的属主和这个程序对SIGURG 信号的具体操作函数。其次,如果
一个程序正阻塞与对这个套接字描述符的select()函数的调用中,则select()函数会产生一个
例外,然后返回。
注意:进程收到带外数据的通知的时候,并不会在乎带外数据的真正数据是否到达。
3.当紧急指针所指的真正的带外数据通过TCP 网络到达接收端的时候,数据或者被
放入带外数据缓冲区或是只是简单的和普通的网络数据混合在一起。在缺省的条件下,
SO_OOBINLINE 套接字选项是不会被设置的,所以这个单字节的带外数据并没有被防在
套接字的接收缓存区中,而是被放入属于这个套接字的一个单独的带外数据缓存区中。如
果这个进程想读取这个带外数据的具体内容的话,唯一的办法就是调用recv,recvfrom,
或是recvmsg 函数,并且一定要指定MSG_OOB 标志。
4.如果一个进程将套接字设置为SO_OOBINLINE 属性,则由紧急指针所指的,代表
带外数据的那个字节将回被放在正常套接字缓冲区中的最左边.在这种情况下,进程不能
指定MSG_OOB 来读取这个一个字节的带外数据,但是它可以知道带外数据的到达时间:
通过检查套接字的带外数据标记.
有可能发生的一些错误:
5.如果当连接没有发送带外数据的时候进程来读取带外数据(比如说,通过MSG_OOB
参数来接收函数),则EINVAL 将会被返回。
6.当真正的带外数据到达之前的时候,进程被通知(SIGURG 或是select 函数)有带
外数据到达(也就是说带外数据的通知信号已经到达),如果进程尝试读取带外数据,则
返回EWOULDFBLOCK .进程所能做的只是去读取套接字的接收缓存区.(也许,由于
缓存区的数据以满,带外数据的那个字节信息无法传输过来,这样的话也许你需要清理一
下接收缓存区来给带外数据空出一些空间)
7.如果进程尝试多次读取同一个带外数据,则EINVAL 将会被返回。
8.如果进程将套接字属性设置为SO_OOBINLINE ,然后尝试通过指定MSG_OOB
第6 章berkeley 套接字- 193 -
标志来读取带外数据,则EINVAL 将会被返回。
下面我们将前面的套接字例程做一些变动来测试带外数据的发送与接收.
6.11.2 OOB 传输套接字例程(服务器代码Server.c)
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
/* 服务器要监听的本地端口*/
#define MYPORT 4000
/* 能够同时接受多少没有accept 的连接*/
#define BACKLOG 10
void
sig_urg(int signo);
main()
{
/* 在sock_fd 上进行监听,new_fd 接受新的连接*/
int sock_fd, new_fd ;
/* 用于存储以前系统缺省的SIGURL 处理器的变量*/ void * old_sig_urg_handle ;
/* 自己的地址信息*/
struct sockaddr_in my_addr;
/* 连接者的地址信息*/
struct sockaddr_in their_addr;
int sin_size;
int n ;
char buff[100] ;
/* 这里就是我们一直强调的错误检查.如果调用socket() 出错,则返回*/
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
- 194 - Linux网络编程
/* 输出错误提示并退出*/
perror(“socket”);
exit(1);
}
/* 主机字节顺序*/
my_addr.sin_family = AF_INET;
/* 网络字节顺序,短整型*/
my_addr.sin_port = htons(MYPORT);
/* 将运行程序机器的IP 填充入s_addr */
my_addr.sin_addr.s_addr = INADDR_ANY;
/* 将此结构的其余空间清零*/
bzero(&(my_addr.sin_zero), 8);
/* 这里是我们一直强调的错误检查!! */ if (bind(sockfd, (struct sockaddr *)&my_addr,
sizeof(struct sockaddr)) == -1)
{
/* 如果调用bind()失败,则给出错误提示,退出*/
perror(“bind”);
exit(1);
}
/* 这里是我们一直强调的错误检查!! */
if (listen(sockfd, BACKLOG) == -1)
{
/* 如果调用listen 失败,则给出错误提示,退出*/
perror(“listen”);
exit(1);
}
/* 设置SIGURG 的处理函数 sig_urg */
old_sig_urg_handle = signal(SIGURG, sig_urg);
/* 更改connfd 的属主*/
fcntl(sockfd, F_SETOWN, getpid());
while(1)
{
第6 章berkeley 套接字- 195 -
/* 这里是主accept()循环*/
sin_size = sizeof(struct sockaddr_in);
/* 这里是我们一直强调的错误检查!! */
if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1)
{
/* 如果调用accept()出现错误,则给出错误提示,进入下一个循环*/
perror(“accept”);
continue;
}
/* 服务器给出出现连接的信息*/
printf(“server: got connection from %s/n”, inet_ntoa(their_addr.sin_addr));
/* 这里将建立一个子进程来和刚刚建立的套接字进行通讯*/
if (!fork())
/* 这里是子进程*/
while(1)
{
if((n = recv(new_fd, buff, sizeof(buff)–1)) == 0)
{
printf(“received EOF/n”);
break ;
}
buff[n] = 0 ;
printf(“Recv %d bytes: %s/n”, n, buff);
}
/* 关闭new_fd 代表的这个套接字连接*/
close(new_fd);
}
}
/* 等待所有的子进程都退出*/
while(waitpid(-1,NULL,WNOHANG) > 0);
/* 恢复系统以前对SIGURG 的处理器*/
signal(SIGURG, old_sig_urg_handle);
}
void
sig_urg(int signo)
{
- 196 - Linux网络编程
int n;
char buff[100] ;
printf(“SIGURG received/n”);
n = recv(new_fd, buff, sizeof(buff)– 1, MSG_OOB);
buff [ n ] = 0 ;
printf(“recv %d OOB byte: %s/n” , n,buff);
}
6.11.3 OOB 传输套接字例程(客户端代码Client.c)
下面是客户端程序:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
/* 服务器程序监听的端口号*/
#define PORT 4000
/* 我们一次所能够接收的最大字节数*/
#define MAXDATASIZE 100
int
main(int argc, char *argv[])
{
/* 套接字描述符*/
int sockfd, numbytes;
char buf[MAXDATASIZE];
struct hostent *he;
/* 连接者的主机信息*/
struct sockaddr_in their_addr;
/* 检查参数信息*/
if (argc != 2)
{
第6 章berkeley 套接字- 197 -
/* 如果没有参数,则给出使用方法后退出*/
fprintf(stderr,“usage: client hostname/n”);
exit(1);
}
/* 取得主机信息*/
if ((he=gethostbyname(argv[1])) == NULL)
/* 如果gethostbyname()发生错误,则显示错误信息并退出*/
herror(“gethostbyname”);
exit(1);
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
/* 如果socket()调用出现错误则显示错误信息并退出*/
perror(“socket”);
exit(1);
}
/* 主机字节顺序*/
their_addr.sin_family = AF_INET;
/* 网络字节顺序,短整型*/
their_addr.sin_port = htons(PORT);
their_addr.sin_addr = *((struct in_addr *)he->h_addr);
/* 将结构剩下的部分清零*/
bzero(&(their_addr.sin_zero), 8);
if(connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1)
{
/* 如果connect()建立连接错误,则显示出错误信息,退出*/
perror(“connect”);
exit(1);
}
/* 这里就是我们说的错误检查! */
if (send(new_fd, “123”, 3, 0) == -1)
{
/* 如果错误,则给出错误提示,然后关闭这个新连接,退出*/
perror(“send”);
close(new_fd);
- 198 - Linux网络编程
exit(0);
}
printf(“Send 3 byte of normal data/n”);
/* 睡眠1 秒*/
sleep(1);
if (send(new_fd, “4”, 1, MSG_OOB)== -1)
{
perror(“send”);
close(new_fd);
exit(0);
}
printf(“Send 1 byte of OOB data/n”);
sleep(1);
if (send(new_fd, “56”, 2, 0) == -1)
{
perror(“send”);
close(new_fd);
exit(0);
}
printf(“Send 2 bytes of normal data/n”);
sleep(1);
if (send(new_fd,“7”, 1, MSG_OOB)== -1)
{
perror(“send”);
close(new_fd);
exit(0);
}
printf(“Send 1 byte of OOB data/n”);
sleep(1);
if (send(new_fd, “89”, 2, MSG_OOB)== -1)
{
perror(“send”);
close(new_fd);
exit(0);
}
printf(“Send 2 bytes of normal data/n”);
sleep(1);
第6 章berkeley 套接字- 199 -
close(sockfd);
return 0;
}
6.11.4 编译例子
注意:你显然需要在运行client 之前启动server.否则client 会执行出错(显示“Connection
refused”).
当只有一个连接的时候(因为这个服务器是多进程的,所以如果有多个连接同时存在
可能会导致屏幕输出混乱),可以得到下面的结果:(注意是使用我们下面的客户程序来连
接的,并且假设你运行我们的服务器程序是在本地机器上面)
root@bbs# gcc –o server server.c
root@bbs# gcc –o client client.c
root@bbs# ./server
root@bbs# ./client 127.0.0.1
Send 3 bytes of normal data <- Client输出
Recv 3 bytes: 123 <- Server输出
Send 1 byte of OOB data <- Client输出
SIGURG received <- Server输出
Recv 1 OOB byte: 4 <- Server输出
Send 2 bytes of normal data <- Client输出
Recv 2 bytes: 56 <- Server输出
Send 1 byte of OOB data <- Client输出
SIGURG Received <- Server输出
Recv 1 OOB byte: 7 <- Server输出
received EOF <- Server输出
这个结果正是我们想要的。每一个客户端发送的带外数据都导致服务器端产生了

SIGURG 信号,服务器端收到SIGURG 信号后,就去读取带外数据了。



来源:http://www.xuebuyuan.com/2184933.html

关于带外数据:http://wenku.baidu.com/view/f04a4dff9e31433239689341?fr=prin

http://book.51cto.com/art/201306/400276.htm

http://baike.baidu.com/view/567593.htm

http://blog.csdn.net/todd911/article/details/22821529

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值