获取本机收到的UDP数据包的目标地址

来源:互联网 发布:支持微信提现赚钱软件 编辑:程序博客网 时间:2024/05/16 14:34

本机收到UDP数据时,通过recvfrom函数可以直接获取发送者的地址:

int recvfrom(  __in          SOCKET s,  __out         char* buf,  __in          int len,  __in          int flags,  __out         struct sockaddr* from,  __in_out      int* fromlen);

但recvfrom没有提供获取包的目标地址的方法,不久前遇到一个需要获取收到包的目标地址的情况,并找到了解决办法。WinSock提供了WSARecvMsg函数来解决类似问题:

int WSARecvMsg(  __in          SOCKET s,  __in_out      LPWSAMSG lpMsg,  __out         LPDWORD lpdwNumberOfBytesRecvd,  __in          LPWSAOVERLAPPED lpOverlapped,  __in          LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);

示例代码如下:

#include <WinSock2.h>#include <MSWSock.h>#include <WS2tcpip.h>#include <Windows.h>#include <stdio.h>#pragma comment(lib, "Ws2_32.lib")int main(){    WSADATA wd;    WSAStartup(MAKEWORD(2, 2), &wd);    SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);    sockaddr_in sin;    sin.sin_family = AF_INET;    sin.sin_addr.s_addr = INADDR_ANY;    sin.sin_port = htons(3333);    if (SOCKET_ERROR == bind(sock, (sockaddr *)&sin, sizeof(sin)))    {        closesocket(sock);        return 0;    }    int optval = 1;    if (SOCKET_ERROR == setsockopt(sock, IPPROTO_IP, IP_PKTINFO, (char*)&optval, sizeof(int)))    {        closesocket(sock);        return 0;    }    GUID guidWSARecvMsg = WSAID_WSARECVMSG;    LPFN_WSARECVMSG lpfnWSARecvMsg = NULL;    DWORD dwOutSize = 0;    WSAIoctl(        sock,        SIO_GET_EXTENSION_FUNCTION_POINTER,        &guidWSARecvMsg,        sizeof(guidWSARecvMsg),        &lpfnWSARecvMsg,        sizeof(lpfnWSARecvMsg),        &dwOutSize,        NULL,        NULL        );    if (lpfnWSARecvMsg == NULL)    {        closesocket(sock);        return 0;    }    while (TRUE)    {        char szControlBuffer[1024] = "";        char szDataBuffer[1024] = "";        sockaddr_in sin_local;        WSABUF wsaBufData;        WSAMSG wsaMsg;        DWORD dwBytesRecved = 0;        wsaBufData.buf = szDataBuffer;        wsaBufData.len = sizeof(szDataBuffer);        wsaMsg.name = (sockaddr *)&sin_local;        wsaMsg.namelen = sizeof(sin_local);        wsaMsg.lpBuffers = &wsaBufData;        wsaMsg.dwBufferCount = 1;        wsaMsg.Control.buf = szControlBuffer;        wsaMsg.Control.len = sizeof(szControlBuffer);        wsaMsg.dwFlags = 0;        if (0 == lpfnWSARecvMsg(sock, &wsaMsg, &dwBytesRecved, NULL, NULL))        {            WSACMSGHDR *pCMsgHdr = WSA_CMSG_FIRSTHDR(&wsaMsg);            if (pCMsgHdr != NULL)            {                if (pCMsgHdr->cmsg_type == IP_PKTINFO)                {                    IN_PKTINFO *pPktInfo = (IN_PKTINFO *)WSA_CMSG_DATA(pCMsgHdr);                    if (pPktInfo != NULL)                    {                        printf(                            "%d.%d.%d.%d:%d->%d.%d.%d.%d:%d %s\n",                            sin_local.sin_addr.S_un.S_un_b.s_b1,                            sin_local.sin_addr.S_un.S_un_b.s_b2,                            sin_local.sin_addr.S_un.S_un_b.s_b3,                            sin_local.sin_addr.S_un.S_un_b.s_b4,                            ntohs(sin_local.sin_port),                            pPktInfo->ipi_addr.S_un.S_un_b.s_b1,                            pPktInfo->ipi_addr.S_un.S_un_b.s_b2,                            pPktInfo->ipi_addr.S_un.S_un_b.s_b3,                            pPktInfo->ipi_addr.S_un.S_un_b.s_b4,                            3333,                            szDataBuffer                            );                    }                }            }        }        else        {            break;        }    }    closesocket(sock);    return 0;}

运行效果如下,可以审计到各个本机地址,甚至环回地址也能精确捕获:


WSARecvMsg从Windows2000后都可用,与此对应的WSASendMsg函数却是从vista平台才可以提供的。linux平台中有个recvmsg函数应该可以达到类似的目的。本文中的程序在vs2008中编译通过。


原创粉丝点击