2013-07-24 2 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, ...); 

이벤트 -> 주소에서 보낸 사람 IP 주소를 읽을 수 있습니다. 하지만 어떻게 대상 IP 주소 UDP 패킷을 읽을 수 있습니까 (소켓은 INADDR_ANY에 바인드되었습니다 - 모든 인터페이스를 수신)?

업데이트. 해결책을 찾았습니다 (Windows Vista 이상). 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 이후부터 사용 가능하지만 Windows XP에서는 Win 7 및 Windows Server 2008이 제대로 작동 함). – Vitaly