• UNP学习 高级I/O函数


    首先为一个I/O函数设置超时,这有三种方法。然后是三个read和write函数的变体:

    • recv和send,他们可以把含有标志的第四个参数从进程传给内核;
    • readv和writev这两个函数可以指定一个缓冲区的向量以输入或输出数据;
    • recvmsg和sendmsg在其他I/O函数的所有功能基础上结合了新的接收和发送辅助数据的能力。

    一、套接口超时

    有三种方法给套接口上的I/O操作设置超时。

    1. 调用alarm,在到达指定事件时产生SIGALRM信号。这涉及到信号处理,这一点随不同实现而变化,而且可能与进程中其他已有的alarm调用冲突。
    2. 使用select阻塞在等待I/O上,select内部有一个时间限制,以此代替在read或write调用上阻塞。
    3. 使用新的SO_RCVTIMEO和SO_SNDTIMEO套接口选项。这种方法存在一个问题,即并不是所有的实现都能支持这两个套接口选项。

    二、recv和send函数

    #include <sys/socket.h>
    
    ssize_t recv(int sockfd, void *buff, size_t nbytes, int flags);
    ssize_t send(int sockfd, const void *buff, size_t nbytes, int flags);
    返回:成功返回读入或写出的字节数,出错返回-1
    前三个参数与read和write相同,flags的值或为0,或是一个或多个常值的逻辑或构成
    MSG_DONTROUTE:不查路由表
    MSG_DONTWAIT:本操作不阻塞
    MSG_OOB:发送或接收带外数据
    MSG_PEEK:查看外来的消息
    MSG_WAITTALL:等待所有数据

    如果进程需要让内核来更新标志,就必须用recvmsg代替recv或recvfrom。

    三、readv和writev函数

    #include <sys/uio.h>
    
    ssize_t readv(int filedes, const struct iovec *iov, int iovcnt);
    ssize_t writev(int filedes, const struct iovec *iov, int iovcnt);
    返回:读到或写出的字节数,出错时为-1
    struct iovec {
        void *iov_base;      /* starting address of buffer */
        size_t iov_len;      /* size of buffer */
    };

    readv和writev可用于任何描述字,不仅限于套接口描述字。

    四、recvmsg和sendmsg函数

    实际上,可以用recvmsg代替read、readv、readcv和recvfrom。

    #include <sys/socket.h>
    
    ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
    ssize_t sendmsg(int sockfd, struct msghdr *msg, int flags);
    返回:成功时为读入或写出的字符数,出错-1
    
    struct msghdr {
        void *msg_name;                 /* protocol address */用于未经连接的套接口
        socklen_t msg_namelen;          /* size of protocol address */未经连接的套接口
        struct iovec *msg_iov;          /* scatter/gather array */指明输入或输出的缓冲区数组
        size_t msg_iovlen;              /* # elements in msg_iov */
        void *msg_control;   /* ancillary data, must be aligned for a cmsghdr structure */
        socklen_t msg_controllen;       /* length of ancillary data */指明可选辅助数据的位置和大小
        int msg_flags;                  /* flags returned by recvmsg() */标志变量
    };
        

     辅助数据由一个或多个辅助数据对象组成,每个对象一个cmsghdr结构开头,该结构在<sys/socket.h>中定义

    struct cmsghdr{
        socklen_t cmsg_len;    /* length in bytes, including this structure */字节长度
        int cmsg_level;        /* originating protocol */初始协议
        int cmsg_type;         /* protocol-specific type */协议指定类型
                               /* followed by unsigned char cmsg_data[] */真正的控制消息数据
    };

     

    因为由recvmsg返回的辅助数据可以包括任意数目的辅助数据对象,为了对应用程序屏蔽可能出现的填充字节。

    在<sys/socket.h>中定义了以下五个宏,以简化对辅助数据的处理。

    #include <sys/socket.h>
    #include <sys/param.h>    /* for ALIGN macro on many implementations */
    struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *mhdrptr);
    返回:指向第一个cmsghdr结构的指针,无辅助数据时为NULL
    struct cmsghdr *CMSG_NXTHDR(struct msghdr *mhdrptr, struct cmsghdr *cmsgptr);
    返回:指向下一个cmsghdr结构的指针,不再有辅助数据对象时为NULL
    unsigned char *CMSG_DATA(struct cmsghdr *cmsgptr);
    返回:指向与cmsghdr结构关联的数据的第一个字节的指针
    unsigned int CMSG_LEN(unsigned in length);
    返回:给定数量下存储在cmsg_len中的值
    unsigned int CMSG_SPACE(unsigned int length);
    返回:给定数据量下一个辅助数据对象的总大小

    一些使用例子:
    struct msghdr msg;
    struct cmsghdr *cmptr;

    cmptr = CMSG_FIRSTHDR(&msg);
    cmptr->cmsg_len = CMSG_LEN(sizeof(int));
    cmptr->cmsg_level = SOL_SOCKET;
    cmptr->cmsg_type = SCM_RIGHTS; /* send file description */
    *((int *)CMSG_DATA(cmptr)) = sendfd;

     使用msghdr的列子:

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <errno.h>
     5 #include <string.h>
     6 #include <sys/types.h>
     7 #include <sys/socket.h>
     8 
     9 int main(int argc, char *argv[])
    10 {
    11     int ret;
    12     int sock[2];
    13     struct msghdr msg;
    14     struct iovec iov[1];
    15     char send_buf[100] = "Hello";
    16 
    17     struct msghdr msgr;
    18     struct iovec iovr[1];
    19     char recv_buf[100];
    20 
    21     ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, sock);
    22     if(ret == -1) {
    23         printf("socketpair err
    ");
    24         return 1;
    25     }
    26 
    27     /* sock[1] send data to local */
    28     bzero(&msg, sizeof(msg));
    29     msg.msg_name = NULL;
    30     msg.msg_namelen = 0;
    31     iov[0].iov_base = send_buf;
    32     iov[0].iov_len = sizeof(send_buf);
    33     msg.msg_iov = iov;
    34     msg.msg_iovlen = 1;
    35 
    36     printf("Starting send data: 
    ");
    37     printf("Send data: %s
    ", send_buf);
    38     ret = sendmsg(sock[1], &msg, 0);
    39     if(ret == -1) {
    40         printf("sendmsg err
    ");
    41         return -1;
    42     }
    43     printf("Send successful
    ");
    44 
    45     /* sock[0] recv data to local */
    46     bzero(&msgr, sizeof(msgr));
    47     msgr.msg_name = NULL;
    48     msgr.msg_namelen = 0;
    49     iovr[0].iov_base = recv_buf;
    50     iovr[0].iov_len  = sizeof(recv_buf);
    51     msgr.msg_iov = iovr;
    52     msgr.msg_iovlen = 1;
    53     ret = recvmsg(sock[0], &msgr, 0);
    54     if(ret == -1) {
    55         printf("recvmsg err
    ");
    56         return -1;
    57     }
    58     printf("Recv successful
    ");
    59     printf("Recv data: %s
    ", recv_buf);
    60 
    61     close(sock[0]);
    62     close(sock[1]);
    63 
    64     return 0;
    65 }

    运行结果:

    $ ./a.out
    Starting send data: Send data: Hello Send successful Recv successful Recv data: Hello
    无欲速,无见小利。欲速,则不达;见小利,则大事不成。
  • 相关阅读:
    browser-sync events.js:85 throw er; // Unhandled 'error' event
    js判断页面放大缩小
    uniapp小实例-新闻列表及详情
    uniapp 分享接口
    uniapp--第三方登录 小程序登录
    vue h5转换uni-app指南(vue转uni、h5转uni)
    uniapp详解及配置
    uniapp快速上手
    Vuex 的项目实例11 列表数据的按需切换
    Vuex 的项目实例10 底部按钮高亮切换
  • 原文地址:https://www.cnblogs.com/ch122633/p/8523244.html
Copyright © 2020-2023  润新知