这两个函数只适用于套接字描述符。read、readv、recv 和 recvfrom 能用的地方,recvmsg 都能使用,而且,recvmsg 能提供更多的功能。同样的,各种 output 类型的函数都可以替换成 sendmsg 函数。
所以,recvmsg 和 sendmsg 是之前我们学过的读写类函数的究极形态。这么强大的函数,使用起来也会相当的复杂,在本文,我们只讨论它的一部分功能,剩下的功能,以后再说。
1. 函数原型
这两个函数,把大多数的参数都放进了一个 struct msghdr 类型的结构体。接下来,我们就来看看这个结构体的样子。
2. msghdr{}
图1 msghdr{} 结构体
- msg_name 和 msg_namelen
这两个成员仅用于无连接的场合(比如无连接的 UDP 套接字)。它相当于 recvfrom 和 sendto 的最后两个参数。
msg_name 指向一个套接字地址结构,msg_namelen 则是该套接字地址结构的大小。对于 sendto 来说,msg_name 存入接收者的地址。对于 recv_msg 来说,它用来接收返回值(值-结果参数)。
- msg_iov 和 msg_iovlen
这两个成员和 writev 和 readv 函数没什么两样。
- msg_control 和 msg_controllen
这两个成员表示辅助数据的位置和大小。对于 recvmsg 来说,这个参数也是一个值-结果参数。关于辅助数据的含义,我们先不讨论它,以后我们遇到它的时候,再进行详细学习。因为就目前来说,我们还用不上它。
- msg_flags
这个成员只针对 recvmsg 函数有效。
recvmsg 被调用时,recvmsg 的最后一个参数 flags 的值会被复制到 msg_flags 成员并交由内核使用。当 recvmsg 返回时,msg_flags 会保存返回结果,因此 msg_flags 也是一个值结果参数。msg_flags 的返回结果可能是下面这些值:MSG_EOR、MSG_OOB、MSG_BCAST、MSG_MCAST、MSG_TRUNC、MSG_CTRUNC、MSG_NOTIFCATION. 千万别晕菜,这些东西你目前来说不需要去理解它是干嘛的……所以我们目前忽略掉它。
sendmsg 要想使用 flags,只能使用 sendmsg 函数的最后一个参数,而不应该使用 msg_flags 成员。
3. 实验
根据第 2 节中的情况,我们掌握前 4 个 msghdr{} 的前 4 个成员:msg_name, msg_namelen, msg_iov, msg_iovlen 即可。实验中,我们将之前写的 udp 回射程序进行改写,将 sendto 和 recvfrom 改成 sendmsg 和 recvmsg 即可。
如果你已经 clone 过这个代码了,请使用 git pull
更新一下。本节程序所使用的程序路径是 unp/program/advcio/rwmsg
udp 回射程序主要修改了两个地方:
- 将客户端的 sendto 修改成了 sendmsg
- 将服务器的 recvfrom 修改成了 recvmsg
3.1 程序代码
- 服务器端关键代码
- 客户端代码
3.2 运行结果
- 客户端
图2 客户端运行结果
- 服务器
图3 服务器运行结果
4. 总结
- 掌握 recvmsg 和 sendmsg 的基本用法