socket通信丢包问题

King_hhuang 2009-02-23 02:25:47
我写的服务器端将监听端口与窗体绑定啦
ON_MESSAGE(WM_NETWORK_EVENT, OnNetworkEvent)
ON_MESSAGE(WM_PCCSOCK_RECV, OnPccsockRecv)

CList<CString ,CString&> m_msglist;

WSAAsyncSelect(m_SocketSvr.GetServerSock(),m_hWnd,WM_NETWORK_EVENT,FD_ACCEPT|FD_READ|FD_CLOSE);

void CXXXDlg::OnNetworkEvent(WPARAM wParam, LPARAM lParam)
{
if(WSAGETSELECTERROR(lParam)) //判断是否SOCKET出错
{
CString Errmsg;
Errmsg.Format("Socket failed with error %d",WSAGETSELECTERROR(lParam));
WriteLog(Errmsg);
m_SocketSvr.CloseServerSock();
}

switch (WSAGETSELECTEVENT(lParam))
{
case FD_ACCEPT:
m_SocketSvr.AcceptSock();
WSAAsyncSelect(m_SocketSvr.GetConnSock(),m_hWnd,WM_PCCSOCK_RECV,FD_READ/*|FD_CLOSE*/);
break;
case FD_READ:
break;
case FD_CLOSE:
m_svrClosed=true;
WriteLog("服务器Socket关闭");
break;
}

}

void CXXXDlg::OnPccsockRecv(WPARAM wParam, LPARAM lParam)
{
char buff[1024];
memset(buff,0,1024);

char strtmp[20];
memset(strtmp,0,20);

if(WSAGETSELECTERROR(lParam)) //判断是否SOCKET出错
{
CString Errmsg;
Errmsg.Format("Socket failed with error %d",WSAGETSELECTERROR(lParam));
m_SocketSvr.CloseSock(wParam);
}

switch (WSAGETSELECTEVENT(lParam))
{
case FD_READ:
if(!m_SocketSvr.RecvSock(buff))
{
CString pccmsg,acmback;
pccmsg.Format("%s",buff);

if(strlen(buff)>=26) {
sprintf(strtmp,"0013%s",pccmsg.Mid(4,4));
strcat(strtmp,"00000");
acmback.Format("%s",strtmp);
m_SocketSvr.SendSock(strtmp); //收到消息后回馈PCC
m_SocketSvr.CloseConnSock();

DealMsglist(pccmsg);
}
}
break;
case FD_CLOSE:
break;
}

}


int CXXXDlg::DealMsglist(CString& pccmsg, int mode)
{
EnterCriticalSection(&m_CriSection1);//进入临界区
int count = m_msglist.GetCount();
if(!mode)
m_msglist.AddTail(pccmsg);
else
{
if(count)
{
pccmsg=m_msglist.GetHead();
m_msglist.RemoveHead();
}
}
count = m_msglist.GetCount();

LeaveCriticalSection(&m_CriSection1);
return count;
}


==============================================
客户端是别人用JAVA写的,我也不知道他是怎么写的,好像是多线程的
就是开启一个线程A发一条消息给服务器端,再开启一个线程B发一条消息给服务器端,……

据我这边的日志记录,大部分情况是先收到A包,然后是B包,……
偶尔也会出现先收到B包,再收到A包,……
因为它一组数据会有2到4个包,每组数据有很短的间隔(可能0.5s),每组数据的包几乎没有间隔,
这样发了9000多组数据后,大约会有7、8组数据没有记入数据库,查询日志是这7、8组数据通信时丢失了关键的一个包

我们来回测试了很多次,发现丢包率约小于千分之一

请问这个可能是什么问题,怎么解决呢?
...全文
1771 31 打赏 收藏 转发到动态 举报
写回复
用AI写文章
31 条回复
切换为时间正序
请发表友善的回复…
发表回复
coldant 2009-02-24
  • 打赏
  • 举报
回复


楼主把客户端发送时间在缩小一些,然后检查接收的buff(把buff由char*改为 char buff[]最少能容纳俩个包这么大)中所有内容,这样看下是否有粘包问题。

timmf 2009-02-23
  • 打赏
  • 举报
回复
哦,那个读到-1指,Java写的服务器端
timmf 2009-02-23
  • 打赏
  • 举报
回复
恩,我有过类似,经历不过服务器端没楼主这么复杂,当时通讯时客户端给服务器端发过来的消息有3000多字节,由于我没有循环接收,即没读到-1,导致有时收到的包不完整,后来才知道是底层分包了,当时临时的一个解决是让客户端把在消息前把消息字节长度加上,先读四个字节转成int型再循环读这么多次,确保收全。
King_hhuang 2009-02-23
  • 打赏
  • 举报
回复
[Quote=引用 27 楼 jinxuliang 的回复:]
排除问题的办法:首先要确定客户端发送有没有问题
先要分清问题边界,然后再找问题。
用个抓包工具看客户端是否真的发出来了。
如果真发出来了,再让客户端用单线程发看是否有问题。
排除问题要有步骤
[/Quote]

我也想过这样啊,但是现在是上千组数据才会丢一个包,比较难找到啊,
而且一般的抓包工具也没有这么详细的日志,不好统计,还是让别人写个java的服务器端试试
jinxuliang 2009-02-23
  • 打赏
  • 举报
