客户端与服务器多消息发送问题:连接过早关闭需维持长连接
解决Windows Socket长连接多消息交互问题
嘿,我明白你的问题了——现在你的客户端和服务器只能完成一次消息收发就断开,想要改成能保持连接、连续发送多条消息对吧?这问题其实出在单次交互后就直接关闭套接字的逻辑上,咱们调整一下循环结构就能搞定。
核心修改思路
不管是服务器还是客户端,都需要在成功建立连接后进入一个持续的交互循环,直到主动触发断开(比如用户输入退出指令)或者出现网络错误才关闭连接。
修改后的服务器代码
#undef UNICODE #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <winsock2.h> #include <ws2tcpip.h> #include <stdlib.h> #include <stdio.h> // 链接Ws2_32.lib #pragma comment(lib, "Ws2_32.lib") #define DEFAULT_BUFLEN 512 #define DEFAULT_PORT "27015" int __cdecl main(void) { WSADATA wsaData; int iResult; SOCKET ListenSocket = INVALID_SOCKET; SOCKET ClientSocket = INVALID_SOCKET; struct addrinfo *result = NULL; struct addrinfo hints; int iSendResult; char recvbuf[DEFAULT_BUFLEN]; int recvbuflen = DEFAULT_BUFLEN; // 初始化Winsock iResult = WSAStartup(MAKEWORD(2,2), &wsaData); if (iResult != 0) { printf("WSAStartup failed: %d\n", iResult); return 1; } ZeroMemory(&hints, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = AI_PASSIVE; // 解析服务器地址和端口 iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result); if (iResult != 0) { printf("getaddrinfo failed: %d\n", iResult); WSACleanup(); return 1; } // 创建监听套接字 ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol); if (ListenSocket == INVALID_SOCKET) { printf("socket failed: %ld\n", WSAGetLastError()); freeaddrinfo(result); WSACleanup(); return 1; } // 绑定套接字 iResult = bind( ListenSocket, result->ai_addr, (int)result->ai_addrlen); if (iResult == SOCKET_ERROR) { printf("bind failed: %d\n", WSAGetLastError()); freeaddrinfo(result); closesocket(ListenSocket); WSACleanup(); return 1; } freeaddrinfo(result); // 开始监听 iResult = listen(ListenSocket, SOMAXCONN); if (iResult == SOCKET_ERROR) { printf("listen failed: %d\n", WSAGetLastError()); closesocket(ListenSocket); WSACleanup(); return 1; } // 接受客户端连接 ClientSocket = accept(ListenSocket, NULL, NULL); if (ClientSocket == INVALID_SOCKET) { printf("accept failed: %d\n", WSAGetLastError()); closesocket(ListenSocket); WSACleanup(); return 1; } // 关闭监听套接字,因为我们只处理一个客户端连接 closesocket(ListenSocket); // ===== 关键修改:进入持续接收循环 ===== do { // 接收客户端消息 iResult = recv(ClientSocket, recvbuf, recvbuflen, 0); if (iResult > 0) { printf("Received %d bytes: %s\n", iResult, recvbuf); // 回显消息给客户端 iSendResult = send( ClientSocket, recvbuf, iResult, 0 ); if (iSendResult == SOCKET_ERROR) { printf("send failed: %d\n", WSAGetLastError()); closesocket(ClientSocket); WSACleanup(); return 1; } printf("Sent %d bytes back to client\n", iSendResult); // 清空缓冲区,避免残留数据 ZeroMemory(recvbuf, recvbuflen); } else if (iResult == 0) { printf("Client closing connection\n"); } else { printf("recv failed: %d\n", WSAGetLastError()); closesocket(ClientSocket); WSACleanup(); return 1; } } while (iResult > 0); // 直到客户端断开(iResult=0)或出错才退出循环 // 关闭客户端套接字 iResult = shutdown(ClientSocket, SD_SEND); if (iResult == SOCKET_ERROR) { printf("shutdown failed: %d\n", WSAGetLastError()); closesocket(ClientSocket); WSACleanup(); return 1; } closesocket(ClientSocket); WSACleanup(); return 0; }
修改后的客户端代码
#undef UNICODE #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <winsock2.h> #include <ws2tcpip.h> #include <stdlib.h> #include <stdio.h> #pragma comment(lib, "Ws2_32.lib") #define DEFAULT_BUFLEN 512 #define DEFAULT_PORT "27015" #define DEFAULT_SERVER "127.0.0.1" int __cdecl main(void) { WSADATA wsaData; int iResult; SOCKET ConnectSocket = INVALID_SOCKET; struct addrinfo *result = NULL, *ptr = NULL, hints; char sendbuf[DEFAULT_BUFLEN]; char recvbuf[DEFAULT_BUFLEN]; int recvbuflen = DEFAULT_BUFLEN; // 初始化Winsock iResult = WSAStartup(MAKEWORD(2,2), &wsaData); if (iResult != 0) { printf("WSAStartup failed: %d\n", iResult); return 1; } ZeroMemory( &hints, sizeof(hints) ); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; // 解析服务器地址和端口 iResult = getaddrinfo(DEFAULT_SERVER, DEFAULT_PORT, &hints, &result); if (iResult != 0) { printf("getaddrinfo failed: %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: %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; } // ===== 关键修改:进入持续发送循环 ===== printf("Enter messages to send (type 'exit' to quit):\n"); while(1) { // 获取用户输入 fgets(sendbuf, DEFAULT_BUFLEN, stdin); // 去掉fgets带来的换行符 sendbuf[strcspn(sendbuf, "\n")] = '\0'; // 判断是否退出 if(strcmp(sendbuf, "exit") == 0) { printf("Closing connection...\n"); break; } // 发送消息到服务器 iResult = send( ConnectSocket, sendbuf, (int)strlen(sendbuf), 0 ); if (iResult == SOCKET_ERROR) { printf("send failed: %d\n", WSAGetLastError()); closesocket(ConnectSocket); WSACleanup(); return 1; } printf("Sent %d bytes: %s\n", iResult, sendbuf); // 接收服务器的回显 iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0); if (iResult > 0) { printf("Received %d bytes: %s\n", iResult, recvbuf); ZeroMemory(recvbuf, recvbuflen); } else if (iResult == 0) { printf("Server closed connection\n"); break; } else { printf("recv failed: %d\n", WSAGetLastError()); break; } } // 关闭套接字 iResult = shutdown(ConnectSocket, SD_SEND); if (iResult == SOCKET_ERROR) { printf("shutdown failed: %d\n", WSAGetLastError()); closesocket(ConnectSocket); WSACleanup(); return 1; } closesocket(ConnectSocket); WSACleanup(); return 0; }
关键修改点说明
服务器端:
- 在
accept()成功获取客户端套接字后,添加了一个do-while循环,持续调用recv()接收消息,直到客户端断开(iResult == 0)或者接收出错。 - 每次接收完消息后处理(比如回显),然后继续等待下一条消息,而不是直接关闭套接字。
- 在
客户端:
- 建立连接后进入一个
while(1)无限循环,让用户可以反复输入消息发送。 - 添加了退出条件:当用户输入
exit时,主动断开连接。 - 每次发送消息后接收服务器的响应,然后回到输入状态,保持连接不关闭。
- 建立连接后进入一个
这样修改后,客户端和服务器就能保持长连接,你可以连续发送多条消息,直到主动退出或者出现网络问题。
内容的提问来源于stack exchange,提问作者Jon
相关产品推荐
相关产品推荐

