2013-07-24 51 views
0

我使用WSARecvFrom函数。它看起来像这样:WSARecv从目标ip地址

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, ...); 

我可以读取来自event-> address的IP地址。但我怎样才能读取目标IP地址UDP数据包发送到(套接字绑定到INADDR_ANY - 它监听所有接口)?

UPDATE。我找到了解决方案(适用于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); 

回答

0

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

+0

感谢您的回复,但我找到了解决方案:可以使用WSARecvMsg函数获得目标IP(自Windows XP以来可用,但在XP上它给了我例外,在Win 7和Win Server 2008上正常工作)。 – Vitaly