客户端偶现没有收到服务器响应问题的排查

客户端偶现没有收到服务器响应问题的排查

问题描述

     客户端偶现收不到服务器端返回的响应包。

问题排查

     当出现服务器端收不到客户端的请求包,或者客户端收不到服务器端的响应包时,第一反应就是是抓包,先确定是否存在丢包的可能。
输入命令:tcpdump -i eth0 src port 9983 -XX抓取我们服务器9983端口发出去的包,结果如下:
在这里插入图片描述     其中红色圈出来的部分就是应该返回的响应包,确定服务器端没有丢包。同样的,使用wireshark抓来自服务器端的包,结果如下:
在这里插入图片描述
     客户端也收到了服务端的包,到此基本排除丢包的可能。

     这个时候我同时也注意到了XWGetKpacgRSp这个响应包是和上一个响应包未发完的部分作为一个完整包发送的。上一个响应包大小是2720字节:
在这里插入图片描述     当前包大小是527字节:
在这里插入图片描述
     因此推测是不是客户端处理拆包的流程有问题,贴上客户端处理拆包的代码:

for (;;)
	{
		//初始化为0,分配四分之一的READ_BUF_SIZE大小,约为32768字节
		uint32_t free_buf_len = m_in_buf.GetAllocSize() - m_in_buf.GetWriteOffset();
		if (free_buf_len < READ_BUF_SIZE)
			m_in_buf.Extend(READ_BUF_SIZE);

		int ret = netlib_recv(m_handle, m_in_buf.GetBuffer() + m_in_buf.GetWriteOffset(), READ_BUF_SIZE);
		if (ret <= 0) break;

		//write_offset偏移当前已经接收的字节数
		m_in_buf.IncWriteOffset(ret);
		//做最小长度保护
		while (m_in_buf.GetWriteOffset() >= imcore::HEADER_LENGTH)
		{
			//比如接收到了472+55字节
			//其中472字节和前面的27200是一起的,且length=27668
			//第一次取出来的是上面netlib_recv返回的长度
			uint32_t len = m_in_buf.GetWriteOffset();
			uint32_t length = 0;
			//获取包体长度
			memcpy(&length, m_in_buf.GetBuffer(), sizeof(length));
			length = ntohl(length);

			//len:接收的总长度
			//length:包体长度
			//PACK_HEADER_LENGTH:包体长度的长度
			//PACK_HEADER_LENGTH + length <= len : 才可以解析
			//如果字符串长度大于此次接收的长度,那么不处理,等待下一次
			if (length + PACK_HEADER_LENGTH > len)
				break;

			try
			{
				if (m_pTcpSocketCB)
					m_pTcpSocketCB->onReceiveData((const char*)m_in_buf.GetBuffer(), length + PACK_HEADER_LENGTH);
				LOGBIN_F__(SOCK, "OnRead", m_in_buf.GetBuffer(), length + PACK_HEADER_LENGTH);
			}
			catch (std::exception& ex)
			{
				assert(FALSE);
				LOGA__(NET, "std::exception,info:%s", ex.what());
				if (m_pTcpSocketCB)
					m_pTcpSocketCB->onReceiveError();
			}
			catch (...)
			{
				assert(FALSE);
				LOG__(NET, _T("unknown exception"));
				if (m_pTcpSocketCB)
					m_pTcpSocketCB->onReceiveError();
			}
			//已经读取的长度置为已读
			m_in_buf.Read(NULL, len);
		}
	}

     XWGetKpacgRSp上一个响应包的大小为4+27668字节,每次发送2720字节,总共发送了10次,最后一个包527字节。也即最后一个网络包包含了55字节大小的XWGetKpacgRSp响应包(因为TCP协议是流模式的)。
     那么在这段代码中,需要判断包体长度和接收长度的大小,如果接收长度小于包体长度,那么不做处理,继续接收,直到接收长度大于等于包体长度。最后将接收到的长度置为已读,避免下次再读:

//已经读取的长度置为已读
m_in_buf.Read(NULL, len);

     可是这里置为已读的长度是当前接收缓存中的未读内容的总长度(也就是上面的2720*10+527),也就是将55字节大小的XWGetKpacgRSp响应包合并到了上一个响应包,这样就导致拆包失败,客户端没有再对XWGetKpacgRSp响应包做处理。将代码修改如下解决问题:

//已经读取的长度置为已读
m_in_buf.Read(NULL, length + PACK_HEADER_LENGTH);

总结

     客户端对服务端的数据包进行处理时,需要考虑粘包问题,这一点大家都知道,那么在书写代码的时候就需要格外小心,避免因为低级的代码书写错误导致程序功能异常。
     当出现客户端与服务器端收不到对方的请求或响应时,按照以下流程排查问题:
1,抓包。分别抓客户端和服务端的网络数据包,看数据是否发送出去或者接收到。(TCP协议是可靠的,不存在丢包问题)
2,如果服务端没有发出响应包,则排查服务端的发包流程是否正常。
3,如果服务端有发出数据包,客户端也有收到,那么排查客户端的收包流程是否正常,特别是粘包,拆包的处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Simple Simple

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

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

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

打赏作者

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

抵扣说明:

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

余额充值