TCP带外数据测试

原文地址: http://blog.csdn.net/hishentan/article/details/38351871


带外数据的应用情况

如果发送客户端程序由于一些原因需要取消已经写入服务器的请求,那么他就需要向服务器紧急发送一个标识取消的请求。

使用带外数据的实际程序例子就是telnet,rlogin,ftp命令。

前两个程序(telnetrlogin)会将中止字符作为紧急数据发送到远程端。这会允许远程端冲洗所有未处理的输入,并且丢弃所有未发送的终端输出。这会快速中断一个向我们屏幕发送大量数据 的运行进程。

ftp命令使用带外数据中断一个文件的传输


TCP的带外数据(TCP紧急数据)

TCP协议没有真正意义上的带外数据.为了发送重要协议,TCP提供了一种称为紧急模式(urgent mode)的机制.TCP协议在数据段中设置URG,表示进入紧急模式.接收方可以对紧急模式采取特殊的处理.很容易看出来,这种方式数据不容易被阻塞,可以通过在我们的服务器端程序里面捕捉SIGURG信号来及时接受数据或者使用带OOB标志的recv函数来接受.



OOBServer.c

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <errno.h>  
  4. #include <string.h>  
  5. #include <sys/types.h>  
  6. #include <netinet/in.h>  
  7. #include <sys/socket.h>  
  8. #include <sys/wait.h>  
  9. #include <unistd.h>  
  10. #include <fcntl.h>  
  11.   
  12.   
  13. /* 服务器要监听的本地端口 */  
  14. #define MYPORT 4000  
  15. /* 能够同时接受多少没有 accept 的连接 */  
  16. #define BACKLOG 10  
  17. int new_fd = -1;// 全局变量 连接套接字 主函数和SIGURG信号处理函数中均会调用  
  18. void sig_urg(int signo);  
  19.   
  20. void main()  
  21. {  
  22. /* 在 sock_fd 上进行监听,new_fd 接受新的连接 */  
  23. int sockfd;  
  24. /* 自己的地址信息 */  
  25. struct sockaddr_in my_addr;  
  26. /* 连接者的地址信息*/  
  27. struct sockaddr_in their_addr;  
  28. int sin_size;  
  29. int n ;  
  30. char buff[100] ;  
  31. /* 用于存储以前系统缺省的 SIGURL 处理器的变量 */   
  32. void * old_sig_urg_handle ;  
  33. /* 这里就是我们一直强调的错误检查.如果调用 socket() 出错,则返回 */  
  34.   
  35. if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)  
  36. {/* 输出错误提示并退出 */  
  37. perror("socket");  
  38. exit(1);  
  39. }  
  40. /* 主机字节顺序 */  
  41. my_addr.sin_family = AF_INET;  
  42. /* 网络字节顺序,短整型 */  
  43. my_addr.sin_port = htons(MYPORT);  
  44. /* 将运行程序机器的 IP 填充入 s_addr */  
  45. my_addr.sin_addr.s_addr = INADDR_ANY;  
  46. /* 将此结构的其余空间清零 */  
  47. bzero(&(my_addr.sin_zero), 8);  
  48. /* 这里是我们一直强调的错误检查!! */   
  49. if (bind(sockfd, (struct sockaddr *)&my_addr,sizeof(struct sockaddr)) == -1)  
  50. {  
  51. /* 如果调用 bind()失败,则给出错误提示,退出 */  
  52. perror("bind");  
  53. exit(1);  
  54. }  
  55. /* 这里是我们一直强调的错误检查!! */  
  56. if (listen(sockfd, BACKLOG) == -1)  
  57. {  
  58. /* 如果调用 listen 失败,则给出错误提示,退出 */  
  59. perror("listen");  
  60. exit(1);  
  61. }  
  62.   
  63.   
  64. /* 设置 SIGURG 的处理函数 sig_urg */  
  65. old_sig_urg_handle = signal(SIGURG, sig_urg);  
  66. /* 将我们的进程创建为套接口的拥有者 */ //wrong;非监听套接字  
  67. /*if(fcntl(sockfd, F_SETOWN, getpid())==-1) 
  68. { 
  69. perror("fcntl"); 
  70. exit(1); 
  71. }*/  
  72.   
  73.   
  74. while(1)  
  75. {  
  76. /* 这里是主 accept()循环 */  
  77. sin_size = sizeof(struct sockaddr_in);  
  78.   
  79. /* 这里是我们一直强调的错误检查!! */  
  80. if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1)  
  81. {  
  82. /* 如果调用 accept()出现错误,则给出错误提示,进入下一个循环 */  
  83. perror("accept");  
  84. continue;  
  85. }  
  86. //printf("in main, new_fd = %d\n",new_fd);  
  87. fcntl(new_fd,F_SETOWN,getpid());//将我们的进程设置为(连接套接字)的拥有者  
  88.   
  89. /* 服务器给出出现连接的信息 */  
  90. printf("server: got connection from %s\n", inet_ntoa(their_addr.sin_addr));  
  91. /* 这里将建立一个子进程来和刚刚建立的套接字进行通讯 */  
  92. //if (!fork())  
  93. if (1)  
  94. {  
  95. while(1)  
  96. {  
  97. if((n=recv(new_fd,buff,sizeof(buff)-1,0)) == 0)  
  98. {  
  99. printf("received EOF\n");  
  100. break;  
  101. }  
  102. buff[n] ='\0';  
  103. printf("Recv %d bytes: %s\n", n, buff);  
  104. }  
  105. }  
  106. /* 关闭 new_fd 代表的这个套接字连接 */  
  107. close(new_fd);  
  108. }  
  109. /* 等待所有的子进程都退出 */  
  110. while(waitpid(-1,NULL,WNOHANG) > 0);  
  111. /* 恢复系统以前对 SIGURG 的处理器 */  
  112. signal(SIGURG, old_sig_urg_handle);  
  113. }  
  114.   
  115. void sig_urg(int signo)  
  116. {  
  117. int n;  
  118. char buff[100];  
  119. printf("SIGURG received\n");  
  120. //printf("in sig_urg(),new_fd = %d\n",new_fd);  
  121. //while((n = recv(new_fd,buff,sizeof(buff)-1,MSG_OOB)) == -1);  
  122. n = recv(new_fd,buff,sizeof(buff)-1,MSG_OOB);  
  123. if(n>0)  
  124. {  
  125. buff[n]='\0';  
  126. printf("recv %d OOB byte: %s\n",n,buff);  
  127. }  
  128. else  
  129. {  
  130. perror("recv");  
  131. }  
  132.   
  133. }  
