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
相关产品推荐
相关产品推荐

