TCP带外数据OOB

来源:互联网 发布:中国网络日报。 编辑:程序博客网 时间:2024/06/03 02:43

原文地址:http://blog.csdn.net/ty_laurel/article/details/52164669

传输层协议使用带外数据(out-of-band,OOB)来发送一些重要的数据,如果通信一方有重要的数据需要通知对方时,协议能够将这些数据快速地发送到对方.为了发送这些数据,协议一般不使用与普通数据相同的通道,而是使用另外的通道实现.

OOB数据(TCP)介绍

带外数据即就是优先数据,linux系统的套接字机制支持低层协议发送和接受带外数据.但是TCP协议没有真正意义上的带外数据.为了发送重要协议,TCP提供了一种称为紧急模式(urgent mode)的机制。TCP在报文头中设置URG位,表示进入紧急模式.接收方可以对紧急模式采取特殊的处理。

这里写图片描述
tcp报文格式

很容易看出来,这种方式数据不容易被阻塞,可以通过在我们的服务器端程序里面捕捉SIGURG信号来及时接受数据或者使用带OOB标志的recv函数来接受.使用send和recv函数时,可以指定最后一个参数flags为MSG_OOB来发送接收带外数据。

    send(...,MSG_OOB,);    recv(...,MSG_OOB,);
  • 1
  • 2

OOB数据特点

1.OOB数据每次只能是一个字符
2.普通数据使用一般方式接收与发送,OOB数据使用MSG_OOB接收与发送
3.一个数据使用MSG_OOB,则最后一个字符是OOB数据,其他的是非OOB数据
4.OOB数据是优先数据。优先体现在什么地方?
在我看来OOB数据优先体现在同一次发送数据(包含普通数据和OOB数据)时,OOB数据会优先于普通数据到达目标端。下边有实例可以认证这一点。

带外数据发送过程:和普通数据发送过程一样,只是在数据字符串最后一个字符处设置一个标记,目标端接收到字符串,也是存放在一个描述符号中,但是会将指针指向最后一个字符上;接收进程发现字符上有带外数据标记,会发送信号SIGURG(紧急数据),即带外数据的优先级体现在信号的优先。

使用带外数据的实际例子telnet、rlogin、ftp命令,前两个命令会将中止字符作为紧急数据发送到远端,这会使得远端冲洗所有未处理的输入,并且丢弃所有为发送的终端输入,会快速中断一个向屏幕发送大量数据的运行进程;ftp命令使用带外数据来中断一个文件的传输。

在循环发送数据时,带外数据标记可能会被覆盖,导致形成一长串字符,只有最后一个被标记带外数据,其他的被覆盖。

案例:

客户程序send函数设置为MSG_OOB:
服务器接收数据recv两次:
a.不设置为MSG_OOB标志,服务器接收的数据没有最后一个字符;
b.设置为MSG_OOB标志,只会接收到一个字符,也就是带外数据。

oob_server.c            recv    MSG_OOBoob_client.c            send    MSG_OOB
  • 1
  • 2
  • 3
  • 4

对于这个实例,客户端程序发送带外数据,服务器端有两种方式可以接受数据,信号和select。

客户端程序如下:

/** * oob_client.c * */main(){        int fd;        int r;        struct sockaddr_in dr;        fd=socket(AF_INET,SOCK_STREAM,0);        if(fd==-1) printf("socket err:%m\n"),exit(-1);        dr.sin_family=AF_INET;        dr.sin_port=htons(9999);        dr.sin_addr.s_addr=inet_addr("192.168.245.149");        r=connect(fd,(struct sockaddr*)&dr,sizeof(dr));        if(r==-1) printf("connect err:%m\n"),exit(-1);        r=send(fd,"hello tty",9,MSG_OOB);    //发送带外数据,只有最后一个字节被标识为带外数据,其他的为普通数据,并且带外数据会优先于普通数据到达        if(r==-1) printf("send err:%m\n"),exit(-1);        close(fd);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

1. 使用SIGURG信号处理带外数据

/** * oob_server_signal.c **/void handle(int s){        int r;        char data[100];        r=recv(cfd,data,100,MSG_OOB);        if(r>0){             data[r]=0;             printf("接收带外数据:%s\n",data);        }}main(){        /*此处为server建立socket,并bind地址*/        fcntl(cfd,F_SETOWN,getpid());    //将进程创建为套接口的所有者,使得带外数据到达时,内核向进程发送一个SIGURG信号        signal(SIGURG,handle);        while(1)        {                r=recv(cfd,buf,1024,0);                if(r>0) {                        buf[r]=0;                        printf("普通数据:%s\n",buf);                } else                        break;        }        ...}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

运行结果:

[ty@tiany I_O]$ ./oob_server_signal 接收带外数据:y普通数据:hello tt
  • 1
  • 2
  • 3

由输出可以发现字符串最后一个字符‘y’先被接收到,正是体现的带外数据的紧急特性,也就是TCP的紧急模式。

2. 使用select异常接收带外数据

 int main(int argc, char **argv) {     /*此处为server建立socket,并bind地址,省略*/     char buf[1024];     //buf缓冲区接收数据(普通数据和带外数据)     fd_set read_fds;    //接收普通数据,放入读事件集合中     fd_set exceptions_fds;   //接收带外数据,发生异常,放入异常事件集合中     FD_ZERO(&read_fds);     FD_ZERO(&exceptions_fds);     while(1) {         FD_SET(connfd,&read_fds);         FD_SET(connfd,&exceptions_fds);         ret = select(connfd+1,&read_fds,NULL,&exceptions_fds,NULL);         if(FD_ISSET(connfd,&read_fds)){             memset(buf,0x00,sizeof(buf));             ret = recv(connfd,buf,sizeof(buf)-1,0);         }          if(FD_ISSET(connfd,&exceptions_fds)) {             memset(buf,0x00,sizeof(buf));             ret =recv(connfd,buf,sizeof(buf)-1,MSG_OOB);         }     }     ... }                                 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

对于select函数的使用参看我上一边博文。
完整代码获取链接:https://github.com/ty92/OOB