文档章节

网络编程学习——高级I/O函数

thanatos_y
 thanatos_y
发布于 2016/04/20 09:40
字数 2024
阅读 19
收藏 0

1 概述

  在I/O操作上设置超时,这里有三种方法。然后read和write这两个函数的三个变体:recv和send允许通过第四个参数从进程到内核传递标志;readv和writev允许指定往其中输入数据或从其中输出数据的缓冲区向量;recvmsg和sendmsg结合了其他I/O函数的所有特性,并具有接收和发送辅助数据的新能力。

2 套接字超时

  在套解字I/O上设置超时的方法有以下三种;

  1. 调用alarm,它在指定超时期满时产生SIGALRM信号。这个方法涉及信号处理,而信号处理在不同的实现上存在差异,而且可能干扰进程中现有alarm调用。

  2. 在select中阻塞等待I/O(select有内置的时间限制),以此代替直接阻塞在read或write调用上。

  3. 使用较新的SO_REVTIMEO和SO_SNDTIMEO套接字选项。这个方法的问题在于并非所有实现都支持这两个套接字选项。

3 recv和send函数

  这两个函数类似标准read和write函数,不过需要一个额外的参数。

#include <socket.h>
/* Read N bytes into BUF from socket FD.
   Returns the number read or -1 for errors.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern ssize_t recv (int __fd, void *__buf, size_t __n, int __flags);

/* Send N bytes of BUF to socket FD.  Returns the number sent or -1.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern ssize_t send (int __fd, const void *__buf, size_t __n, int __flags);

  I/O函数的flag值。

图1-1 I/O函数的flags参数

 

4 readv和writev函数

  readv和writev允许单个系统调用读入到或写出自一个或多个缓冲区。这些操作分别称为分散读(scatter read)和集中写(gather write),因为来自读操作的输入数据被分散到多个应用缓冲区,而来自多个应用缓冲区的输出数据则被集中提供给单个写操作。

#include <bits/uio.h>

/* Structure for scatter/gather I/O.  */
struct iovec
  {
    void *iov_base;    /* Pointer to data.  */
    size_t iov_len;    /* Length of data.  */
  };


#include <sys/uio.h>

/* Read data from file descriptor FD, and put the result in the
   buffers described by IOVEC, which is a vector of COUNT 'struct iovec's.
   The buffers are filled in the order specified.
   Operates just like 'read' (see <unistd.h>) except that data are
   put in IOVEC instead of a contiguous buffer.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern ssize_t readv (int __fd, const struct iovec *__iovec, int __count)
  __wur;

  
/* Write data pointed by the buffers described by IOVEC, which
   is a vector of COUNT 'struct iovec's, to file descriptor FD.
   The data is written in the order specified.
   Operates just like 'write' (see <unistd.h>) except that the data
   are taken from IOVEC instead of a contiguous buffer.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern ssize_t writev (int __fd, const struct iovec *__iovec, int __count)
  __wur;

  writev是一个原子操作。

5 recvmsg和sendmsg函数

  这两个函数是最通用的I/O函数。实际上我们可以把所有read、readv、recv和recvfrom调用替换成recvmsg调用。类似地,各种输出函数调用也可以替换成sendmsg调用。

#include <socket.h>

/* Send a message described MESSAGE on socket FD.
   Returns the number of bytes sent, or -1 for errors.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern ssize_t sendmsg (int __fd, const struct msghdr *__message,
            int __flags);
            
/* Send a VLEN messages as described by VMESSAGES to socket FD.
   Returns the number of datagrams successfully written or -1 for errors.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern int sendmmsg (int __fd, struct mmsghdr *__vmessages,
             unsigned int __vlen, int __flags);

  这两个函数把大部分参数封装到一个mmshdr。

#include <socket.h>
/* For `recvmmsg' and `sendmmsg'.  */
struct mmsghdr
  {
    struct msghdr msg_hdr;    /* Actual message header.  */
    unsigned int msg_len;    /* Number of received or sent bytes for the
                   entry.  */
  };
    
#include <sys/socket.h>

  /* Structure describing messages sent by
   `sendmsg' and received by `recvmsg'.  */
