带外数据比普通数据具有更高的优先级,TCP没有真正的带外数据,而是提供了一个我们要讨论的紧急模式,TCP将数据放置在套机口发送缓冲区的下一个可用位置,并设置这个连接的TCP紧急指针(urgent pointer)为下一个可用位置,TCP紧急指针有一个比用MSG_OOB标志写入的数据多一个字节的序列号。由于接收端的带外缓冲只有1Byte所以发送端发送的多字节带外数据只有最后1Byte被当做带外数据,若接收端TCP连接设置了SO_OOBINLINE选项则带外数据和普通数据一样存放于TCP接收缓冲区中。带外数据的发送与接收可以调用含有MSG_OOB标志的send和recv。sockatmark(int sockfd)可以判断sockfd下一个数据是否含有带外数据,若有就可以调用含有MSG_OOB标志的recv接收带外数据,接收带外数据的方式是带有MSG_OOB的recv接收
检测带外数据到来的方法:
1 sockatmark(int sockfd)
2 由于带外数据对于select返回是异常事件所以可以由select返回判断带外数据到来
3 通过SIG_URG信号处理函数
发送带外数据的客户端程序
1 /* 2 * 带外数据发送端 3 * oob_client.c 4 * Created on: Oct 30, 2016 5 * Author: zhangming 6 */ 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <unistd.h> 10 #include <string.h> 11 #include <sys/socket.h> 12 #include <netinet/in.h> 13 #include <arpa/inet.h> 14 15 #define SEND_NORMAL_DATA "123" 16 #define SEND_OOB_DATA "abc" //其中只有最后的字符'c'会被当做带外数据 17 18 int main(int argc, char **argv) { 19 int fd=socket(AF_INET,SOCK_STREAM,0); 20 if(fd==-1) printf("1:%m\n"),exit(-1); 21 22 struct sockaddr_in dr; 23 dr.sin_family = AF_INET; 24 dr.sin_port = htons(10000); 25 dr.sin_addr.s_addr = inet_addr("192.168.209.128"); 26 27 int result = connect(fd,(struct sockaddr*)&dr,sizeof(dr)); 28 if(result==-1) printf("2:%m\n"),exit(-1); 29 30 fd_set fds; 31 FD_ZERO(&fds); 32 FD_SET(fd,&fds); 33 select(fd+1,0,&fds,0,0); 34 send(fd,SEND_NORMAL_DATA,strlen(SEND_NORMAL_DATA),0); //发送正常数据 35 //注意: 带外数据,接收端的带外缓存只有1Byte,所以只有字符'c'是真正的带外数据,同理带MSG_OOB标志的recv能接收带外数据 36 send(fd,SEND_OOB_DATA,strlen(SEND_OOB_DATA),MSG_OOB); //使用带外数据标志flag=MSG_OOB 37 38 close(fd); 39 }
接收带外数据的服务端程序(采用SIGURG信号检测带外数据的到来)
1 /* 2 * 带外数据接收端 3 * oob_server.c 4 * Created on: Oct 30, 2016 5 * Author: zhangming 6 */ 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <unistd.h> 10 #include <sys/socket.h> 11 #include <netinet/in.h> 12 #include <arpa/inet.h> 13 #include <signal.h> 14 #include <fcntl.h> 15 16 int fd,cfd; 17 18 void handle(int s){ 19 char data[100]; 20 if(s==SIGURG){ 21 int r = recv(cfd,data,sizeof(data),MSG_OOB); 22 data[r] = 0; 23 printf("接收%d字节的带外数据(oob data): %s\n",r,data); 24 } 25 } 26 27 int main(int argc, char **argv) { 28 fd=socket(AF_INET,SOCK_STREAM,0); 29 if(fd==-1) printf("1:%m\n"),exit(-1); 30 31 struct sockaddr_in dr; 32 dr.sin_family = AF_INET; 33 dr.sin_port = htons(10000); 34 dr.sin_addr.s_addr = inet_addr("192.168.209.128"); 35 36 int result = bind(fd,(struct sockaddr*)&dr,sizeof(dr)); 37 if(result==-1) printf("2:%m\n"),exit(-1); 38 39 result = listen(fd,10); 40 if(result==-1) printf("3:%m\n"),exit(-1); 41 42 signal(SIGURG,handle); //添加SIGURG信号 43 cfd = accept(fd,0,0); //建立客户端连接 44 if(cfd==-1) printf("4:%m\n"),exit(-1); 45 fcntl(cfd,F_SETOWN,getpid()); //SIGURG的前提条件是进程必须持有文件描述符cfd 46 47 char buf[100]; 48 while(1){ 49 int r = recv(cfd,buf,sizeof(buf),0); 50 if(r>0){ 51 buf[r]=0; 52 printf("接收%d字节的正常数据(normal data):%s\n",r,buf); 53 sleep(1); 54 }else{ 55 break; 56 } 57 } 58 59 close(cfd); 60 close(fd); 61 }
运行结果截图: