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\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函数的更多相关文章
- UNIX网络编程——select函数的并发限制和 poll 函数应用举例
一.用select实现的并发服务器,能达到的并发数,受两方面限制 1.一个进程能打开的最大文件描述符限制.这可以通过调整内核参数.可以通过ulimit -n来调整或者使用setrlimit函数设置, ...
- UNIX网络编程——fcntl函数
fcntl函数提供了与网络编程相关的如下特性: 非阻塞式I/O. 通过使用F_SETFL命令设置O_NONBLOCK文件状态标志,我们可以把一个套接字设置为非阻塞型. 信号驱动式I/O. 通过使用F ...
- UNIX网络编程——ioctl 函数的用法详解
1.介绍 Linux网络程序与内核交互的方法是通过ioctl来实现的,ioctl与网络协议栈进行交互,可得到网络接口的信息,网卡设备的映射属性和配置网络接口.并且还能够查看,修改,删除ARP高速缓存的 ...
- UNIX网络编程——getsockname和getpeername函数
UNIX网络编程--getsockname和getpeername函数 来源:网络转载 http://www.educity.cn/linux/1241293.html 这两个函数或者 ...
- UNIX网络编程——客户/服务器心搏函数
阅读此博客时,可以参考以前的博客<<UNIX网络编程--socket的keep-alive>>和<<UNIX网络编程--套接字选项(心跳检测.绑定地址复用)> ...
- UNIX网络编程——UDP 的connect函数(改进版)
上一篇我们提到,除非套接字已连接,否则异步错误是不会返回到UDP套接字的.我们确实可以给UDP套接字调用connect,然而这样做的结果却与TCP连接大相径庭:没有三次握手.内核只是检查是否存在立即可 ...
- 【unix网络编程第三版】阅读笔记(五):I/O复用:select和poll函数
本博文主要针对UNP一书中的第六章内容来聊聊I/O复用技术以及其在网络编程中的实现 1. I/O复用技术 I/O多路复用是指内核一旦发现进程指定的一个或者多个I/O条件准备就绪,它就通知该进程.I/O ...
- UNIX网络编程——使用select函数编写客户端和服务器
首先看原先<UNIX网络编程--并发服务器(TCP)>的代码,服务器代码serv.c: #include<stdio.h> #include<sys/types.h> ...
- UNIX网络编程 第6章 I/O复用:select和poll函数
UNIX网络编程 第6章 I/O复用:select和poll函数
随机推荐
- poj3185 高斯消元
The Water Bowls Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 5329 Accepted: 2081 D ...
- Python之作业购物车
作业之购物车优化 购物车优化要求如下: 用户入口: 启动程序后,输入用户名密码后,如果是第一次登录,让用户输入工资,然后打印商品列表 允许用户根据商品编号购买商品 用户选择商品后,检测余额是否够,够就 ...
- MongoDb进阶实践之六 MongoDB查询命令详述(补充)
一.引言 上一篇文章我们已经介绍了MongoDB数据库的查询操作,但是并没有介绍全,随着自己的学习的深入,对查询又有了新的东西,决定补充进来.如果大家想看上一篇有关MongoDB查询的 ...
- 一日一练-JS toString 和valueOf 方法的联系与区别
子曰:类型转换中toString 和valueOf 的联系与区别分析 首先是看看ES5 的规范是如何进行说明的 在这里有几个基础知识点需要了解一下: [[Class]] [[Class]] 属于Obj ...
- Java HttpClient伪造请求之简易封装满足HTTP以及HTTPS请求
HttpClient简介 HTTP 协议可能是现在 Internet 上使用得最多.最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源.虽然在 JDK 的 jav ...
- IPFS星际文件系统
IPFS星际文件系统(InterPlanetary File System)是去中心化文件系统,本文介绍IPFS节点软件系统安装,环境搭建等简介入门教程,及学习如何使用ipfs-api和Node.js ...
- C语言关闭日志文件时忘了将日志文件全局变量指针置为NULL
C语言写了一个write_log函数以写日志,写了一个close_log_file函数以关闭日志,声明了一个日志文件全局变量文件指针plogFile. write_log中首先判断plogFile是否 ...
- Win7 环境下虚拟机内 Samba 服务器的安装、配置以及与主机的通信实现
考虑到window和linux虚拟机之间互传文件较为麻烦,遂打算在虚拟机中安装Samba服务器,以此实现共享文件给window使用.然而安装配置过程曲折,遂作记录如下: 一.samba服务器的安装 正 ...
- 线程停止与volatile
1.使用标志位停止线程 在Java中希望停止线程,可以使用设置标志位的方法,如下例所示: class SimpleTask implements Runnable{ private boolean s ...
- Redis和nosql简介,api调用;Redis数据功能(String类型的数据处理);List数据结构(及Java调用处理);Hash数据结构;Set数据结构功能;sortedSet(有序集合)数
1.Redis和nosql简介,api调用 14.1/ nosql介绍 NoSQL:一类新出现的数据库(not only sql),它的特点: 1. 不支持SQL语法 2. 存储结构跟传统关系型数 ...