struct msghdr
  {
    void *msg_name;        /* Address to send to/receive from.  */
    socklen_t msg_namelen;    /* Length of address data.  */

    struct iovec *msg_iov;    /* Vector of data to send/receive into.  */
    size_t msg_iovlen;        /* Number of elements in the vector.  */

    void *msg_control;        /* Ancillary(辅助数据) data (eg BSD filedesc passing). */
    size_t msg_controllen;    /* Ancillary data buffer length.
                   !! The type should be socklen_t but the
                   definition of the kernel is incompatible
                   with this.  */

    int msg_flags;        /* Flags on received message.  */
  };

  图1-2汇总了内核为输入和输出函数检查的flags参数值以及recvmsg可能返回的msg_flag成员值。其中没有sendmsg msg_flags一栏,因为我们已提及本组合无效。

图1-2 各种I/O函数输入和输出标志的总结

  图1-3展示了一个msghdr结构以及它指向的各种信息。

图1-3 对一个UDP套接字调用recvmsg时的数据结构

  图1-4展示了recvmsg返回时msghdr结构中的所有信息。

  图1-5汇总了我们已讲述的5组I/O函数之间的差异。

图1-5 5组I/O函数的比较

 

6 辅助数组

  辅助数据(ancillary data)可通过调用sendmsg和recvmsg这两个函数,使用msghdr结构中的msg_control和msg_controllen这两个成员发送和接收。辅助数据的另一个称谓是控制信息(control information)。图1-6汇总了辅助数据的各种用途。

图1-6 辅助数据用途的总结

  辅助数据由一个或多个辅助数据对象(ancillary data object)构成,每个对象以一个定义在头文件<bits/socket.h>中的cmsghdr结构开头。

/* Structure used for storage of ancillary data object information.  */
struct cmsghdr
  {
    size_t cmsg_len;        /* Length of data in cmsg_data plus length
                   of cmsghdr structure.
                   !! The type should be socklen_t but the
                   definition of the kernel is incompatible
                   with this.  */
    int cmsg_level;        /* Originating protocol.  */
    int cmsg_type;        /* Protocol specific type.  */
#if (!defined __STRICT_ANSI__ && __GNUC__ >= 2) || __STDC_VERSION__ >= 199901L
    __extension__ unsigned char __cmsg_data __flexarr; /* Ancillary data.  */
#endif
  };

  图1-7展示了在一个控制缓冲区出现2个辅助数据对象的例子。

图1-7 包含两个辅助数据对象的辅助函数

  图1-8显示了通过一个Unix域套接字传递描述符或传递凭证时所用cmshhdr结构的格式。

图1-8 用在Unix域套接字上的cmsghdr结构

  既然由recvmsg返回的辅助数据可含有任意数目的辅助数据对象,为了对应用程序屏蔽可能出现的填充字节,头文件<bits/socket.h>中定义了以下5个宏,以简化对辅助数据的处理。

#if (!defined __STRICT_ANSI__ && __GNUC__ >= 2) || __STDC_VERSION__ >= 199901L
# define CMSG_DATA(cmsg) ((cmsg)->__cmsg_data)
#else
# define CMSG_DATA(cmsg) ((unsigned char *) ((struct cmsghdr *) (cmsg) + 1))
#endif
#define CMSG_NXTHDR(mhdr, cmsg) __cmsg_nxthdr (mhdr, cmsg)
#define CMSG_FIRSTHDR(mhdr) \
  ((size_t) (mhdr)->msg_controllen >= sizeof (struct cmsghdr)              \
   ? (struct cmsghdr *) (mhdr)->msg_control : (struct cmsghdr *) 0)
#define CMSG_ALIGN(len) (((len) + sizeof (size_t) - 1) \
             & (size_t) ~(sizeof (size_t) - 1))
#define CMSG_SPACE(len) (CMSG_ALIGN (len) \
             + CMSG_ALIGN (sizeof (struct cmsghdr)))
#define CMSG_LEN(len)   (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len))

  这些宏能以如下伪代码形式使用。

  CMSG_SPACE要记可能出现的尾部的填充字节,而CMSG_LEN不会。

7 排队的数据量

  有时候我们想知道在不真正读取数据的前提下直到一个套接字上已有多少数据排队等着读取。有3个技术可用于获悉已排队的数据量。

  1. 如果获悉已排队数据量的目的在于避免读操作阻塞在内核中,那么可以使用非阻塞式I/O。

  2. 如果我们既想查看数据,又想数据仍然留在接收队列中以供本进程其他部分稍后读取,那么可以使用MSG_PEEK标志。

  3. 一些实现支持ioctl的FINONREAD命令。

