每当收到一个带外数据时,就有一个与之关联的带外标记。这是发送进程发送带外字节时该字节在发送端普通数据流中的位置。在从套接字读入期间,接收进程通过调用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\n");Send(sockfd, "4", 1, MSG_OOB);printf("wrote 1 byte of OOB data\n");Write(sockfd, "5", 1);printf("wrote 1 byte of normal data\n");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);elseerr_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\n");if ( (n = Read(connfd, buff, sizeof(buff)-1)) == 0) {printf("received EOF\n");exit(0);}buff[n] = 0; /* null terminate */printf("read %d bytes: %s\n", 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\n");sleep(5);Send(sockfd, "a", 1, MSG_OOB);printf("wrote 1 byte of OOB data\n");Write(sockfd, buff, 1024);printf("wrote 1024 bytes of normal data\n");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);elseerr_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");n = Recv(connfd, buff, sizeof(buff)-1, MSG_OOB);buff[n] = 0;     /* null terminate */printf("read %d OOB byte\n", 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\n");Send(sockfd, "4", 1, MSG_OOB);printf("wrote 1 byte of OOB data\n");Write(sockfd, "5", 1);printf("wrote 1 byte of normal data\n");Send(sockfd, "6", 1, MSG_OOB);printf("wrote 1 byte of OOB data\n");Write(sockfd, "7", 1);printf("wrote 1 byte of normal data\n");exit(0);
}

各个输出调用之间没有停顿,使得所有数据能够迅速的发送到接收端TCP。
       接收端,它在接收连接之后睡眠5秒,以允许来自发送端得数据到达接收TCP。以下是接收进程的输出:

read 5 bytes:12345
at OOB mark
read 2bytes:67
received EOF

第二个带外字节(6)的到来覆写了第一个带外字节(4)到来时存放的带外标记。正像我们所说,每个TCP连接最多只有一个带外标记。

转载于:https://www.cnblogs.com/hehehaha/p/6332568.html

UNIX网络编程——sockatmark函数相关推荐

  1. UNIX网络编程-listen函数及其包裹函数介绍

    UNIX网络编程-listen函数及其包裹函数介绍 函数简介 #include<sys/socket.h>int listen(int sockfd,int backlog);返回:若成功 ...

  2. UNIX网络编程——fcntl函数

    fcntl函数提供了与网络编程相关的如下特性: 非阻塞式I/O.  通过使用F_SETFL命令设置O_NONBLOCK文件状态标志,我们可以把一个套接字设置为非阻塞型. 信号驱动式I/O. 通过使用F ...

  3. UNIX网络编程——select函数的并发限制和 poll 函数应用举例

    http://blog.csdn.net/chenxun_2010/article/details/50489577 一.用select实现的并发服务器,能达到的并发数,受两方面限制 1.一个进程能打 ...

  4. unix网络编程——ioctl 函数的用法详解

    https://blog.csdn.net/ashlingr/article/details/8227603

  5. UNIX网络编程之旅-配置unp.h头文件环境

    最近在学习Unix网络编程(UNP),书中steven在处理网络编程时只用了一个#include "unp.h"  相当有个性并且也很便捷 于是我把第三版的源代码编译实现了这个过程 ...

  6. 网编编程必看书籍:unix网络编程

    unix网络编程被誉为圣经,该书主要讲socket套接字相关,socket API,从底层剖析网络编程.网络编程中需要用到的一些经典函数,多路复用函数,这些都值得去反复学习研究. 目录: 录 Part ...

  7. UNIX网络编程学习笔记(代码超详细解析)(持续更新)

    1. 其他函数准备 1. TCP 回射服务器程序: str_echo 函数 #include "unp.h"void str_echo(int sockfd) {ssize_t n ...

  8. 【LINUX/UNIX网络编程】之使用消息队列,信号量和命名管道实现的多进程服务器(多人群聊系统)...

    RT,使用消息队列,信号量和命名管道实现的多人群聊系统. 本学期Linux.unix网络编程的第三个作业. 先上实验要求: 实验三  多进程服务器 [实验目的] 1.熟练掌握进程的创建与终止方法: 2 ...

  9. linux下网络编程设置非阻塞,UNIX网络编程 非阻塞connect的实现

    一.<UNIX网络编程>-非阻塞connect 在一个TCP套接口被设置为非阻塞之后调用connect,connect会立即返回EINPROGRESS错误,表示连接操作正在进行中,但是仍未 ...

最新文章

  1. 计算机图形学入门总结!
  2. 【Android游戏开发之十】(优化处理)详细剖析Android Traceview 效率检视工具
  3. 2018年第一场省赛:黑龙江省智能车邀请赛
  4. Android Wear 唤醒热词会比“你好,安卓”好吗?
  5. java socketchannel api,SocketChannel API
  6. java 图片分段上传_java 分段读取文件 并通过HTTP上传
  7. Code Review
  8. 设计原则 里氏替换原则
  9. Netty:原理架构解析
  10. 【SPFA】腾讯大战360
  11. 跨期套利模型 2017-2019年白银跨期实盘年化7%-15%策略 策略开发
  12. html打开网页过场动画_动画演示制作软件(高级版)比PPT强太多了
  13. win10向右键新建中添加可创建的文件类型
  14. Spark HA高可用部署、基于文件系统单点恢复、基于zookeeper的Standby Master、如何恢复到上一次活着master挂掉之前的状态 03
  15. 逻辑回归中的梯度下降法
  16. Shell脚本读取mysql结果集各数据项的值
  17. 题解报告:hdu 1570 A C
  18. JS安全防护算法与逆向分析——新浪微博登录JS加密算法
  19. 2020年《财富》中国500强排行榜数据爬取,看看都有哪些
  20. 零售业无线技术应用--IP导航

热门文章

  1. linux服务器防cc攻击工具,宝塔linux面板
  2. win10设置默认输入法_为什么说win10越来越好用了?(技巧篇)
  3. mac mysql 报错_mac os mysql 配置?报错-问答-阿里云开发者社区-阿里云
  4. java可视化界面视频_java中的可视化界面
  5. 无监督学习与监督学习的区别
  6. 事件驱动数据管理 微服务和分布式数据管理问题
  7. 电子商务实战课程-张晨光-专题视频课程
  8. 漫画: 什么是外部排序?
  9. 学习《apache源代码全景分析》之存储段和存储段组摘录
  10. 基础练习 数列排序 c语言