原文地址: http://blog.csdn.net/hishentan/article/details/38351871
带外数据的应用情况
如果发送客户端程序由于一些原因需要取消已经写入服务器的请求,那么他就需要向服务器紧急发送一个标识取消的请求。
使用带外数据的实际程序例子就是telnet,rlogin,ftp命令。
前两个程序(telnet和rlogin)会将中止字符作为紧急数据发送到远程端。这会允许远程端冲洗所有未处理的输入,并且丢弃所有未发送的终端输出。这会快速中断一个向我们屏幕发送大量数据 的运行进程。
ftp命令使用带外数据来中断一个文件的传输。
TCP的带外数据(TCP紧急数据)
TCP协议没有真正意义上的带外数据.为了发送重要协议,TCP提供了一种称为紧急模式(urgent mode)的机制.TCP协议在数据段中设置URG位,表示进入紧急模式.接收方可以对紧急模式采取特殊的处理.很容易看出来,这种方式数据不容易被阻塞,可以通过在我们的服务器端程序里面捕捉SIGURG信号来及时接受数据或者使用带OOB标志的recv函数来接受.
OOBServer.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>
- #include <unistd.h>
- #include <fcntl.h>
-
-
-
- #define MYPORT 4000
-
- #define BACKLOG 10
- int new_fd = -1;
- void sig_urg(int signo);
-
- void main()
- {
-
- int sockfd;
-
- struct sockaddr_in my_addr;
-
- struct sockaddr_in their_addr;
- int sin_size;
- int n ;
- char buff[100] ;
-
- void * old_sig_urg_handle ;
-
-
- if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
- {
- perror("socket");
- exit(1);
- }
-
- my_addr.sin_family = AF_INET;
-
- my_addr.sin_port = htons(MYPORT);
-
- 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)
- {
-
- perror("bind");
- exit(1);
- }
-
- if (listen(sockfd, BACKLOG) == -1)
- {
-
- perror("listen");
- exit(1);
- }
-
-
-
- old_sig_urg_handle = signal(SIGURG, sig_urg);
-
-
-
-
-
-
-
-
- while(1)
- {
-
- sin_size = sizeof(struct sockaddr_in);
-
-
- if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1)
- {
-
- perror("accept");
- continue;
- }
-
- fcntl(new_fd,F_SETOWN,getpid());
-
-
- printf("server: got connection from %s\n", inet_ntoa(their_addr.sin_addr));
-
-
- if (1)
- {
- while(1)
- {
- if((n=recv(new_fd,buff,sizeof(buff)-1,0)) == 0)
- {
- printf("received EOF\n");
- break;
- }
- buff[n] ='\0';
- printf("Recv %d bytes: %s\n", n, buff);
- }
- }
-
- close(new_fd);
- }
-
- while(waitpid(-1,NULL,WNOHANG) > 0);
-
- signal(SIGURG, old_sig_urg_handle);
- }
-
- void sig_urg(int signo)
- {
- int n;
- char buff[100];
- printf("SIGURG received\n");
-
-
- n = recv(new_fd,buff,sizeof(buff)-1,MSG_OOB);
- if(n>0)
- {
- buff[n]='\0';
- printf("recv %d OOB byte: %s\n",n,buff);
- }
- else
- {
- perror("recv");
- }
-
- }
OOBClient.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)
- {
-
- fprintf(stderr,"usage: client hostname\n");
- exit(1);
- }
-
- if ((he=gethostbyname(argv[1])) == NULL)
- {
-
- herror("gethostbyname");
- exit(1);
- }
- if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
- {
-
- 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)
- {
-
- perror("connect");
- exit(1);
- }
-
- if (send(sockfd, "5", 1, MSG_OOB)== -1)
- {
- perror("send");
- close(sockfd);
- exit(0);
- }
- printf("Send 1 byte of OOB data\n");
- sleep(2);
-
- if (send(sockfd, "123", 3, 0) == -1)
- {
-
- perror("send");
- close(sockfd);
- exit(0);
- }
- printf("Send 3 byte of normal data\n");
-
- sleep(2);
- if (send(sockfd, "4", 1, MSG_OOB)== -1)
- {
- perror("send");
- close(sockfd);
- exit(0);
- }
- printf("Send 1 byte of OOB data\n");
- sleep(2);
- if (send(sockfd, "56", 2, 0) == -1)
- {
- perror("send");
- close(sockfd);
- exit(0);
- }
- printf("Send 2 bytes of normal data\n");
- sleep(2);
- if (send(sockfd, "7", 1, MSG_OOB)== -1)
- {
- perror("send");
- close(sockfd);
- exit(0);
- }
- printf("Send 1 byte of OOB data\n");
- sleep(2);
- if (send(sockfd, "89", 2, MSG_OOB)== -1)
- {
- perror("send");
- close(sockfd);
- exit(0);
- }
- printf("Send 2 bytes of OOB data\n");
- sleep(2);
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- close(sockfd);
- return 0;
- }
运行结果:
笔记:
1.收发带外数据
01.发送方如何发送带外数据
注意TCP一次所发送的数据中,可能只包含了TCP的URG标志,却没有包含我们所发送的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);