zoukankan      html  css  js  c++  java
  • UNIX网络编程——sockatmark函数

           每当收到一个带外数据时,就有一个与之关联的带外标记。这是发送进程发送带外字节时该字节在发送端普通数据流中的位置。在从套接字读入期间,接收进程通过调用sockatmark函数确定是否处于带外标记。

    #include <sys/socket.h>
    int sockatmark(int sockfd); /* 返回值:如果在带外标记上为1, 不在标记上为0, 出错为-1 */
           本函数时POSIX创造的,如下给出了常见的SIOCATMARK  ioctl完成的本函数的一个实现:

    #include	"unp.h"
    
    int
    sockatmark(int fd)
    {
    	int		flag;
    
    	if (ioctl(fd, SIOCATMARK, &flag) < 0)
    		return(-1);
    	return(flag != 0);
    }
           不管接收进程在线(SO_OOBINLINE套接字选项)还是带外(MGS_OOB标志)接收带外数据,带外标记都适合。带外标记的常见用法之一是接收进程特殊的对待所有数据,直到标记通过。


    1.例子   

          我们现在给出一个简单的例子说明带外标记的以下两个特性:

    (1)带外标记总是指向普通数据最后一个字节紧后的位置。这意味着,如果带外数据在线接收,那么如果下一个待读入的字节时使用MSG_OOB标志发送的,sockatmask就返回真。而如果SO_OOBINLINE套接字选项没有开启,那么,若下一个待读入的字节是跟在带外数据后发送的第一个字节,sockatmark就返回真。

    (2)读操作总是停在带外标记上。也就是说,如果在套接字接收缓冲区有100个字节,不过在带外标记之前只有5个字节,而进程执行一个请求100个字节的read调用,那么返回的是带外标记之前的5个字节。这种在带外标记上强制停止读操作的做法使得进程能够调用sockatmark确实缓冲区指针是否处于带外标记。

         如下是我们的发送程序。它发送3个字节普通数据,1个字节带外数据,再跟1个字节普通数据。每个输出操作之间没有停顿。

    #include	"unp.h"
    
    int
    main(int argc, char **argv)
    {
    	int		sockfd;
    
    	if (argc != 3)
    		err_quit("usage: tcpsend04 <host> <port#>");
    
    	sockfd = Tcp_connect(argv[1], argv[2]);
    
    	Write(sockfd, "123", 3);
    	printf("wrote 3 bytes of normal data
    ");
    
    	Send(sockfd, "4", 1, MSG_OOB);
    	printf("wrote 1 byte of OOB data
    ");
    
    	Write(sockfd, "5", 1);
    	printf("wrote 1 byte of normal data
    ");
    
    	exit(0);
    }


           下面是接收程序。它既不使用SIGURG信号也不使用select。它调用sockatmark来确定何时碰到带外字节。
    #include	"unp.h"
    
    int
    main(int argc, char **argv)
    {
    	int		listenfd, connfd, n, on=1;
    	char	buff[100];
    
    	if (argc == 2)
    		listenfd = Tcp_listen(NULL, argv[1], NULL);
    	else if (argc == 3)
    		listenfd = Tcp_listen(argv[1], argv[2], NULL);
    	else
    		err_quit("usage: tcprecv04 [ <host> ] <port#>");
    
    	Setsockopt(listenfd, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on));
    
    	connfd = Accept(listenfd, NULL, NULL);
    	sleep(5);
    
    	for ( ; ; ) {
    		if (Sockatmark(connfd))
    			printf("at OOB mark
    ");
    
    		if ( (n = Read(connfd, buff, sizeof(buff)-1)) == 0) {
    			printf("received EOF
    ");
    			exit(0);
    		}
    		buff[n] = 0;	/* null terminate */
    		printf("read %d bytes: %s
    ", n, buff);
    	}
    }

    读入来自发送进程的所有数据
    21-30   程序循环调用read,并显示收到的数据。不过在调用read之前,先调用sockatmark检查缓冲区指针是否处于带外标记。

    我们运行本程序得到如下输出:

    read 3 bytes:123
    at OOB mask
    read 2bytes:45
    recvived EOF
           尽管接收进程首次调用read时接收端TCP已经接收了所有数据(因为接收进程调用了sleep),但是首次read调用因遇到带外标记而仅仅返回3个字节即在第四个字节(OOB标记)将会停在这里。下一个读入的字节时带外字节(值为4),因为我们早先告知内核在线放置带外数据。

    2.例子

     我们现在给出另一个简单的例子,用于展示早先提到过的带外数据的另外两个特性。
    (1)即使因为流量控制而停止发送数据,TCP仍然发送带外数据的通知(即它的紧急指针)。
    (2)在带外数据到达之前,接收进程可能被通知说发送进程已经发送了带外数据(使用SIGURG信号或通过select)。如果接收进程接着指定MSG_OOB调用recv,而带外数据却尚未到达,recv将返回EWOULDBLOCK错误。


    如下是发送程序:

    #include	"unp.h"
    
    int
    main(int argc, char **argv)
    {
    	int		sockfd, size;
    	char	buff[16384];
    
    	if (argc != 3)
    		err_quit("usage: tcpsend05 <host> <port#>");
    
    	sockfd = Tcp_connect(argv[1], argv[2]);
    
    	size = 32768;
    	Setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
    
    	Write(sockfd, buff, 16384);
    	printf("wrote 16384 bytes of normal data
    ");
    	sleep(5);
    
    	Send(sockfd, "a", 1, MSG_OOB);
    	printf("wrote 1 byte of OOB data
    ");
    
    	Write(sockfd, buff, 1024);
    	printf("wrote 1024 bytes of normal data
    ");
    
    	exit(0);
    }

    15-25   该进程把它的套接字发送缓冲区大小设置为32768,写出16384字节的普通数据,然后睡眠5秒钟。我们稍后将看到接收进程把它的套接字接收缓冲区大小设置为4096,因此发送进程的这些操作确保发送端TCP填满接收端得套接字接收缓冲区。发送进程接着发送单字节的带外数据,后跟1024字节的普通数据,然后终止。
     

     

     如下是接收程序:

    #include	"unp.h"
    
    int		listenfd, connfd;
    
    void	sig_urg(int);
    
    int
    main(int argc, char **argv)
    {
    	int		size;
    
    	if (argc == 2)
    		listenfd = Tcp_listen(NULL, argv[1], NULL);
    	else if (argc == 3)
    		listenfd = Tcp_listen(argv[1], argv[2], NULL);
    	else
    		err_quit("usage: tcprecv05 [ <host> ] <port#>");
    
    	size = 4096;
    	Setsockopt(listenfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
    
    	connfd = Accept(listenfd, NULL, NULL);
    
    	Signal(SIGURG, sig_urg);
    	Fcntl(connfd, F_SETOWN, getpid());
    
    	for ( ; ; )
    		pause();
    }
    
    void
    sig_urg(int signo)
    {
    	int		n;
    	char	buff[2048];
    
    	printf("SIGURG received
    ");
    	n = Recv(connfd, buff, sizeof(buff)-1, MSG_OOB);
    	buff[n] = 0;		/* null terminate */
    	printf("read %d OOB byte
    ", n);
    }
    

    19-28   接收进程把监听套接字接收缓冲区大小设置为4096.连接建立之后,这个大小将传承给已连接套接字。接收进程接着accept连接,建立一个SIGURG信号处理函数,并建立套接字的属主。主程序然后再一个无穷循环中调用pause。
    31-41   信号处理函数调用recv读入带外数据。


           我们先启动接收进程,接着启动发送进程,以下是来自发送进程的输出:

    wrote 16384 bytes of normal data
    wrote 1 bytes of OOB data
    wrote 1024 bytes of normal data
           正如所期,所有这些数据适合发送进程套接字发送缓冲区的大小,发送进程手终止。以下是来自接收进程的输出:

    SIGURG received
    recv error:Resource temporarily unavailable

           接收进程的输出结果说明了(2)。

           发送端TCP向接收端TCP发送了带外通知,由此产生递交给接收进程的SIGURG信号。然而当接收进程指定MSG_OOB标志调用recv时,相应带外字节不能读入因为带外数据还没有到达。

           解决办法是让接收进程通知读入已排队的普通数据,在套接字接收缓冲区中腾出空间。这将导致接收端TCP向发送端通告一个非零的窗口,最终允许发送带外字节。


    3.例子

          我们下一个例子展示了一个给定TCP连接只有一个带外标记,如果在接收进程读入某个现有带外数据之前有新的带外数据到达,先前的标记就丢失。
          下面是发送程序:

    #include	"unp.h"
    
    int
    main(int argc, char **argv)
    {
    	int		sockfd;
    
    	if (argc != 3)
    		err_quit("usage: tcpsend06 <host> <port#>");
    
    	sockfd = Tcp_connect(argv[1], argv[2]);
    
    	Write(sockfd, "123", 3);
    	printf("wrote 3 bytes of normal data
    ");
    
    	Send(sockfd, "4", 1, MSG_OOB);
    	printf("wrote 1 byte of OOB data
    ");
    
    	Write(sockfd, "5", 1);
    	printf("wrote 1 byte of normal data
    ");
    
    	Send(sockfd, "6", 1, MSG_OOB);
    	printf("wrote 1 byte of OOB data
    ");
    
    	Write(sockfd, "7", 1);
    	printf("wrote 1 byte of normal data
    ");
    
    	exit(0);
    }
    
           各个输出调用之间没有停顿,使得所有数据能够迅速的发送到接收端TCP。
           接收端,它在接收连接之后睡眠5秒,以允许来自发送端得数据到达接收TCP。以下是接收进程的输出:
    read 5 bytes:12345
    at OOB mark
    read 2bytes:67
    received EOF
           第二个带外字节(6)的到来覆写了第一个带外字节(4)到来时存放的带外标记。正像我们所说,每个TCP连接最多只有一个带外标记。
  • 相关阅读:
    bugku细心地大象
    【学术篇】一些水的不行的dp
    【笔记篇】莫队算法(一)
    【学术篇】luogu1351 [NOIP2014提高组] 联合权值
    【学术篇】网络流24题——方格取数加强版
    【学术篇】SDOI2009 SuperGCD
    【学术篇】网络流24题——方格取数问题
    【模板篇】A* 寻路算法
    【模板篇】k短路 SDOI2010 魔法猪学院
    【学术篇】SDOI2009 最优图像
  • 原文地址:https://www.cnblogs.com/hehehaha/p/6332568.html
Copyright © 2011-2022 走看看