好的,我们来详细理解一下 Error 10053 和 Error 10054 这两个网络错误码,常见于 Windows 下的网络编程,特别是使用 Winsock 或 TCP 连接时。
1. Error 10053 — Software caused connection abort
含义
- 这个错误表示连接被本地软件(通常是你的程序或者操作系统)异常中止了。
- 通常是应用层主动关闭连接,或者由于网络中断、超时等原因,导致本地网络栈中断了连接。
常见原因
- 应用程序调用了
closesocket()
或者直接关闭了连接。 - 应用程序超时未响应,操作系统为了释放资源主动关闭连接。
- 防火墙或杀毒软件拦截了连接,强制关闭。
- 网络不稳定,导致本地检测到错误并关闭连接。
典型场景
- 客户端连接服务器时,中途客户端程序异常退出或者调用关闭。
- 长时间无数据传输,某些设备或者操作系统超时关闭了连接。
- 发送数据时本地网络栈检测到异常,终止连接。
2. Error 10054 — Connection reset by peer
含义
- 连接被对端(远程主机)强制关闭,表现为“连接重置”。
- 远程主机发送了 TCP RST(重置包),告诉本地主机连接异常被切断。
常见原因
- 远程服务器程序崩溃、重启或异常退出。
- 远程服务器主动调用关闭连接,且没有正常关闭流程。
- 网络设备(防火墙、路由器)主动拒绝或重置连接。
- 远程主机网络故障或重置连接。
典型场景
- 客户端发送数据到服务器,服务器进程已关闭连接,返回 RST 包。
- 远程主机超时未响应,主动断开连接。
- 远程主机的防火墙策略拒绝当前连接。
3. 简单对比总结
错误码 | 发生位置 | 关闭主动方 | 说明 |
---|---|---|---|
10053 | 本地 | 本地主机 | 本地软件主动中止连接 |
10054 | 远端 | 远程主机(对端) | 远程主机重置连接,强制关闭 |
4. 解决思路
- 对于 10053:
- 检查本地应用程序是否合理关闭了套接字。
- 排查防火墙、杀毒软件是否误拦截。
- 增加超时和重连机制,避免长时间无响应被关闭。
- 对于 10054:
- 检查服务器端程序是否异常退出或重启。
- 查看服务器防火墙设置,是否主动拒绝连接。
- 网络链路质量排查,避免中间设备断开。
好,给你准备了两个小工具示例,分别帮助你排查 Error 10053 和 Error 10054,以及示范如何在 Winsock 程序中捕获和处理这些错误。
1. 简单的网络状态检测脚本(Python)
这个脚本通过 TCP 连接目标服务器,检测连接是否正常,方便快速判断网络和服务器状态。
import socket
import sys
def test_connect(host, port):
try:
sock = socket.create_connection((host, port), timeout=5)
print(f"连接成功: {host}:{port}")
sock.close()
except socket.timeout:
print(f"连接超时: {host}:{port}")
except ConnectionResetError:
print(f"连接被远端重置 (Error 10054)")
except ConnectionAbortedError:
print(f"连接被本地中止 (Error 10053)")
except Exception as e:
print(f"连接失败: {e}")
if __name__ == "__main__":
if len(sys.argv) != 3:
print("用法: python test_connect.py <host> <port>")
sys.exit(1)
host = sys.argv[1]
port = int(sys.argv[2])
test_connect(host, port)
运行示例:
python test_connect.py 192.168.1.100 80
2. Winsock 错误捕获示例(C++)
演示如何在 Winsock TCP 客户端程序中捕获 10053 和 10054 错误,并做相应处理。
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#pragma comment(lib, "ws2_32.lib")
int main() {
WSADATA wsaData;
SOCKET sock = INVALID_SOCKET;
sockaddr_in serverAddr;
WSAStartup(MAKEWORD(2, 2), &wsaData);
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET) {
std::cerr << "socket error: " << WSAGetLastError() << std::endl;
WSACleanup();
return 1;
}
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(80);
inet_pton(AF_INET, "192.168.1.100", &serverAddr.sin_addr);
int ret = connect(sock, (sockaddr*)&serverAddr, sizeof(serverAddr));
if (ret == SOCKET_ERROR) {
int err = WSAGetLastError();
if (err == 10053) {
std::cerr << "Error 10053: Connection aborted locally." << std::endl;
// 处理本地中止逻辑
} else if (err == 10054) {
std::cerr << "Error 10054: Connection reset by peer." << std::endl;
// 处理远程重置逻辑
} else {
std::cerr << "连接失败,错误码: " << err << std::endl;
}
closesocket(sock);
WSACleanup();
return 1;
}
// 连接成功,发送/接收代码
closesocket(sock);
WSACleanup();
return 0;
}
3. 处理建议
- 捕获到 10053 错误时,建议检查是否应用层逻辑关闭了套接字,或者添加重连逻辑。
- 捕获到 10054 错误时,建议排查服务器端是否异常关闭连接,同时做好断线重连处理。
好,我给你写一个带自动重连机制的 WinSock TCP 客户端示例,示范如何捕获错误 10053、10054 并尝试重连。
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <thread>
#include <chrono>
#pragma comment(lib, "ws2_32.lib")
bool connect_to_server(SOCKET& sock, const sockaddr_in& serverAddr) {
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET) {
std::cerr << "Socket 创建失败,错误码:" << WSAGetLastError() << std::endl;
return false;
}
if (connect(sock, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
int err = WSAGetLastError();
std::cerr << "连接失败,错误码:" << err << std::endl;
closesocket(sock);
sock = INVALID_SOCKET;
return false;
}
std::cout << "连接服务器成功!" << std::endl;
return true;
}
void run_client(const char* ip, unsigned short port) {
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
std::cerr << "WSAStartup失败" << std::endl;
return;
}
sockaddr_in serverAddr{};
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(port);
inet_pton(AF_INET, ip, &serverAddr.sin_addr);
SOCKET sock = INVALID_SOCKET;
const int max_retries = 5;
int retry_count = 0;
while (true) {
if (sock == INVALID_SOCKET) {
if (!connect_to_server(sock, serverAddr)) {
retry_count++;
if (retry_count > max_retries) {
std::cerr << "重连超过最大次数,退出程序" << std::endl;
break;
}
std::cout << "等待3秒后重连..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(3));
continue;
}
retry_count = 0; // 成功连接,重置计数
}
// 发送数据示例
const char* msg = "Hello server";
int ret = send(sock, msg, (int)strlen(msg), 0);
if (ret == SOCKET_ERROR) {
int err = WSAGetLastError();
std::cerr << "发送数据失败,错误码:" << err << std::endl;
if (err == 10053) {
std::cerr << "本地中止连接(10053),尝试重连" << std::endl;
} else if (err == 10054) {
std::cerr << "远程重置连接(10054),尝试重连" << std::endl;
} else {
std::cerr << "其他错误,退出" << std::endl;
break;
}
closesocket(sock);
sock = INVALID_SOCKET;
continue;
}
// 接收数据示例(简化)
char buf[512];
ret = recv(sock, buf, sizeof(buf) - 1, 0);
if (ret == 0) {
std::cerr << "连接关闭,尝试重连" << std::endl;
closesocket(sock);
sock = INVALID_SOCKET;
continue;
} else if (ret == SOCKET_ERROR) {
int err = WSAGetLastError();
std::cerr << "接收数据失败,错误码:" << err << std::endl;
if (err == 10053 || err == 10054) {
std::cerr << "连接异常断开,尝试重连" << std::endl;
closesocket(sock);
sock = INVALID_SOCKET;
continue;
} else {
std::cerr << "其他错误,退出" << std::endl;
break;
}
} else {
buf[ret] = '\0';
std::cout << "收到数据: " << buf << std::endl;
}
std::this_thread::sleep_for(std::chrono::seconds(5)); // 模拟间隔
}
if (sock != INVALID_SOCKET) {
closesocket(sock);
}
WSACleanup();
}
int main() {
const char* server_ip = "192.168.1.100";
unsigned short server_port = 80;
run_client(server_ip, server_port);
return 0;
}
说明:
- 失败时针对错误 10053/10054 关闭 socket 并重连,最多重连 5 次。
- 每次重连间隔 3 秒。
- 简单模拟发送“Hello server”,接收数据。
- 其他错误会退出程序。
明白!我帮你写一个基于之前代码的多线程版 Winsock TCP 客户端,包含:
- 独立线程做连接和自动重连
- 主线程处理数据收发
- 线程安全退出机制
- 错误日志写文件
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <thread>
#include <atomic>
#include <chrono>
#include <fstream>
#include <mutex>
#pragma comment(lib, "ws2_32.lib")
std::atomic<bool> running{ true };
std::mutex log_mutex;
void log_error(const std::string& msg) {
std::lock_guard<std::mutex> lock(log_mutex);
std::ofstream log("client_error.log", std::ios::app);
log << msg << std::endl;
std::cerr << msg << std::endl;
}
bool connect_to_server(SOCKET& sock, const sockaddr_in& serverAddr) {
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET) {
log_error("Socket 创建失败,错误码:" + std::to_string(WSAGetLastError()));
return false;
}
if (connect(sock, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
int err = WSAGetLastError();
log_error("连接失败,错误码:" + std::to_string(err));
closesocket(sock);
sock = INVALID_SOCKET;
return false;
}
std::cout << "连接服务器成功!" << std::endl;
return true;
}
void connection_manager(SOCKET& sock, const sockaddr_in& serverAddr) {
const int max_retries = 5;
int retry_count = 0;
while (running) {
if (sock == INVALID_SOCKET) {
if (!connect_to_server(sock, serverAddr)) {
retry_count++;
if (retry_count > max_retries) {
log_error("重连超过最大次数,停止连接管理线程");
running = false;
break;
}
log_error("等待3秒后重连...");
std::this_thread::sleep_for(std::chrono::seconds(3));
continue;
}
retry_count = 0;
}
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
void data_handler(SOCKET& sock) {
while (running) {
if (sock == INVALID_SOCKET) {
std::this_thread::sleep_for(std::chrono::milliseconds(200));
continue;
}
// 发送示例
const char* msg = "Hello server";
int ret = send(sock, msg, (int)strlen(msg), 0);
if (ret == SOCKET_ERROR) {
int err = WSAGetLastError();
log_error("发送失败,错误码:" + std::to_string(err));
if (err == 10053 || err == 10054) {
log_error("连接异常断开,准备重连");
closesocket(sock);
sock = INVALID_SOCKET;
continue;
} else {
log_error("未知错误,停止程序");
running = false;
break;
}
}
// 接收示例
char buf[512];
ret = recv(sock, buf, sizeof(buf) - 1, 0);
if (ret == 0) {
log_error("服务器关闭连接,准备重连");
closesocket(sock);
sock = INVALID_SOCKET;
continue;
}
else if (ret == SOCKET_ERROR) {
int err = WSAGetLastError();
log_error("接收失败,错误码:" + std::to_string(err));
if (err == 10053 || err == 10054) {
log_error("连接异常断开,准备重连");
closesocket(sock);
sock = INVALID_SOCKET;
continue;
} else {
log_error("未知错误,停止程序");
running = false;
break;
}
}
else {
buf[ret] = '\0';
std::cout << "收到数据: " << buf << std::endl;
}
std::this_thread::sleep_for(std::chrono::seconds(5));
}
}
int main() {
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
log_error("WSAStartup失败");
return 1;
}
sockaddr_in serverAddr{};
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(80);
inet_pton(AF_INET, "192.168.1.100", &serverAddr.sin_addr);
SOCKET sock = INVALID_SOCKET;
std::thread conn_thread(connection_manager, std::ref(sock), std::ref(serverAddr));
std::thread data_thread(data_handler, std::ref(sock));
std::cout << "按回车退出程序..." << std::endl;
std::cin.get();
running = false;
conn_thread.join();
data_thread.join();
if (sock != INVALID_SOCKET) {
closesocket(sock);
}
WSACleanup();
return 0;
}
说明:
connection_manager
线程负责连接和重连,最多尝试 5 次失败后停止程序。data_handler
线程负责数据收发,检测错误 10053 和 10054 后关闭连接,等待重连。- 采用
std::atomic<bool>
控制线程退出。 - 所有错误日志写入
client_error.log
,同时输出到控制台。 - 主线程等待用户回车后,安全退出所有线程和清理资源。
好,这里帮你写一个简单的 CMakeLists.txt,用于编译上面那个多线程 Winsock TCP 客户端示例。这个配置适合 Windows 平台,使用 Visual Studio 或者命令行编译。
CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(WinSockClientMultiThread LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(WinSockClientMultiThread main.cpp)
# 链接 Winsock 库
target_link_libraries(WinSockClientMultiThread PRIVATE ws2_32)
使用步骤
- 把你写好的 C++ 代码保存为
main.cpp
(与 CMakeLists.txt 同目录)。 - 打开命令行(PowerShell 或 cmd),进入项目目录。
- 生成 Visual Studio 解决方案(假设你安装了 Visual Studio 和 CMake):
mkdir build
cd build
cmake .. -G "Visual Studio 17 2022" # 或根据你的 VS 版本替换生成器
- 编译:
- 在 Visual Studio 里打开生成的解决方案,编译运行。
- 或者直接用命令行编译:
cmake --build . --config Release
- 编译成功后,在
build/Release
目录下会生成WinSockClientMultiThread.exe
,直接运行。
发表回复