如何修改Winsock客户端服务器代码以支持localhost/127.0.0.1连接
问题
已下载并编译微软Winsock客户端与服务器代码示例,同一计算机上运行时,客户端传入主机名或本地IPv4地址可正常连接,但传入"localhost"或"127.0.0.1"无法连接,需修改代码使这两种地址也能正常连接。
原因分析
问题核心在于服务器的地址监听配置:
- 原服务器代码将
hints.ai_family设为AF_INET,仅支持IPv4地址解析,且若系统中localhost优先解析为IPv6地址(::1),客户端会尝试连接未被监听的IPv6端口,导致失败。 - 若服务器实际绑定的是特定网卡的IPv4地址而非通配地址
0.0.0.0,回环地址127.0.0.1也无法匹配服务器监听端口。
修改方案
方案一:修改服务器,支持双栈监听(推荐)
让服务器同时监听IPv4和IPv6的所有本地地址,覆盖回环地址:
- 将
hints.ai_family从AF_INET改为AF_UNSPEC,允许解析IPv4和IPv6地址。 - 新增循环逻辑,为
getaddrinfo返回的每个地址创建并绑定监听套接字,确保所有可用地址都被监听。 - 对IPv6套接字禁用
IPV6_V6ONLY选项,使其可同时接受IPv4连接。
方案二:修改客户端,仅解析IPv4地址
如果只需支持IPv4连接,限制客户端仅解析IPv4地址:
将客户端代码中的hints.ai_family从AF_UNSPEC改为AF_INET,这样localhost会直接解析为127.0.0.1,匹配服务器的IPv4监听端口。
修改后的代码
修改后的服务器代码(方案一)
#undef UNICODE #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <winsock2.h> #include <ws2tcpip.h> #include <stdlib.h> #include <stdio.h> // Need to link with Ws2_32.lib #pragma comment (lib, "Ws2_32.lib") #define DEFAULT_BUFLEN 512 #define DEFAULT_PORT "27015" // 创建并配置单个监听套接字 SOCKET setup_listen_socket(struct addrinfo *addr) { SOCKET ListenSocket = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); if (ListenSocket == INVALID_SOCKET) { printf("socket failed with error: %ld\n", WSAGetLastError()); return INVALID_SOCKET; } // 禁用IPv6套接字的V6ONLY选项,允许接受IPv4连接 if (addr->ai_family == AF_INET6) { DWORD v6only = 0; if (setsockopt(ListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&v6only, sizeof(v6only)) == SOCKET_ERROR) { printf("setsockopt IPV6_V6ONLY failed with error: %d\n", WSAGetLastError()); closesocket(ListenSocket); return INVALID_SOCKET; } } if (bind(ListenSocket, addr->ai_addr, (int)addr->ai_addrlen) == SOCKET_ERROR) { printf("bind failed with error: %d\n", WSAGetLastError()); closesocket(ListenSocket); return INVALID_SOCKET; } if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR) { printf("listen failed with error: %d\n", WSAGetLastError()); closesocket(ListenSocket); return INVALID_SOCKET; } printf("Listening on %s\n", addr->ai_family == AF_INET ? "IPv4" : "IPv6"); return ListenSocket; } int __cdecl main(void) { WSADATA wsaData; int iResult; struct addrinfo *result = NULL; struct addrinfo hints; int iSendResult; char recvbuf[DEFAULT_BUFLEN]; int recvbuflen = DEFAULT_BUFLEN; // Initialize Winsock iResult = WSAStartup(MAKEWORD(2,2), &wsaData); if (iResult != 0) { printf("WSAStartup failed with error: %d\n", iResult); return 1; } ZeroMemory(&hints, sizeof(hints)); hints.ai_family = AF_UNSPEC; // 修改为AF_UNSPEC,支持IPv4和IPv6 hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = AI_PASSIVE; // Resolve the server address and port iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result); if ( iResult != 0 ) { printf("getaddrinfo failed with error: %d\n", iResult); WSACleanup(); return 1; } // 创建多个监听套接字,覆盖所有解析到的地址 SOCKET* listen_sockets = malloc(sizeof(SOCKET) * 10); int socket_count = 0; struct addrinfo *ptr = result; while (ptr != NULL) { SOCKET sock = setup_listen_socket(ptr); if (sock != INVALID_SOCKET) { listen_sockets[socket_count++] = sock; } ptr = ptr->ai_next; } freeaddrinfo(result); if (socket_count == 0) { printf("Failed to create any listening socket!\n"); WSACleanup(); return 1; } // 等待任意监听套接字的连接请求 fd_set read_fds; SOCKET ClientSocket = INVALID_SOCKET; while (ClientSocket == INVALID_SOCKET) { FD_ZERO(&read_fds); for (int i = 0; i < socket_count; i++) { FD_SET(listen_sockets[i], &read_fds); } iResult = select(0, &read_fds, NULL, NULL, NULL); if (iResult == SOCKET_ERROR) { printf("select failed with error: %d\n", WSAGetLastError()); for (int i = 0; i < socket_count; i++) { closesocket(listen_sockets[i]); } free(listen_sockets); WSACleanup(); return 1; } // 处理有连接请求的套接字 for (int i = 0; i < socket_count; i++) { if (FD_ISSET(listen_sockets[i], &read_fds)) { ClientSocket = accept(listen_sockets[i], NULL, NULL); if (ClientSocket == INVALID_SOCKET) { printf("accept failed with error: %d\n", WSAGetLastError()); } else { printf("Accepted connection from a client\n"); } break; } } } // 关闭所有监听套接字 for (int i = 0; i < socket_count; i++) { closesocket(listen_sockets[i]); } free(listen_sockets); // 接收并回显客户端数据 do { iResult = recv(ClientSocket, recvbuf, recvbuflen, 0); if (iResult > 0) { printf("Bytes received: %d\n", iResult); iSendResult = send( ClientSocket, recvbuf, iResult, 0 ); if (iSendResult == SOCKET_ERROR) { printf("send failed with error: %d\n", WSAGetLastError()); closesocket(ClientSocket); WSACleanup(); return 1; } printf("Bytes sent: %d\n", iSendResult); } else if (iResult == 0) printf("Connection closing...\n"); else { printf("recv failed with error: %d\n", WSAGetLastError()); closesocket(ClientSocket); WSACleanup(); return 1; } } while (iResult > 0); // 关闭发送方向 iResult = shutdown(ClientSocket, SD_SEND); if (iResult == SOCKET_ERROR) { printf("shutdown failed with error: %d\n", WSAGetLastError()); closesocket(ClientSocket); WSACleanup(); return 1; } // 清理资源 closesocket(ClientSocket); WSACleanup(); return 0; }
修改后的客户端代码(方案二)
#define WIN32_LEAN_AND_MEAN #include <windows.h> #include <winsock2.h> #include <ws2tcpip.h> #include <stdlib.h> #include <stdio.h> // Need to link with Ws2_32.lib, Mswsock.lib, and Advapi32.lib #pragma comment (lib, "Ws2_32.lib") #pragma comment (lib, "Mswsock.lib") #pragma comment (lib, "AdvApi32.lib") #define DEFAULT_BUFLEN 512 #define DEFAULT_PORT "27015" int __cdecl main(int argc, char **argv) { WSADATA wsaData; SOCKET ConnectSocket = INVALID_SOCKET; struct addrinfo *result = NULL, *ptr = NULL, hints; const char *sendbuf = "this is a test"; char recvbuf[DEFAULT_BUFLEN]; int iResult; int recvbuflen = DEFAULT_BUFLEN; // 校验参数 if (argc != 2) { printf("usage: %s server-name\n", argv[0]); return 1; } // 初始化Winsock iResult = WSAStartup(MAKEWORD(2,2), &wsaData); if (iResult != 0) { printf("WSAStartup failed with error: %d\n", iResult); return 1; } ZeroMemory( &hints, sizeof(hints) ); hints.ai_family = AF_INET; // 修改为AF_INET,仅解析IPv4地址 hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; // 解析服务器地址和端口 iResult = getaddrinfo(argv[1], DEFAULT_PORT, &hints, &result); if ( iResult != 0 ) { printf("getaddrinfo failed with error: %d\n", iResult); WSACleanup(); return 1; } // 尝试连接到解析到的地址,直到成功 for(ptr=result; ptr != NULL ;ptr=ptr->ai_next) { ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); if (ConnectSocket == INVALID_SOCKET) { printf("socket failed with error: %ld\n", WSAGetLastError()); WSACleanup(); return 1; } iResult = connect( ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen); if (iResult == SOCKET_ERROR) { closesocket(ConnectSocket); ConnectSocket = INVALID_SOCKET; continue; } break; } freeaddrinfo(result); if (ConnectSocket == INVALID_SOCKET) { printf("Unable to connect to server!\n"); WSACleanup(); return 1; } // 发送测试数据 iResult = send( ConnectSocket, sendbuf, (int)strlen(sendbuf), 0 ); if (iResult == SOCKET_ERROR) { printf("send failed with error: %d\n", WSAGetLastError()); closesocket(ConnectSocket); WSACleanup(); return 1; } printf("Bytes Sent: %ld\n", iResult); // 关闭发送方向 iResult = shutdown(ConnectSocket, SD_SEND); if (iResult == SOCKET_ERROR) { printf("shutdown failed with error: %d\n", WSAGetLastError()); closesocket(ConnectSocket); WSACleanup(); return 1; } // 接收服务器回显数据,直到连接关闭 do { iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0); if ( iResult > 0 ) printf("Bytes received: %d\n", iResult); else if ( iResult == 0 ) printf("Connection closed\n"); else printf("recv failed with error: %d\n", WSAGetLastError()); } while( iResult > 0 ); // 清理资源 closesocket(ConnectSocket); WSACleanup(); return 0; }
内容的提问来源于stack exchange,提问作者blogger13
相关产品推荐
相关产品推荐

