帶外數據比普通數據具有更高的優先級,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 }
運行結果截圖: