首先为一个I/O函数设置超时,这有三种方法。然后是三个read和write函数的变体:
- recv和send,他们可以把含有标志的第四个参数从进程传给内核;
- readv和writev这两个函数可以指定一个缓冲区的向量以输入或输出数据;
- recvmsg和sendmsg在其他I/O函数的所有功能基础上结合了新的接收和发送辅助数据的能力。
一、套接口超时
有三种方法给套接口上的I/O操作设置超时。
- 调用alarm,在到达指定事件时产生SIGALRM信号。这涉及到信号处理,这一点随不同实现而变化,而且可能与进程中其他已有的alarm调用冲突。
- 使用select阻塞在等待I/O上,select内部有一个时间限制,以此代替在read或write调用上阻塞。
- 使用新的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\n"); 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: \n"); 37 printf("Send data: %s\n", send_buf); 38 ret = sendmsg(sock[1], &msg, 0); 39 if(ret == -1) { 40 printf("sendmsg err\n"); 41 return -1; 42 } 43 printf("Send successful\n"); 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\n"); 56 return -1; 57 } 58 printf("Recv successful\n"); 59 printf("Recv data: %s\n", 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