【求助,急】MFC中CAsyncSocket在OnReceive()中接收数据出问题

_浮生_ 2015-12-09 09:33:10
本人在MFC中使用AsyncSocket进行异步收发,客户端使用AfxMessageBox显示收到的数据,现在的问题是我发送“123”给客户端,客户端确实能收到,但是很少能一次显示“123”,几乎都是先显示一半,点击对话框“确定”再显示另一半,例如下图是某次试验的结果:
先是显示“23”

点击“确定”后显示“1”

完全就是乱的。。。当然偶尔也会显示正确,如下图:

为此,我在程序里接收完数据后设置了断点,查看到底收到了什么,如下图:
设置断点,第一次试验,接收字符数组收到了“1”。。。

再试验,这次收到了“12”。。。

再试验,又只收到了“1”

试了多次,出现了一次收到“123”

这其中令我最不解的是为什么this指针的值有时没变,有时变了(图中变红)

其次,还有一个奇怪的问题,当客户端CAsyncSocket对象connect时,出现下面的情况:
点击连接,看起来貌似很正常:

点击“确定”,竟然是“listen busy”。。。

客户端CAsyncSocket对象connect部分程序如下:
	
MyClientDlg.cpp
……
if (!m_Conn.Connect("192.168.8.1",2001))
{
int nErrorCode = m_Conn.GetLastError();
if(nErrorCode == 10035)
AfxMessageBox("Listen Busy!");
if (nErrorCode!=WSAEWOULDBLOCK)
{
AfxMessageBox("Listen Error!");
m_Conn.Close();
PostQuitMessage(0);
return;
}
}
……
void CMyClientDlg::OnConnected()
{
MessageBox("连接成功");
}

m_Conn是在CMyClientDlg类中定义的CClientSock类变量
ClientSock.cpp中OnConnect部分的程序如下:

void CClientSock::OnConnect(int nErrorCode)
{
// TODO: Add your specialized code here and/or call the base class
if (0 == nErrorCode)
{
m_pDlg->OnConnected();
}
CAsyncSocket::OnConnect(nErrorCode);
}

m_pDlg是CClientSock类中定义的CMyClientDlg类的指针变量:CMyClientDlg* m_pDlg

这里我的疑问是,m_Conn.Connect("192.168.8.1",2001)执行完成后,不管成功与否都会去执行CClientSock::OnConnect(int nErrorCode) ,而OnConnect(int nErrorCode) 中的nErrorCode只有等于0,也就是connect成功了,才会去调用m_pDlg的OnConnected(),即显示“连接成功”,但是从之后跳出“listen busy”可以知道, if (!m_Conn.Connect("192.168.8.1",2001))判断语句内的语句还是执行了,也就是说m_Conn.Connect("192.168.8.1",2001)是FALSE,即connect失败,那么到底是连接成功了还是连接失败了?@_@
当然,int nErrorCode = m_Conn.GetLastError();后面显示了“listen busy”表明nErrorCode=10035,即“忙”,可能不久它就connect上了,可是这个时刻,它是“忙”的,也就是没有连接成功,那么为什么能执行OnConnected()显示“连接成功”,不解@_@

以上是我的两个疑问,这两天搞得要崩溃了,希望有这方面经验和知识的大侠能够指点一下,指出问题,更希望能够给点解决思路,刚接触MFC和socket,不太熟悉,谢谢!
...全文
349 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
gnorth 2015-12-11
  • 打赏
  • 举报
