1.套接字超时
涉及套接字上的I/O操作设置超时的方法有三种方法:
- 调用alarm,在到达指定时间时产生SIGALRM信号
- 使用select阻塞在等待I/O上,select内部有一个时间限制,以此代替在read或write调用上阻塞
- 使用新的SO_RCVTIMEO和SO_SNDTIMEO套接字选项
前两种技术可以用于任何描述字,而第三种只能用于套接口描述字。
2.recv和send函数
这两个函数和标准的read和write函数都类似,不过多了一个附加参数
#include <sys/socket.h>
sszie_t recv(int sockfd, void * buff, size_t nbytes, int flags);
ssize_t send(int sockfd, void * buff, size_t nbytes, int flags);
//返回: 成功返回读入或写出的字节数,出错返回-1
flag在设计上存在一个基本问题:它是按值传递的,而不是值-结果参数,因此它只能从进程向内核传递标志,内核不能向进程传递标志。
3.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
两个函数的第二个参数都是一个指向iovec结构的数组的指针,在
struct iovec {
void * iov_base; /* starting address of buffer */
size_t iov_len; /* size of buffer */
}
readv和writev函数可以用于任何描述字,不仅限于套接口描述字,而且writev是一个原子操作。
4.recvmsg和sendmsg函数
这两个函数是最通用的I/O函数,实际上,可以用recvmsg代替read, readv, recv, recvfrom. 类似的,各种输出函数可以用sendmsg取代。
#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
两个函数把大部分参数都包装到一个msghdr结构中:
struct msghdr {
void *msg_name; /* protocol address */
socklen_t msg_namelen; /* size of protocol address */
struct iovec *msg_iov; /* scatter/gather array */
int msg_iovlen; /* # elements in msg_iov */
void *msg_control; /* ancillary data (cmsghdr struct) */
socklen_t msg_controllen; /* length of ancillary data */
int msg_flags; /* flags returned by recvmsg() */
};
5.辅助数据
辅助数据(ancillary data)可以通过sendmsg和recvmsg这两个函数,使用msghdr结构中的msg_contorl和msg_controllen成员发送和接收。
辅助数据的另一种叫法是控制信息(control information)
辅助数据是一个或多个辅助数据对象组成,每个对象由一个cmsghdr结构开头,该结构在
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[] */
};
下图为在Unix域套接字上的cmsghdr结构:
6 排队的数据量
在不读出数据的情况下,如何知道一个套接口的接收队列中有多少数据可读呢?有三种方法:
- 如果在没有数据可读时还有其他事情要做,为了不阻塞在内核中,可以使用非阻塞I/O
- 如果想检查一下数据而使数据仍留在接收队列中,可以使用MSG_PEEK标志。如果想这样做,但又不能肯定是否有数据可读,可以把这个标志和非阻塞套接口相结合,或与MSG_DONTWAIT标志结合使用。
- 一些实现支持ioctl的FIONREAD命令
7.套接口和标准I/O
标准I/O库执行三种缓冲:
- 完全缓冲意味着只有在以下情况时才进行I/O:缓冲区满,进程明确地调用fflush或进程调用exit终止。标准I/O缓冲区大小通常为8192字节。
- 行缓冲意味着在以下情况时进行I/O:遇到一个换行符,进程调用fflush或进程调用exit终止。
- 不缓冲意味着每次调用标准I/O输出函数时都进行I/O
大多数Unix中标准I/O库的实现遵循了以下规则:
- 标准错误输出总是不缓冲。
- 标准输入和标准输出是全缓冲的。除非他们是一个终端设备,那样的话他们是行缓冲。
- 其他的流都是全缓冲的,除非他们是一个终端设备,那样的话他们是行缓冲。
既然套接口不是终端设备,上面str_echo函数的问题就在于输出流(fpout)是全缓冲的。
有两种解决方法:调用setvbuf将输出流强制成行缓冲的,或者在每次fputs之后调用fflush强制输出回射行。