我使用 WSARecvFrom 函数。它看起来像这样:
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(listen_port);
bind(s, reinterpret_cast<PSOCKADDR>(&addr), sizeof(addr));
...
WSARecvFrom(s, ..., reinterpret_cast<sockaddr *>(&event->address), &event->addressLength, ...);
我可以从事件->地址中读取来自 IP 地址。但是如何读取目标 IP 地址 UDP 数据包发送到(套接字绑定到 INADDR_ANY - 它侦听所有接口)?
更新。我找到了解决方案(适用于 Windows Vista 及更高版本)。我写了 2 个函数:WSARecvFromEx 允许获取目标 IP,WSASendToEx 允许发送“来自”IP:
//copyright (c) 2013 Vitaly. http://blog.coolsoftware.ru/
#if !(_WIN32_WINNT >= 0x0501)
typedef
INT
(PASCAL FAR * LPFN_WSARECVMSG) (
__in SOCKET s,
__inout LPWSAMSG lpMsg,
__out_opt LPDWORD lpdwNumberOfBytesRecvd,
__inout_opt LPWSAOVERLAPPED lpOverlapped,
__in_opt LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
#define WSAID_WSARECVMSG \
{0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22}}
#endif !(_WIN32_WINNT >= 0x0501)
#if !(_WIN32_WINNT >= 0x0600)
typedef
INT
(PASCAL FAR * LPFN_WSASENDMSG) (
__in SOCKET s,
__in LPWSAMSG lpMsg,
__in DWORD dwFlags,
__out_opt LPDWORD lpNumberOfBytesSent,
__inout_opt LPWSAOVERLAPPED lpOverlapped OPTIONAL,
__in_opt LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine OPTIONAL
);
#define WSAID_WSASENDMSG /* a441e712-754f-43ca-84a7-0dee44cf606d */ \
{0xa441e712,0x754f,0x43ca,{0x84,0xa7,0x0d,0xee,0x44,0xcf,0x60,0x6d}}
#endif !(_WIN32_WINNT >= 0x0600)
LPFN_WSARECVMSG pfWSARecvMsg = NULL;
LPFN_WSASENDMSG pfWSASendMsg = NULL;
int WSARecvFromEx(
SOCKET s,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesRecvd,
LPDWORD lpFlags,
struct sockaddr FAR * lpFrom,
LPINT lpFromlen,
char * pControlBuffer,
ULONG nControlBufferLen,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
{
if (pControlBuffer != NULL &&
nControlBufferLen >= sizeof(WSACMSGHDR))
{
memset(pControlBuffer, 0, sizeof(WSACMSGHDR));
}
else
{
return WSARecvFrom(s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpFrom, lpFromlen, lpOverlapped, lpCompletionRoutine);
}
if (pfWSARecvMsg == NULL)
{
return WSARecvFrom(s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpFrom, lpFromlen, lpOverlapped, lpCompletionRoutine);
}
else
{
WSAMSG Msg;
Msg.name = lpFrom;
Msg.namelen = lpFromlen ? *lpFromlen : 0;
Msg.lpBuffers = lpBuffers;
Msg.dwBufferCount = dwBufferCount;
Msg.Control.buf = pControlBuffer;
Msg.Control.len = nControlBufferLen;
Msg.dwFlags = lpFlags ? *lpFlags : 0;
return pfWSARecvMsg(s, &Msg, lpNumberOfBytesRecvd, lpOverlapped, lpCompletionRoutine);
}
} //WSARecvFromEx()
int WSASendToEx(
SOCKET s,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesSent,
DWORD dwFlags,
struct sockaddr FAR * lpTo,
int iTolen,
ULONG fromIp4,
char * pControlBuffer,
ULONG nControlBufferLen,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
{
int sum = 0;
if (fromIp4 != INADDR_ANY &&
pControlBuffer != NULL &&
nControlBufferLen >= WSA_CMSG_SPACE(sizeof(struct in_pktinfo)))
{
memset(pControlBuffer, 0, WSA_CMSG_SPACE(sizeof(struct in_pktinfo)));
WSACMSGHDR *pCMsgHdr = (WSACMSGHDR *)pControlBuffer;
pCMsgHdr->cmsg_level = IPPROTO_IP;
pCMsgHdr->cmsg_type = IP_PKTINFO;
pCMsgHdr->cmsg_len = WSA_CMSG_LEN(sizeof(struct in_pktinfo));
struct in_pktinfo *pktinfo = (struct in_pktinfo *)WSA_CMSG_DATA(pCMsgHdr);
pktinfo->ipi_addr.s_addr = htonl(fromIp4);
sum += WSA_CMSG_SPACE(sizeof(struct in_pktinfo));
}
else
{
return WSASendTo(s, lpBuffers, dwBufferCount, lpNumberOfBytesSent, dwFlags, lpTo, iTolen, lpOverlapped, lpCompletionRoutine);
}
if (pfWSASendMsg == NULL)
{
return WSASendTo(s, lpBuffers, dwBufferCount, lpNumberOfBytesSent, dwFlags, lpTo, iTolen, lpOverlapped, lpCompletionRoutine);
}
else
{
WSAMSG Msg;
Msg.name = lpTo;
Msg.namelen = iTolen;
Msg.lpBuffers = lpBuffers;
Msg.dwBufferCount = dwBufferCount;
Msg.Control.buf = pControlBuffer;
Msg.Control.len = sum;
Msg.dwFlags = dwFlags;
return pfWSASendMsg(s, &Msg, dwFlags, lpNumberOfBytesSent, lpOverlapped, lpCompletionRoutine);
}
} //WSASendToEx()
初始化:
if (dwMajorVersion >= 6)
{
if (pfWSARecvMsg == NULL)
{
// get WSARecvMsg pointer
GUID WSARecvMsg_GUID = WSAID_WSARECVMSG;
DWORD NumberOfBytes = 0;
if (WSAIoctl(socket_, SIO_GET_EXTENSION_FUNCTION_POINTER,
&WSARecvMsg_GUID, sizeof(WSARecvMsg_GUID),
&pfWSARecvMsg, sizeof(pfWSARecvMsg),
&NumberOfBytes, NULL, NULL) == SOCKET_ERROR)
{
printf("Could not get WSARecvMsg. WSAGetLastError returned [%i]", WSAGetLastError());
pfWSARecvMsg = NULL;
}
}
if (pfWSASendMsg == NULL)
{
// get WSASendMsg pointer
GUID WSASendMsg_GUID = WSAID_WSASENDMSG;
DWORD NumberOfBytes = 0;
if (WSAIoctl(socket_, SIO_GET_EXTENSION_FUNCTION_POINTER,
&WSASendMsg_GUID, sizeof(WSASendMsg_GUID),
&pfWSASendMsg, sizeof(pfWSASendMsg),
&NumberOfBytes, NULL, NULL) == SOCKET_ERROR)
{
printf("Could not get WSASendMsg. WSAGetLastError returned [%i]", WSAGetLastError());
pfWSASendMsg = NULL;
}
}
}
WSASendToEx 使用示例:
int result = WSASendToEx(event->socket_, &sendBufferDescriptor, 1, &dwSent, 0,
reinterpret_cast<sockaddr *>(&event->address), event->addressLength,
from, event->controlBuffer, sizeof(event->controlBuffer),
&event->overlapped, NULL);
WSARecvFromEx 的用法示例:
int result = WSARecvFromEx(event->socket_, &recvBufferDescriptor, 1,
&numberOfBytes, &recvFlags,
reinterpret_cast<sockaddr *>(&event->address), &event->addressLength,
event->controlBuffer, sizeof(event->controlBuffer),
&event->overlapped, NULL);