OOBClient.c

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <errno.h>  
  4. #include <string.h>  
  5. #include <netdb.h>  
  6. #include <sys/types.h>  
  7. #include <netinet/in.h>  
  8. #include <sys/socket.h>  
  9. /* 服务器程序监听的端口号 */  
  10. #define PORT 4000  
  11. /* 我们一次所能够接收的最大字节数 */  
  12. #define MAXDATASIZE 100  
  13. int main(int argc, char *argv[])  
  14. {  
  15. /* 套接字描述符 */  
  16. int sockfd,numbytes;  
  17. char buf[MAXDATASIZE];  
  18. struct hostent *he;  
  19. /* 连接者的主机信息 */  
  20. struct sockaddr_in their_addr;  
  21. /* 检查参数信息 */  
  22. if (argc != 2)  
  23. {  
  24. /* 如果没有参数,则给出使用方法后退出 */  
  25. fprintf(stderr,"usage: client hostname\n");  
  26. exit(1);  
  27. }  
  28. /* 取得主机信息 */  
  29. if ((he=gethostbyname(argv[1])) == NULL)  
  30. {  
  31. /* 如果 gethostbyname()发生错误,则显示错误信息并退出 */  
  32. herror("gethostbyname");  
  33. exit(1);  
  34. }  
  35. if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)  
  36. {  
  37. /* 如果 socket()调用出现错误则显示错误信息并退出 */  
  38. perror("socket");  
  39. exit(1);  
  40. }  
  41. /* 主机字节顺序 */  
  42. their_addr.sin_family = AF_INET;  
  43. /* 网络字节顺序,短整型 */  
  44. their_addr.sin_port = htons(PORT);  
  45. their_addr.sin_addr = *((struct in_addr *)he->h_addr);  
  46. /* 将结构剩下的部分清零*/  
  47. bzero(&(their_addr.sin_zero), 8);  
  48. if(connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1)  
  49. {  
  50. /* 如果 connect()建立连接错误,则显示出错误信息,退出 */  
  51. perror("connect");  
  52. exit(1);  
  53. }  
  54.   
  55. if (send(sockfd, "5", 1, MSG_OOB)== -1)  
  56. {  
  57. perror("send");  
  58. close(sockfd);  
  59. exit(0);  
  60. }  
  61. printf("Send 1 byte of OOB data\n");  
  62. sleep(2);  
  63. /* 这里就是我们说的错误检查! */  
  64. if (send(sockfd, "123", 3, 0) == -1)//   123  normal data  
  65. {  
  66. /* 如果错误,则给出错误提示,然后关闭这个新连接,退出 */  
  67. perror("send");  
  68. close(sockfd);  
  69. exit(0);  
  70. }  
  71. printf("Send 3 byte of normal data\n");  
  72. /* 睡眠 1 秒 */  
  73. sleep(2);  
  74. if (send(sockfd, "4", 1, MSG_OOB)== -1)  
  75. {  
  76. perror("send");  
  77. close(sockfd);  
  78. exit(0);  
  79. }  
  80. printf("Send 1 byte of OOB data\n");  
  81. sleep(2);  
  82. if (send(sockfd, "56", 2, 0) == -1)//  56  normal data  
  83. {  
  84. perror("send");  
  85. close(sockfd);  
  86. exit(0);  
  87. }  
  88. printf("Send 2 bytes of normal data\n");  
  89. sleep(2);  
  90. if (send(sockfd, "7", 1, MSG_OOB)== -1)  
  91. {  
  92. perror("send");  
  93. close(sockfd);  
  94. exit(0);  
  95. }  
  96. printf("Send 1 byte of OOB data\n");  
  97. sleep(2);  
  98. if (send(sockfd, "89", 2, MSG_OOB)== -1)  
  99. {  
  100. perror("send");  
  101. close(sockfd);  
  102. exit(0);  
  103. }  
  104. printf("Send 2 bytes of OOB data\n");  
  105. sleep(2);  
  106. /* 
  107. if (send(sockfd, "ABC", 3, MSG_OOB)== -1) 
  108. { 
  109. perror("send"); 
  110. close(sockfd); 
  111. exit(0); 
  112. } 
  113. printf("Send 3 bytes of OOB data\n"); 
  114. sleep(2); 
  115. if (send(sockfd, "abc", 3, MSG_OOB)== -1) 
  116. { 
  117. perror("send"); 
  118. close(sockfd); 
  119. exit(0); 
  120. } 
  121. printf("Send 3 bytes of OOB data\n"); 
  122. sleep(2); 
  123. */  
  124. close(sockfd);  
  125. return 0;  
  126. }  