8 kqueue接口

  本接口允许进程向内核注册描述所关注kqueue事件的事件过滤器(event filter)。事件除了与select所关注类似的文件I/O和超时外,还有异步I/O、文件修改通知(例如文件被删除或修改时发生的通知)、进程跟踪(例如进程调用exit或fork时发出通知)和信号处理。kqueue接口包括如下2个函数和1个宏。

  kqueue函数返回一个新的kqueue描述符,用于后序调用的kevent调用中。kevent函数既用于注册所关注的事件,也用于确定是否有所关注事件发生。

   其中flags成员在调用时指定过滤器更改行为,在返回时额外给出条件,如图1-9所示。

图1-9 kevent结构的flags成员

  filter成员指定的过滤器类型如图1-10所示。

图1-10 kevent结构的filter成员

 


 

 

 

 

 

 

 

 

© 著作权归作者所有

共有 人打赏支持
thanatos_y
粉丝 7
博文 112
码字总数 315059
作品 0
成都
程序员
0-Linux 网络编程学习笔记导航

学习交流群: Linux 学习交流群 610441700 说明:本系列文章并不能取代 《UNP》这本旷世之作,文章中难免有错误与不足之处,希望读者们遇到有疑问的地方可以加群互相交流,共同进步。写这一系...

q1007729991
2017/04/04
0
0
Qzone 微信 Java高级——dubbo源码分析之远程通信 netty

Java高级——dubbo源码分析之远程通信 netty dubbo 底层通信选择了 netty 这个 nio 框架做为默认的网络通信框架并且通过自定义协议进行通信。dubbo 支持以下网络通信框架: Netty(默认) Min...

Java架构师那些事
08/29
0
0
再读Socket编程——《UNIX网络编程(卷一)》学习点滴

  原先曾以Socket编程为入口开始自己的新的学习,毕竟未曾致用,时至今日已比较生疏。借着阅读《UNIX网络编程(卷一)》(简称UNPv1)的机会,正好复习一番,而且希望将新的感受记录下来。...

长征6号
2017/04/12
0
0
《Node.js开发指南》书评汇总

刚查了下库存,发现订阅《Node.js开发指南》的读者大增,这是为什么呢?看了下近期本书在豆瓣的评论,口碑很好,现将豆瓣的书评汇总如下: ----------------------------------------------...

生气的散人
2012/10/15
0
0
如何学好C和C++

有人在酷壳的留言版上询问下面的问题 keepwalker : 今天晚上我看到这篇文章。 http://programmers.stackexchange.com/questions/62502/small-c-projects 我也遇到了和提问的老外一样的问题。...

zhangyujsj
2015/09/06
24
0

没有更多内容

加载失败,请刷新页面

加载更多

原型模式

1、原型模式-定义 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象 克隆(浅度克隆->拷贝值类型或者引用,深度克隆->创建新的对象,开辟新的内存) 例如客户端知道抽象Pro...

阿元
今天
16
0
awk命令扩展使用操作

awk 中使用外部shell变量 示例1 [root@centos01 t1022]# A=888[root@centos01 t1022]# echo "" | awk -v GET_A=$A '{print GET_A}'888[root@centos01 t1022]# echo "aaaaaaaaaaaaa" | aw......

野雪球
今天
16
0
深入解析MySQL视图VIEW

Q:什么是视图?视图是干什么用的? A:视图(view)是一种虚拟存在的表,是一个逻辑表,本身并不包含数据。作为一个select语句保存在数据字典中的。   通过视图,可以展现基表的部分数据;...

IT--小哥
今天
23
0
虚拟机学习之二:垃圾收集器和内存分配策略

1.对象是否可回收 1.1引用计数算法 引用计数算法:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时候计数器值为0的对象就是不可能...

贾峰uk
今天
14
0
smart-doc功能使用介绍

smart-doc从8月份底开始开源发布到目前为止已经迭代了几个版本。在这里非常感谢那些敢于用smart-doc去做尝试并积极提出建议的社区用户。因此决定在本博客中重要说明下smart-doc的功能,包括使...

上官胡闹
昨天
25
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部