#includessize_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 的 3 个参数,flags 参数的值或为 0,或为下表中的一个或多个常值的逻辑或。
其中各标志选项的含义如下。
* MSG_DONTROUTE:告知内核目的主机在某个直接连接的本地网络上,而无需查找路由表。这可使用本标志针对单个输出操作开启,也可使用 SO_DONTROUTE 套接字选项(见 通用套接字选项)针对某个给定套接字上的所有输出操作开启。
* MSG_DONTWAIT:临时将当个 I/O 操作指定为非阻塞。
* MSG_OOB:对于 send,表示即将发送带外数据;对于 recv,表示即将读入的是带外数据。后面会讲到,TCP 连接上只有一个字节可以作为带外数据发送。
* MSG_PEEK:适用于 recv 和 recvfrom 函数,它允许我们查看已可读取的数据,而且系统不会在 recv 或 recvfrom 返回后丢弃这些数据。
* MSG_WAITALL:告知内核不要在尚未读入请求数目的字节前让一个读操作返回。当然,当(a)捕获一个信号,(b)连接被终止,(c)套接字发生错误时仍有可能返回比所请求的字节数要少的数据。
另有一些标志适用于 TCP/IP 以外的协议族,比如 OSI 的传输层是基于记录的(TCP 是基于字节流),其输出操作支持 MSG_EOR 标志,用于指示逻辑记录的结束。
最后,这两个函数的 flags 参数都是按值传递的,内核无法向进程传回标志,因此如果一个进程需要由内核更新标志,它就必须调用更为通用的 recvmsg 和 sendmsg 函数。
#includessize_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 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() };
在 msghdr 结构的成员变量之中:
msg_name 和 msg_namelen 用于套接字未连接的场合(如未连接的 UDP 套接字),msg_name 指向一个套接字地址结构,用来存放接收者(对于 sendmsg)或发送者(对于 recvmsg)的协议地址。如果无需指明协议地址(如对于 TCP 或已连接 UDP 套接字),应将其置为空指针。msg_namelen 对于 sendmsg 是一个值参数,对于 recvmsg 则是一个值-结果参数,表示协议地址的长度。
msg_iov 和 msg_iovlen 用于指定输入或输出缓冲区数组,类似于 readv 或 writev 的第二个和第三个参数(见 readv/writev 函数及存储映射 I/O)。msg_control 和 msg_controllen 用于指定可选的辅助数据的位置和大小,这会在后面介绍。
这里还需要区分 recvmsg 和 sendmsg 的 flags 参数和 msghdr 结构的 msg_flags 成员。
1、只有 recvmsg 使用 msg_flags 成员。在调用 recvmsg 时,flags 参数会被复制到 msg_flags 成员,并由内核使用其值驱动接收处理过程。内核还依据 recvmsg 的结果更新 msg_flags 成员的值。
2、sendmsg 会忽略 msg_flags 成员,因为它直接使用 flags 参数驱动发送处理过程。这意味着如果想在某个 sendmsg 调用中设置 MSG_DONTWAIT 等标志,要设置的应该是 flags 参数,而非 msg_flags 成员。
下表汇总了内核为输入和输出函数检查的 flags 参数值以及 recvmsg 可能返回的 msg_flags 成员值。
其中,内核只检查而不返回前 4 个标志,既检查又返回接下来的 2 个标志,不检查而只返回后 4 个标志。recvmsg 返回的 7 个标志解释如下。
* MSG_BCAST:它的返回条件是本数据报作为链路层广播收取或者其目的 IP 地址是一个广播地址。它是比用 IP_RECVDSTADDR 套接字选项来判定一个 UDP 数据报是否发往某个广播地址的更好方法。
* MSG_MCAST:它的返回条件是本数据报作为链路层多播收取。
* MSG_TRUNC:它的返回条件是本数据报被截断,即内核预备返回的数据超过进程事先分配的空间(所有 iov_len 成员之和)。
* MSG_CTRUNC:它的返回条件是本数据报的辅助数据被截断,即内核预备返回的辅助数据超过进程事先分配的空间(msg_controllen)。
* MSG_EOR:它的返回条件是返回数据结束一个逻辑记录。
* MSG_OOB:它绝不为 TCP 带外数据返回,用于其他协议族(如 OSI)。
* MSG_NOTIFICATION:它由 SCTP 接收者返回,指示读入的消息是一个事件通知,而不是数据消息。