You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unix转Windows程序:TCP RAW Socket recvfrom报错10022求助

错误10022(WSAEINVAL)的解决办法

直接触发错误的原因:recvfrom参数不完整

你代码里的recvfrom调用缺失了最后一个必填参数——地址长度的指针,这是导致错误10022的直接原因:
原代码片段:

int data_size = recvfrom(sock, (char*)read_buffer, READ_BUFFER_SIZE, 0, (sockaddr*)&saddr,

修正后的正确写法:

int data_size = recvfrom(sock, (char*)read_buffer, READ_BUFFER_SIZE, 0, (sockaddr*)&saddr, &saddr_size);

recvfrom要求最后一个参数必须传入保存地址长度变量的指针,否则会触发「无效参数」错误。

其他需要修正的问题

  • WSACleanup调用时机错误:你的fuck_fd函数中,Windows下每次关闭套接字都调用WSACleanup是错误的。WSACleanup应当在整个程序退出时调用一次,与程序初始化时的WSAStartup配对。多次调用会导致后续Winsock API调用失败。修改fuck_fd:

    int fuck_fd(int fd)
    {
    #ifdef _WIN32
        closesocket(fd);
        // WSACleanup(); 移至程序退出阶段统一调用
    #else
        close(fd);
    #endif
    }
    

    同时,ncread函数内每次调用都执行WSAStartup也不合理,应将WSAStartup放到程序初始化阶段仅执行一次。

  • Windows原始套接字权限要求:在Windows下使用SOCK_RAW类型的套接字,程序必须以管理员权限运行,否则可能出现套接字创建失败或无法接收报文的情况。

  • TCP原始套接字平台差异:Windows与Unix的TCP原始套接字行为存在区别,默认情况下SOCK_RAW + IPPROTO_TCP仅能接收TCP报文头部,无法获取完整数据段;若要发送自定义报文还需设置IP_HDRINCL选项(你当前场景为接收,重点注意接收内容范围即可)。


你提供的代码整理

读取函数ncread

#define NESCA

int
ncread(const char* dest_ip, int recv_timeout_ms, unsigned char **buffer, bool debug,
      int dest_port, int source_port, bool packet_trace)
{
    /*Create a structure and pass the recipient's IP into it.*/
    struct in_addr dest; dest.s_addr = inet_addr(dest_ip);

    /*Time buffer.*/
    unsigned char *read_buffer = *buffer;

    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != NO_ERROR) {
        wprintf(L"WSAStartup failed with error %d\n", iResult);
        return -1;
    }
    int sock = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
    if (sock == -1) {return SOCKET_ERROR;}

#ifdef NESCA
    /*Function similar to the entry below*/
    int result = set_socket_timeout(sock, recv_timeout_ms, 1, 1);
#else
    struct timeval timeout;
    timeout.tv_sec = recv_timeout_ms / 1000;
    timeout.tv_usec = (recv_timeout_ms % 1000) * 1000;
    int result = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));
#endif
    if (result < 0)
    {
        std::cout << "timeout infinity\n\n";
        /*Timeout.*/
        fuck_fd(sock);
        return -1;
    }


    /*To compare the IPs.*/
    struct sockaddr saddr;
    int saddr_size = sizeof(saddr);
    auto start_time = std::chrono::steady_clock::now();

    /*An endless loop, on accepting any packets from the system at all.*/
    for (;;)
    {
       /*Receive packet to buffer.*/
       int data_size = recvfrom(sock, (char*)read_buffer, READ_BUFFER_SIZE, 0, (sockaddr*)&saddr,
       if (data_size == -1)
       {
            /*ERROR: 10022*/
            wprintf(L"recvfrom failed with error %d\n", WSAGetLastError());
            fuck_fd(sock);
            return READ_ERROR;
       }

       /*Obtaining the IP header of the received packet.*/
       struct ip_header *iph = (struct ip_header*)read_buffer;
       unsigned short iphdrlen = (iph->ihl) * 4;
       if (iphdrlen < 20)
       {
          fuck_fd(sock);
          return IP_HEADER_LEN_ERROR;
       }

       /*Retrieve the sender's IP from it.*/
       struct sockaddr_in source;
       memset(&source, 0, sizeof(source));
       source.sin_addr.s_addr = iph->saddr;

       /*Compare the IP to see if it matches the one we sent it to.
        * This is to discard other packets.*/
       if (source.sin_addr.s_addr != dest.s_addr)
       {
          if (debug) {std::cout << "Got the wrong package.\n";}

          /*A windowless for timeout might work here.*/
          auto current_time = std::chrono::steady_clock::now();
          auto elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(current_time - start_time).count();
          if (elapsed_time >= recv_timeout_ms)
          {
                  std::cout << "timeout infinity\n\n";
             fuck_fd(sock);
             return INFINITY_TIMEOUT_EXITED;
          }

          /*If it's the wrong one, it catches again until it times out,
           * on a windowless loop. Or until the right packet arrives.*/
          continue;
       }
       else 
       {
#ifdef NESCA
          if (packet_trace)
          {
            struct in_addr addr;
            addr.s_addr = iph->saddr;
            std::string source_ip = inet_ntoa(addr);
            addr.s_addr = iph->daddr;
            std::string dest_ip = inet_ntoa(addr);

            struct tcp_header *tcph = (struct tcp_header*)(buffer+ iphdrlen);
            unsigned short id = ntohs(iph->id);
            unsigned int seq = ntohl(tcph->seq);
            unsigned int iplen = ntohs(iph->tot_len);
            packet_trace1.lock();
            np3.nlog_packet_trace("RCVD", "TCP", source_ip, dest_ip, dest_port, source_port, "", iph->ttl, id, tcph->window, seq, iplen);
            packet_trace1.unlock();
          }
#endif
          /*If it's the right packet.
           * Fill the buffer with it.*/
          *buffer = read_buffer;
          fuck_fd(sock);
          return SUCCESS_READ;
       }
    }
    /*oh.*/
    fuck_fd(sock);
    return READ_ERROR;
}

关闭套接字函数

int fuck_fd(int fd)
{
#ifdef _WIN32
    WSACleanup();
    closesocket(fd);
#else
    close(fd);
#endif
}

内容的提问来源于stack exchange,提问作者lomaster

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 20:05:56