• Windows 系统下怎么获取 UDP 本机地址


    Windows 系统下怎么获取 UDP 本机地址

    我们知道 UDP 获取远端地址非常简单,通常接口 recvfrom 就可以直接获取到远端的地址和端口;如果获取 UDP 的本机地址就需要点特殊处理了,特别是本机有多网卡的情况下,我们想知道是那个 IP 接收的 UDP 包。对于 linux 我们知道,现在有了对应的解决方法,就是利用套接字选项 IP_PKTINFO 和 recvmsg 接口,就能轻松完成这个动作。

    const int on = 1;
    // 开启获取包信息 , 结果存放在辅助数据当中
    setsockopt(sock,IPPROTO_IP,IP_PKTINFO,&on,sizeof(on));
    ...
    // 接收数据包
    if ((retvalue=recvmsg(sock,&msg,0)) < 0){
        break;
    }
    //开始获取辅助数据,由于辅助数据可以是一个也可以是一个数组,因此循环;
    for ( pcmsg = CMSG_FIRSTHDR(&msg) ; pcmsg != NULL ; pcmsg = CMSG_NXTHDR(&msg,pcmsg) ) {
        //判断是否是包信息
        if ( pcmsg->cmsg_level == IPPROTO_IP &&
            pcmsg->cmsg_type == IP_PKTINFO ) {
            //获取我们的自定义数据 struct in_pktinfo ;
            unsigned char * pData = CMSG_DATA(pcmsg);
            struct in_pktinfo * pInfo = (struct in_pktinfo *)pData;
            //转换
            inet_ntop(AF_INET,&pInfo->ipi_addr,dst_ip_buf,sizeof(dst_ip_buf));
            inet_ntop(AF_INET,&pInfo->ipi_spec_dst,route_ip_buf,sizeof(route_ip_buf));
            //下面都是打印信息
            printf("client_addr:%s,port:%d\n",inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
            printf("route ip :%s, dst ip:%s , ifindex:%d\n" , route_ip_buf,dst_ip_buf, pInfo->ipi_ifindex);
            recvbuf[retvalue] = 0;
            printf("recv bytes:%d , recvbuf:%s \n", retvalue, recvbuf);
        }
    }
    

    Windows 系统下该怎么处理?

    其实 Windows 系统下也是类似的操作,套接字选项也是需要开启 IP_PKTINFO 选项,但接收函数 recvmsg 是 linux 系统的函数,windows 系统的对应函数是 WSARecvMsg,利用此接口,我们也能轻松实现获取 UDP 包本机地址的需求

    啥都没代码有说服力 ( 代码有点烂,凑合看吧 )

    #include 
    #include 
    #include 
    #include 
    #include 
    
    #pragma comment(lib, "ws2_32.lib")
    
    typedef unsigned char uint8_t;
    LPFN_WSARECVMSG WSARecvMsg = nullptr;
    
    void get_wsarecvmsg_fptr(void)
    {
        DWORD dwBytesRecvd = 0;
        GUID guidWSARecvMsg = WSAID_WSARECVMSG;
        SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
        WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER,
            &guidWSARecvMsg, sizeof(guidWSARecvMsg),
            &WSARecvMsg, sizeof(WSARecvMsg),
            &dwBytesRecvd, NULL, NULL);
        closesocket(sock);
    }
    
    int recv_localaddr(SOCKET s, uint8_t* buf, size_t buf_sz,
        struct sockaddr_in* remote_addr,
        struct sockaddr_in* local_addr)
    {
        DWORD bytes_received;
        WSAMSG msg = { 0 };
        WSABUF sbuf = { 0 };
        uint8_t cmdbuf[512];
        WSACMSGHDR* cmsg;
        PIN_PKTINFO pi;
    
        sbuf.buf = (char FAR*)buf;
        sbuf.len = (u_long)buf_sz;
        msg.lpBuffers = &sbuf;
        msg.dwBufferCount = 1;
        msg.name = (LPSOCKADDR)remote_addr;
        msg.namelen = sizeof(*remote_addr);
        msg.Control.buf = (char FAR*)cmdbuf;
        msg.Control.len = (u_long)sizeof(cmdbuf);
    
        /* Receive a packet */
        (WSARecvMsg)(s, &msg, &bytes_received, NULL, NULL);
    
        /* Parse the header info, look for the local address */
        cmsg = WSA_CMSG_FIRSTHDR(&msg);
        for ( ; cmsg != NULL; cmsg = WSA_CMSG_NXTHDR(&msg, cmsg) ) {
            if ((cmsg->cmsg_level == IPPROTO_IP) &&
                (cmsg->cmsg_type == IP_PKTINFO)) {
                char ipbuf[128] = { 0 };
                size_t iplen = 128;
                pi = (PIN_PKTINFO)WSA_CMSG_DATA(cmsg);
                local_addr->sin_family = AF_INET;
                local_addr->sin_addr = pi->ipi_addr;
                printf("local ip: %s, local port: %d\n", 
                    inet_ntop(AF_INET, &(local_addr->sin_addr), ipbuf, iplen), ntohs(local_addr->sin_port));
                printf("recv msg: %s", buf);
                break;
            }
        }
    
        return (int)bytes_received;
    }
    
    int main(int argc, char* argv[])
    {
        WSADATA wsaData = {};
        if ( WSAStartup(MAKEWORD(2, 1), &wsaData) == -1 ) {
            return -1;
        }
        get_wsarecvmsg_fptr();
    
        SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);
        struct sockaddr_in serv_addr, cli_addr;
        memset(&serv_addr, 0, sizeof(serv_addr));
        memset(&cli_addr, 0, sizeof(cli_addr));
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_port = htons(8090);
        serv_addr.sin_addr.s_addr = 0;
        if (bind(sock, (sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
            closesocket(sock);
            WSACleanup();
            return -1;
        }
    
        int sockopt = 1;
        setsockopt(sock, IPPROTO_IP, IP_PKTINFO, (char*)&sockopt, sizeof(sockopt));
    
        size_t length = 2048;
        char buffer[2048] = { 0 };
        recv_localaddr(sock, (uint8_t*)buffer, length, &cli_addr, &serv_addr);
        closesocket(sock);
        WSACleanup();
        return 0;
    }
    
  • 相关阅读:
    Java实现二叉树的三种遍历方式
    [论文阅读] Center-based 3D Object Detection andTracking-CenterPoint
    Java基础教学 日期类Api导学
    PIMPL技巧
    es小记(copy_to)
    Netty服务端启动的整体流程-基于源码4.1.96Final分析
    VsCode预览Geojson数据
    Word控件Spire.Doc 【段落处理】教程(七):如如何通过在 C# 中附加 HTML 代码来设置 word 项目符号样式
    读像火箭科学家一样思考笔记03_第一性原理(上)
    Linux安装JDK
  • 原文地址:https://www.cnblogs.com/superconvert/p/17240476.html