回复
tcp的数据保证整个流可靠性,但是不保证一次发送/接收的字节数对齐,一次in的数据可能需要对端执行数次out,数次in的数据可能只需要对端执行1次out,这取决于两端的程序性能。 通常我们的做法是,自己用一个简单的协议。 struct pack_head{ int sn; int len; }; template<typename T> struct pack_t{ int sn;//请求服务号 int len;//包长 T val;//具体的结构 }; 服务端的接收缓冲区,最好是分配内存池,这样可以最大化的提升IO性能。 struct CONNECTION_STRUCT{ SOCKET s; SOCKADDR_IN sa; CHAR buf[1024]; int len; }; 在处理FD_READ事件时,注意以下代码算是伪代码:(其中conn为CONNECTION_STRUCT,它就是对应每一个SOCKET的资源) int nRecv = recv(s, conn.buf + conn.len, 1024 - conn.len, 0); if(nRecv <= 0) { closesocket(s); ... } conn.len += nRecv; if(conn.len >= sizeof(pack_head)) { //当接收到的字节数大于协议头时,就先把接收缓冲区指针转换成协议头的结构类型,先来判断请求服务号。 pack_head *ph = (pack_head *)(conn.buf); switch(ph->sn) { case xxx: //例如对应xxx服务号的结构是pack_t<RECT> if(ph->len != sizeof(pack_t<RECT>)) //如果长度不对,就关闭,这样能避免一些攻击。 { closesocket(s); ... } if(conn.len >= sizeof(pack_t<RECT>)) { //这里去执行对应的请求逻辑 //执行完之后 conn.len -= sizeof(pack_t<RECT>); if(conn.len != 0)//将剩余的数据前移。 memmove(conn.buf, 1024, conn.buf + sizeof(pack_t<RECT>), conn.len); } ... break; case yyy: ... break; default: closesocket(s); ... } } 对于32位的服务端来说,可以在程序启动时,就分配好每个连接相关的资源,因为SOCKET的取值范围是0 ~ 0xFFFF,假如你的协议请求最大不超过1024字节,那么分配的这部分内存也就是70M左右,这点内存,现在就是几十元的服务器,也能随便够用。 也可以动态分配,每一次accept之后就为对应的SOCKET分配资源,closesocket之后清理掉,用std::map之类的东西或者二叉查找树来定位资源,不过我还是推荐前一种方法。
信阳毛尖 2015-12-10
  • 打赏
  • 举报
回复
自定义一个包头数据结构,并自定义协议,包头+数据,包头含有数据长度信息 最简单的包头就是至包含一个4字节的long,存储要发送的完整数据长度 比如你要发送64个字节的数据,实际发送的就是64+4 然后在OnReceive中根据m_nRecvBufLen和前面提到的数据长度来判断一下即可,一般是要经过两次判断,第一次判断m_nRecvBufLen是否小于包头长度(4),第二次判断m_nRecvBufLen是否小于n+4 最后取数据,把包头数据区去掉即可
paschen 2015-12-10
  • 打赏
  • 举报
回复
tcp包不会为数据划分边界,接收需要判断是否收完,其中阻塞和非阻塞下又不太一样,具体看MSDN
_浮生_ 2015-12-10
  • 打赏
  • 举报
回复
引用 5 楼 wxhxj0268 的回复:
直接去下载一个程序看一下就行了:http://download.csdn.net/download/wxhxj0268/4448688 (VS2009编译环境)
这个程序里的Receive处理和我程序里的处理基本一样额
_浮生_ 2015-12-10
  • 打赏
  • 举报
回复
引用 4 楼 lsq19871207 的回复:
自定义一个包头数据结构,并自定义协议,包头+数据,包头含有数据长度信息 最简单的包头就是至包含一个4字节的long,存储要发送的完整数据长度 比如你要发送64个字节的数据,实际发送的就是64+4 然后在OnReceive中根据m_nRecvBufLen和前面提到的数据长度来判断一下即可,一般是要经过两次判断,第一次判断m_nRecvBufLen是否小于包头长度(4),第二次判断m_nRecvBufLen是否小于n+4 最后取数据,把包头数据区去掉即可
非常感谢!项目里原来要求是接受一个字符串再执行相应动作,不过时间有点紧,我现在直接采用接受一个字母,然后根据不同的字母采取相应的动作,谢谢提供这么详细的思路,等后面仔细研究异步通信时再试下你的方法 : )
_浮生_ 2015-12-10
  • 打赏
  • 举报
回复
引用 3 楼 paschen 的回复:
tcp包不会为数据划分边界,接收需要判断是否收完,其中阻塞和非阻塞下又不太一样,具体看MSDN
好的,O(∩_∩)O谢谢
笨笨仔 2015-12-10
  • 打赏
  • 举报
回复
直接去下载一个程序看一下就行了:http://download.csdn.net/download/wxhxj0268/4448688 (VS2009编译环境)
_浮生_ 2015-12-09
  • 打赏
  • 举报
回复
引用 1 楼 oyljerry 的回复:
要考虑拆包 所以接受的时候不一定一次接受完了 需要循环接受直到结束
好的,谢谢!我去补一下这方面的知识
oyljerry 2015-12-09
  • 打赏
  • 举报
回复
要考虑拆包 所以接受的时候不一定一次接受完了 需要循环接受直到结束

18,356

社区成员

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

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