0

我使用 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);
4

1 回答 1

-1

WSARecvFrom()绑定到 时无法获取目标 IP INADDR_ANY。如果您需要目标 IP,则必须为要接收的每个本地 IP 创建并绑定一个单独的套接字,然后您可以使用getsockname()来了解接收套接字绑定到哪个本地 IP。

于 2013-07-25T00:50:30.947 回答