运行结果:


笔记:

1.收发带外数据


01.发送方如何发送带外数据

注意TCP一次所发送的数据中,可能只包含了TCPURG标志,却没有包含我们所发送的OOB数据(当发送数据队列中再OOB前面的数据已经达到TCP数据包的长度限制)。

if(send(new_fd, "4", 1, MSG_OOB)== -1)

{

perror("send");

close(new_fd);

exit(0);

}

printf("Send1 byte of OOB data\n");

 

 

02.接收方如何接收带外数据

/*设置 SIGURG的处理函数 sig_urg */

old_sig_urg_handle= signal(SIGURG, sig_urg);

voidsig_urg(int signo)

{

intn;

charbuff[100] ;

printf("SIGURGreceived\n");

n = recv(new_fd, buff,sizeof(buff)–1, MSG_OOB);

buff[ n ] = 0 ;

printf("recv%d OOB byte: %s\n", n, buff);

}

2.send(sendfd,"ABC",3,MSG_OOB),将发送3个字节的带外数据OOB数据.但是这里TCP又只支持一个字节的OOB,难道丢掉2个字节?

 TCP将把紧急模式URG 置位,紧急指针定位第三个字节("C")(这里不管它的具体位置,紧急指针的作用就是提供定位那个OOB字节的信息),前两个字节("AB")当作普通字节发送.其实TCP总是把最后一个字节当作OOB数据,其他的当作普通字节.不管你通过带MSG_OOB标志的sendxxx函数发送多少字节带外数据OOB数据,发送端只把最后一个字节当作OOB数据,接收端也只能收到一个字节的OOB数据.

3. fcntl(new_fd,F_SETOWN,getpid());//将我们的进程设置为 (连接套接字,而不是 监听套接字 )的拥有者

4.创建子进程接收普通数据时,带外数据不能正常接收。。。


附:

信号的处理

UNIX 的系统调用 signal()用于接收一个指定类型的信号,并可以指定相应的方法。这就是说,signal()能够将指定的处理函数与信号向关联

函数原型

int signal(int sig,_sighanler_t handler);//sig为需要处理的信号类型,handler为与信号关联的动作,可以是函数地址,也可以是SIG_IGN(忽略信号),SIG_DFL(恢复系统对信号的默认处理).

函数返回值

如果执行成功,则返回信号在此次signal调用之前的关联


Eg:

void sig_urg(int signo);

 

/* 用于存储以前系统缺省的 SIGURL 处理器的变量 */

void * old_sig_urg_handle ;

/* 设置 SIGURG 的处理函数 sig_urg*/

old_sig_urg_handle = signal(SIGURG,sig_urg);

******

***数据处理工作****.

******

/* 恢复系统以前对 SIGURG 的处理器 */

signal(SIGURG, old_sig_urg_handle);


 

 

(2)在一个套接字上使用信号驱动 I/O 操作

让内核在文件描述符(此处即套接字)就绪的时候使用信号来通知我们。

为了在一个套接字上使用信号驱动 I/O 操作,下面这三步是所必须的。

(1)一个和SIGIO信号的处理函数必须设定。

(2)套接字的拥有者所属进程必须被设定。一般来说是使用fcntl函数的 F_SETOWN 参数来进行设定拥有者。以便有明确的进程来接收当某个套接字就绪时发出的信号。

(3)套接字必须被允许使用异步 I/O。一般是通过调用 fcntl 函数的 F_SETFL 命令,O_ASYNC为参数来实现。

Eg:

1.首先,为SIGIO信号设置一个处理函数,用来读取并处理位于输入缓存中的数据。

signal ( SIGIO , void ( * getmyinput ) ( int signum ) );

2.设置一个用来接受SIGIO信号的进程。用fcntl函数。

fcntl ( my_fd , F_SETOWN, getpid() );

3.得到文件描述符的状态标志集,为该状态标志集添加一个O_ASYNC属性。

int  flags = fcntl ( my_fd ,F_GETFL);

fcntl ( my_fd , F_SETFL , flags | O_ASYNC);


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值