獲取本機收到的UDP資料包的目標地址
阿新 • • 發佈:2019-02-01
本機收到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中編譯通過。