每当收到一个带外数据时,就有一个与之关联的带外标记。这是发送进程发送带外字节时该字节在发送端普通数据流中的位置。在从套接字读入期间,接收进程通过调用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);
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\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);
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");
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连接最多只有一个带外标记。

UNIX网络编程——sockatmark函数的更多相关文章

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

    一.用select实现的并发服务器,能达到的并发数,受两方面限制 1.一个进程能打开的最大文件描述符限制.这可以通过调整内核参数.可以通过ulimit -n来调整或者使用setrlimit函数设置,  ...

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

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

  3. UNIX网络编程——ioctl 函数的用法详解

    1.介绍 Linux网络程序与内核交互的方法是通过ioctl来实现的,ioctl与网络协议栈进行交互,可得到网络接口的信息,网卡设备的映射属性和配置网络接口.并且还能够查看,修改,删除ARP高速缓存的 ...

  4. UNIX网络编程——getsockname和getpeername函数

    UNIX网络编程--getsockname和getpeername函数   来源:网络转载   http://www.educity.cn/linux/1241293.html     这两个函数或者 ...

  5. UNIX网络编程——客户/服务器心搏函数

    阅读此博客时,可以参考以前的博客<<UNIX网络编程--socket的keep-alive>>和<<UNIX网络编程--套接字选项(心跳检测.绑定地址复用)> ...

  6. UNIX网络编程——UDP 的connect函数(改进版)

    上一篇我们提到,除非套接字已连接,否则异步错误是不会返回到UDP套接字的.我们确实可以给UDP套接字调用connect,然而这样做的结果却与TCP连接大相径庭:没有三次握手.内核只是检查是否存在立即可 ...

  7. 【unix网络编程第三版】阅读笔记(五):I/O复用:select和poll函数

    本博文主要针对UNP一书中的第六章内容来聊聊I/O复用技术以及其在网络编程中的实现 1. I/O复用技术 I/O多路复用是指内核一旦发现进程指定的一个或者多个I/O条件准备就绪,它就通知该进程.I/O ...

  8. UNIX网络编程——使用select函数编写客户端和服务器

    首先看原先<UNIX网络编程--并发服务器(TCP)>的代码,服务器代码serv.c: #include<stdio.h> #include<sys/types.h> ...

  9. UNIX网络编程 第6章 I/O复用:select和poll函数

    UNIX网络编程 第6章 I/O复用:select和poll函数

随机推荐

  1. xx学院学员评优评奖管理系统

    [勤拂拭软件,软件开发,毕业设计,程序作业,论文写作指导:q-[1215714557]  加好友请注明:勤拂拭)] 之前帮助一个军校学生做的一个评优评奖管理系统,该系统主要用于学校学生评优评先使用. ...

  2. 毕业设计-JSP论文盲审系统

    之前做的一款jsp的论文盲审系统,ssh框架的,学生提交论文,系统管理员将论文分配给教员,教员在不知学员是谁的情况之下,对论文进行打分,然后提交给系统,最后系统发布成绩,供学员查看. 整体做的还不错, ...

  3. C# 解决winform 窗体控件在窗体变化时闪烁的问题

    在窗体form代码中加入如下代码即可: protected override CreateParams CreateParams { get { CreateParams cp = base.Crea ...

  4. Spring MVC - 静态页面

    环境搭建 以下示例显示如何使用Spring MVC Framework编写一个简单的基于Web的应用程序,它可以使用<mvc:resources>标记访问静态页面和动态页面.首先使用Int ...

  5. SQL Server数据库账号密码变更后导致vCenter Server无法访问数据库

    SQL Server数据库账号密码变更后导致vCenter Server无法访问数据库 1.1状况描述: 若SQL Server数据库的账号(这里以sa为例)密码发生了变更,那么连接数据的客户端vCe ...

  6. TypeScript: Week Reflection

    TypeScript: Week Reflection Introduction Type Script already provide decorators to help developers i ...

  7. RabbitMQ日志无法禁用问题

    最近使用spring+rabbitmq发现其Debug日志非常多,几天就把服务器磁盘弄爆了. 原来rabbitmq依赖logback.xml输出日志. 在和log4j.properties同目录下加一 ...

  8. 容器化现有ASP.NET MVC 5应用

    .NET Core的出现使得ASP.NET应用在Linux环境下使用变得更加普及.而配合上Docker容器,令ASP.NET应用的布署与管理也变得更加方便.在新的项目中运用ASP.NET Core无可 ...

  9. ACM Ignatius and the Princess II

    Problem Description Now our hero finds the door to the BEelzebub feng5166. He opens the door and fin ...

  10. Scala:输入输出

    http://blog.csdn.net/pipisorry/article/details/52902694 Scala基本输入输出 从屏幕上读取用户输入 有时候我们需要接收用户在屏幕输入的指令来处 ...