复现socket error 10054及理解

对于这个错误码的解释网上有很多,就是链接被远程主机重置,说实话这句话说了和没说一样,根本不知道啥意思,于是就各种查

对于10054可能的出现原因,一个大佬总结的很好,我这里直接搬运过来
对端关闭了socket(可能是异常也可能是超时),然而本端还没有感知到,依旧写入数据(对端接收缓冲区有数据),对端发现有未处理的数据直接回复RST重置连接标记,表明对端已经关闭了socket

检测出这个错误码一般是对端发送了RST包给本端,本端接收这个包, 表示对端出现了异常: 四种情况发送RST码:

  1. 本端连接对端未打开的端口, 对端发送RST包

  2. 请求超时

  3. 已关闭连接上收到数据: 

主要出现在连接的关闭过程,当请求关闭连接的一方在两个MSL后,仍然收到服务机发送来的最后一个FIN,说明其最后发送出的ACK丢包, 服务机又重发了FIN包,而此时客户机已经不处于TIME_OUT状态,连接已经彻底不存在,再收到服务机发送来的FIN,就会直接回复一个RST包,让服务机直接关闭连接,抛弃 缓冲区的所有数据。

举一个其他例子: 主动端关闭socket,发送了fin给被动端,此时主动端无法接受和发送数据了,此时如果被动端调用read会收到fin(读取0),如果被动端write给主动端,主动端会发送rst,但rst不会立即通知到被动段,只有被动端在第二次write时,数据不会发送给主动端而是发出一个SIGPIPE信号而终止被动端(可捕获忽略该信号)

  4. close连接时, 接收缓冲区还有数据

如果close连接时,接收缓冲区还有对端发过来的数据,则回一个RST,直接丢弃接收缓冲区的内容

  5. 设置了so_linger选项但是 l_onoff 不为0,l_linger为0, 此时close系统调用立即结束,TCP模块将丢弃被关闭的socket对应的TCP发送缓冲区中残留的数据,同时回给对方一个RST

上面提到的主动端是指客户端,被动端指服务端

下面贴出可以浮现的代码
首先是客户端

/*
 * @Author: windofbarcelona
 * @Date:
 * @LastEditTime:
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 */
#include<iostream>
#include<WinSock2.h>
#include<string>
#include<vector>
#include<fstream>
#include<io.h>
#include<algorithm>
#pragma comment(lib, "Ws2_32.lib")
#pragma warning(disable : 4996)
using namespace std;
int main() {


	WORD    VersionRequested;
	WSADATA WsaData;

	VersionRequested = MAKEWORD(2, 2);

	if (WSAStartup(VersionRequested, &WsaData))
		return -1;

	SOCKET SocketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	SOCKADDR_IN AddrServer;
	AddrServer.sin_addr.S_un.S_addr = inet_addr("192.168.1.100");
	AddrServer.sin_port = htons(9999);
	AddrServer.sin_family = AF_INET;

	if (0 != connect(SocketServer, (SOCKADDR*)&AddrServer, sizeof(SOCKADDR)))
	{
		closesocket(SocketServer);
		cout << "connect error:" << WSAGetLastError() << endl;
		return false;
	}

	int err = 0;
	char temp[] = "hello, server";
	int rs = send(SocketServer, temp, sizeof(temp) + 1, 0);
	cout << "first send:" << rs << endl;
	char buff[1024] = { 0 };
	rs = recv(SocketServer, buff, sizeof(buff), 0);
	cout << "first recv:" << rs << endl;
	rs = send(SocketServer, temp, sizeof(temp) + 1, 0);
	err = WSAGetLastError();
	cout << "second send:" << rs << ",error:" << err << endl;
	rs = recv(SocketServer, buff, sizeof(buff), 0);
	err = WSAGetLastError();
	cout << "second recv:" << rs << ",error:" << err << endl;
	closesocket(SocketServer);
	WSACleanup();
	system("pause");
	return 0;
}

然后是服务端的代码

/*
 * @Author: windofbarcelona
 * @Date:
 * @LastEditTime: 
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 */
#include<iostream>
#include<WinSock2.h>
#include<string>
#include<vector>
#include<fstream>
#include<io.h>
#include<algorithm>
#pragma comment(lib, "Ws2_32.lib")
#pragma warning(disable : 4996)
using namespace std;
int main() {

	WORD    VersionRequested;
	WSADATA WsaData;

	VersionRequested = MAKEWORD(2, 2);

	if (WSAStartup(VersionRequested, &WsaData))
	{
		printf("加载socket库失败!\n");
		return -1;
	}

	// 监听来自浏览器的请求
	SOCKET SockServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	SOCKADDR_IN AddrClient;
	AddrClient.sin_family = AF_INET;
	AddrClient.sin_addr.s_addr = INADDR_ANY;
	AddrClient.sin_port = htons(9999);

	if (SockServer == INVALID_SOCKET)
	{
		printf("socket初始化失败!\n");
		closesocket(SockServer);
		WSACleanup();
		return -1;
	}

	if (bind(SockServer, (sockaddr*)&AddrClient, sizeof(AddrClient)) != 0)
	{
		printf("socket绑定失败!\n");
		closesocket(SockServer);
		WSACleanup();
		return -1;
	}

	if (listen(SockServer, 10) != 0)
	{
		printf("socket监听失败!\n");
		closesocket(SockServer);
		WSACleanup();
		return -1;
	}

	while (1)
	{
		SOCKET NewSocket = accept(SockServer, NULL, NULL);
		if (INVALID_SOCKET == NewSocket)
		{
			closesocket(NewSocket);
			continue;
		}
		else
		{
			//plan A 
			struct linger so_linger;
			so_linger.l_onoff = 10;
			so_linger.l_linger = 0;
			setsockopt(NewSocket, SOL_SOCKET, SO_LINGER, (const char*)&so_linger, sizeof(so_linger));
		}

		char buff[1024];
		int rs = recv(NewSocket, buff, sizeof(buff), 0);
		cout << "recv:" << rs << endl;
		char temp[] = "hello, client";
		rs = send(NewSocket, temp, sizeof(temp) + 1, 0);
		cout << "send:" << rs << endl;
		//Sleep(3000); plan B
		closesocket(NewSocket);
	}
	return 0;
}

说明,服务端的代码提供了plan A和plan B两种方式,都可以出现10054的错误
对于plan A就是上面原因5说明的
对于plan B就是上面的原因4,close的时候接收缓冲区中还有未处理的数据,于是放弃数据 直接回RST,我在服务端设置延时是为了,在关闭的时候,客户端的数据一定发过来了
上面的代码可以同时用wireshark进行抓包观察

代码运行环境说明,用了两台 win10系统的电脑进行测试,在服务端的主机上用wireshark进行抓包观察

解决方案我觉得就是得分析协议的实现是否出现了问题,导致了10054的发生
参考链接:
https://www.cnblogs.com/pengyusong/p/6433516.html
http://www.blogjava.net/pandawang/archive/2013/11/28/406922.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巴塞罗那的风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值