回复
排除问题的办法:首先要确定客户端发送有没有问题
先要分清问题边界,然后再找问题。
用个抓包工具看客户端是否真的发出来了。
如果真发出来了,再让客户端用单线程发看是否有问题。
排除问题要有步骤
King_hhuang 2009-02-23
  • 打赏
  • 举报
回复
客户端的发的包是有规律的,开始四个字节定义了包长,例如(包开头为0149),那么这个包长就是149个字节,而且每组数据中最短的包的长度是26个字节(这个发的几率很小)
King_hhuang 2009-02-23
  • 打赏
  • 举报
回复
已经加了临界保护啦,应该不会有问题的
vagrantisme 2009-02-23
  • 打赏
  • 举报
回复
你这个链表操作会不会有问题呢,两个线程访问时要注意同步。
King_hhuang 2009-02-23
  • 打赏
  • 举报
回复
[Quote=引用 22 楼 cnzdgs 的回复:]
不能这样做。
响应FD_READ时调用一次recv即可,根据返回值来判断,如果实际接收长度小于你要处理的数据长度,可以先把数据保存在缓冲区中,等下次FD_READ的实际继续接收后面部分。
[/Quote]

我每次响应FD_READ的时候(数据大于26字节时)都有把数据放到链表中啊,而且我的这个链表都有记录到日志中啊,应该不会少啊,即使是丢包了,也应该是个不完整的包,怎么日志里面完全没有这个丢掉的包的任何痕迹呢?
cnzdgs 2009-02-23
  • 打赏
  • 举报
回复
不能这样做。
响应FD_READ时调用一次recv即可,根据返回值来判断,如果实际接收长度小于你要处理的数据长度,可以先把数据保存在缓冲区中,等下次FD_READ的实际继续接收后面部分。
King_hhuang 2009-02-23
  • 打赏
  • 举报
回复
是不是用这种while(recv(sock,buf,1023,0)>0)就能确保接收完,但是这样写不是都会浪费一次recv
cnzdgs 2009-02-23
  • 打赏
  • 举报
回复
用TCP协议是要判断接收到的实际长度的,每次接收到的数据长度是不能确定的,可能一次接收到多次发送的数据,也可能一次发送的数据分多次收到。
King_hhuang 2009-02-23
  • 打赏
  • 举报
回复
我查看日志,我的包是整个的丢了,不是丢了一部分啊
King_hhuang 2009-02-23
  • 打赏
  • 举报
回复
我想再请教一下,如果一次没有recv完一个包(假设要2到3次),
FD_READ事件会不会对应响应2-3次呢?
vagrantisme 2009-02-23
  • 打赏
  • 举报
回复
if(rs==SOCKET_ERROR)
{
CString Errorcode;
Errorcode.Format("接收客户端数据失败,SOCKET_ERROR,错误代码:%d",WSAGetLastError());
WriteLog(Errorcode);
return -1;
}
else if(rs==0)
{
WriteLog("读取客户端时客户端已关闭");
return -2;
}

return 0;
}

你根本就没有判断你接收的包体的长度的。
King_hhuang 2009-02-23
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 cnzdgs 的回复:]
TCP是不会丢包的。
没看到你在接收数据之后如何处理,怎么判断接收数据的长度的?
[/Quote]
我确实没有判断接收数据的长度,但是客户端发过来的包是固定长度的,而且我查了出问题的这组包,好像没有粘包的情况
vagrantisme 2009-02-23
  • 打赏
  • 举报
回复
我只是说可能的情况。你那样写是逻辑上的不对,必须对接收的数据进行完整性的校验的。否则程序本身就存在错误。
网络情况好与差只是一个相对的概念。按说“客户端和服务器端在同一台机器上,网络应该还可以”,但你每次发1024个字段,让CPU全整跑的话,也有可能会有问题的。你要判断接收字节数是否是你的包体长度的。
百事烟 2009-02-23
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 cnzdgs 的回复:]
TCP是不会丢包的。
没看到你在接收数据之后如何处理,怎么判断接收数据的长度的?
[/Quote]
分析判断每个包头部的特征数据,或数据长度之类的
真地没看到你解析数据的部分,让你发recv就是想看你对数据是如何处理的?
King_hhuang 2009-02-23
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 vagrantisme 的回复:]
rs = recv(m_hConnsock,buff,1024,0);
这个的rs是实际接收的数量,你要判断一下的。

如果网络情况很好(局域网)的情况,就能一下全收下来的。网络情况一差,肯定不行了。
[/Quote]

客户端和服务器端在同一台机器上,网络应该还可以
King_hhuang 2009-02-23
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 caitian6 的回复:]
TCP 粘包 , 你客户端0.5秒 发次包, 还是多线程(同一台机器) 。 TCP 是流式的 ,无消息边界。 所以有可能 2 个包(或多个包) 合并成一个包。
提供一种解决法案: 客户端 每次发包的间隔 大一点 ,设成 5秒
[/Quote]

是0.5秒一组包,每组包有2-4条,且是没有间隔滴
另外客户端是人家开发的,要改可能性不大,因为他们以前就在用啦,以前对数据完整性没有太做要求,现在他们突然提高要求,要我们找问题,我只是想确认一下,问题到底在不在我这里,如果在我这里,是什么问题,怎么解决,谢谢!
加载更多回复(11)

18,356

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