套接字I/O

阻塞和非阻塞套接字

套接字的默认状态是阻塞的,可能阻塞的套接字调用可分为以下四类:

  1. 输入操作,包括read、readv、recv、recvfrom和recvmsg共5个函数。
    • 阻塞的TCP套接字:如果该套接字接收缓冲区中没有数据可读,进程将被投入睡眠,直到有一些数据(单个字节或一个TCP分节等)到达。
    • 阻塞的UDP套接字:如果该套接字接收缓冲区为空,对它调用输入函数的进程将被投入睡眠,直到有UDP数据报到达。
    • 非阻塞套接字:上述TCP和UDP输入操作不会被投入睡眠,而是立即返回一个EWOULDBLOCK错误。
  2. 输出操作,包括write、writev、send、sendto和sendmsg共5个函数。
    • 阻塞的TCP套接字:如果其发送缓冲区中没有空间,进程将被投入睡眠,直到发送缓冲区有一定空间并且进程请求的数据已全部复制到发送缓冲区后才返回。
    • 非阻塞TCP套接字:如果其发送缓冲区中没有空间,输出函数调用立即返回一个EWOULDBLOCK错误。如果其发送缓冲区中有一些空间,返回值将是内核能够复制到该缓冲区的字节数。
    • 阻塞的UDP套接字:由于UDP不存在真正的发送缓冲区,输出操作不会因TCP一样的原因而阻塞,但仍可能因其他原因阻塞。
  3. 接收外来连接,即accept函数。
    • 阻塞的TCP套接字:如果尚无新的连接到达,调用进程将被投入睡眠。
    • 非阻塞TCP套接字:如果尚无新的连接到达,accept调用将立即返回一个EWOULDBLOCK错误。
  4. 发起外出连接,即connect函数。
    • 阻塞的TCP套接字:调用进程至少花费一个到服务器的RTT时间。
    • 非阻塞TCP套接字:如果连接不能立即建立,会返回一个EINPROGRESS错误,但是能正常发起TCP的三路握手过程。

read和write

    #include <unistd.h>
    ssize_t read(int fildes, void *buff, size_t nbyte);
    ssize_t write(int fildes, const void *buff, size_t nbyte);

字节流套接字(TCP套接字)上调用read或write输入或输出的字节数可能比请求的数量少,这个现象的原因在于内核中用于套接字的缓冲区可能已达到了极限。这个现象在read一个字节流套接字时很常见,但是在write一个字节流套接字时只能在该套接字为非阻塞的前提下才出现。

recv和send

    #include <sys/socket.h>
    ssize_t recv(int sockfd, void *buff, size_t nbyte, int flags);
    ssize_t send(int sockfd, const void *buff, size_t nbyte, int flags);

recv和send的前三个参数等同于read和write。flag参数的值或为0,或为下图:
1
另外注意,flags参数是按值传递的,因此它只能用于从进程向内核传递标志,而内核无法向进程传回标志。

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);

    struct iovec {             /* Scatter/gather array items */
           void  *iov_base;    /* Starting address */
           size_t iov_len;     /* Number of bytes to transfer */
    };

这两个函数类似read和write,不过readv和writev允许单个系统调用读入到或写出自一个或多个缓冲区。iov参数是指向某个iovec结构数组的指针,iovec结构数组中元素的数目存在某个限制,POSIX要求在头文件<sys/uio.h>中定义IOV_MAX常值,而且其值至少为16。另外,writev是一个原子操作,由于一个4字节的write跟一个396字节的write可能触发Nagle算法,此时首选办法之一是针对这两个缓冲区调用writev。

recvfrom和sendto

#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buff, size_t nbytes, int flags, struct sockaddr *from, socklen_t *addrlen);
ssize_t sendto(int sockfd, const void *buff, size_t nbytes, int flags, const struct sockaddr *to, socklen_t *addrlen);

前三个参数:等同于read和write函数,而且sendto长度为0的数据报或者recvfrom返回0值都是可行的。
flag参数:与recv/send类型函数相关。
sendto的后两个参数:指向一个含有数据报接收者的协议地址(类似于connect)。
recvfrom的后两个参数:指向一个数据报发送者的协议地址(类似于accept)。
返回值:所接收数据报中的用户数据量大小。

recvmsg和sendmsg

    #include <sys/socket.h>
    ssize_t recvmsg(int sockfd, const struct msghdr *msg, int flags);
    ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

    struct msghdr {
           void         *msg_name;       /* optional address */
           socklen_t     msg_namelen;    /* size of address */
           struct iovec *msg_iov;        /* scatter/gather array */
           size_t        msg_iovlen;     /* # elements in msg_iov */
           void         *msg_control;    /* ancillary data, see below */
           size_t        msg_controllen; /* ancillary data buffer len */
           int           msg_flags;      /* flags on received message */
    };

    struct cmsghdr {
           socklen_t     cmsg_len;     /* data byte count, including hdr */
           int           cmsg_level;   /* originating protocol */
           int           cmsg_type;    /* protocol-specific type */
       /* followed by
           unsigned char cmsg_data[]; */
    };

这两个函数是最通用的I/O函数,实际上可把所有read、readv、recv和recvfrom调用替换成recvmsg调用。类似地,替换为sendmsg调用。这两个函数参数说明如下:

  • msg参数
    • msg_namemsg_namelen用于套接字未连接的场合(如未连接UDP),对于sendmsg调用,msg_name存放接收者的协议地址,msg_namelen是一个值参数;对于recvmsg调用,msg_name存放发送者的协议地址,msg_namelen是一个值-结果参数。如果无需指明协议地址,msg_name应置为空指针。
    • msg_iovmsg_iovlen类似于readv或writev的第二个和第三个参数。
    • msg_controlmsg_controllen指定辅助数据的位置和大小。msg_control成员指向的缓冲区被填以一个cmsghdr结构,其中msg_controllen对于recvmsg是一个值-结果参数。
    • msg_flags成员只有recvmsg才能使用,而sendmsg则忽略msg_flags成员。recvmsg被调用时,flags参数被复制到msg_flags成员,内核还依据recvmsg的结果更新msg_flags成员的值。
  • flags参数可设标志如下:
    2

I/O函数对比

1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值