windows好像没有实现recvmsg功能。
事实上,它确实如此(嗯,无论如何,是等价的):
WSARecvMsg 函数
类似的功能已被修改,如 WSRevcMsg,但这些功能仅包含在 Windows Vista 及更高版本中。
您不能总是相信 MSDN 文档。当 Microsoft 正式放弃对某个 Windows 版本(如 XP)的支持时,它往往会从 MSDN 中删除对该版本的引用,包括“最低支持……”API 函数的要求(这让许多程序员烦恼)。关键点是支持的。微软没有support较旧的 Windows 版本。
WSARecvMsg()
首次在 XP 中引入,于 2014 年停产,MSDN 文档中删除了许多对它的引用(这里是proof从 2013 年起,当WSARecvMsg()
文档指出 XP 而不是 Vista 是“最低限度支持的 client").
如中所述WSARecvFrom()
文档:
Note函数指针为WSARecvMsg
函数必须在运行时通过调用来获得WSAIoctl()函数与SIO_GET_EXTENSION_FUNCTION_POINTER
指定操作码。输入缓冲区传递给WSAIoctl
函数必须包含WSAID_WSARECVMSG
,一个全局唯一标识符(GUID),其值标识WSARecvMsg
扩展功能。成功时,返回的输出WSAIoctl
函数包含一个指向WSARecvMsg
功能。这WSAID_WSARECVMSG
GUID 定义在Mswsock.h头文件。
WSAID_WSARECVMSG
定义为:
#define WSAID_WSARECVMSG \
{0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22}}
aka {F689D7C8-6F1F-436B-8A53-E54FE351C322}
以文本格式。
例如:
SOCKET sckt = ...;
LPFN_WSARECVMSG lpWSARecvMsg = NULL;
GUID g = WSAID_WSARECVMSG;
DWORD dwBytesReturned = 0;
if (WSAIoctl(sckt, SIO_GET_EXTENSION_FUNCTION_POINTER, &g, sizeof(g), &lpWSARecvMsg, sizeof(lpWSARecvMsg), &dwBytesReturned, NULL, NULL) != 0)
{
// WSARecvMsg is not available...
}
然后,使用它:
BYTE buffer[...];
DWORD dwBytesRecv;
WSABUF msgbuf;
memset(&msgbuf, 0, sizeof(msgbuf));
msgbuf.len = sizeof(buffer);
msgbuf.buf = (char *) buffer;
// call WSA_CMSG_SPACE() once for each option you enable
// on the socket that can return data in WSARecvMsg()...
int size = 0;
if (... IP_PKTINFO is enabled ...)
size += WSA_CMSG_SPACE(sizeof(struct in_pktinfo));
if (... IPV6_PKTINFO is enabled ...)
size += WSA_CMSG_SPACE(sizeof(struct in6_pktinfo));
// other packet options as needed...
BYTE *controlbuf = (BYTE *) malloc(size);
SOCKADDR_STORAGE *addrbuf = (SOCKADDR_STORAGE *) malloc(sizeof(SOCKADDR_STORAGE));
WSAMSG msg;
memset(&msg, 0, sizeof(msg));
msg.name = (struct sockaddr *) addrbuf;
msg.namelen = sizeof(SOCKADDR_STORAGE);
msg.lpBuffers = &msgbuf;
msg.dwBufferCount = 1;
msg.Control.len = size;
msg.Control.buf = (char *) controlbuf;
if (lpWSARecvMsg(sckt, &msg, &dwBytesRecv, NULL, NULL) == 0)
{
// addrbuf contains the sender's IP and port...
switch (addrbuf->ss_family)
{
case AF_INET:
{
struct sockaddr_in *addr = (struct sockaddr_in*) addrbuf;
// use addr as needed...
break;
}
case AF_INET6:
{
struct sockaddr_in6 *addr = (struct sockaddr_in6*) addrbuf;
// use addr as needed...
break;
}
}
WSACMSGHDR *msghdr = WSA_CMSG_FIRSTHDR(&msg);
while (msghdr)
{
switch (msghdr->cmsg_type)
{
case IP_PKTINFO: // also IPV6_PKTINF
{
// must call setsockopt(sckt, IPPROTO_IP, IP_PKTINFO, TRUE) beforehand to receive this for IPv4
// must call setsockopt(sckt, IPPROTO_IPV6, IPV6_PKTINFO, TRUE) beforehand to receive this for IPv6
switch (addrbuf->ss_family)
{
case AF_INET:
{
struct in_pktinfo *pktinfo = (struct in_pktinfo *) WSA_CMSG_DATA(msghdr);
// use pktinfo as needed...
break;
}
case AF_INET6:
{
struct in6_pktinfo *pktinfo = (struct in6_pktinfo *) WSA_CMSG_DATA(msghdr);
// use pktinfo as needed...
break;
}
}
break;
}
// other packet options as needed...
}
msghdr = WSA_CMSG_NXTHDR(&msg, msghdr);
}
}
free(addrbuf);
free(controlbuf);