代码是这样的:
void CMy_CAsyncSocket::OnReceive(int nErrorCode)
{
// TODO: 在此添加专用代码和/或调用基类
if(nErrorCode == 0)
{
//这个是缓存池结构,可以不管
netdata_cls * recvcls;
//这个while循环去掉就没事了,这里设置为 -- [[[[[[解决方法A]]]]]]
while(true)
{
//到缓存池拿出一个结构和缓冲区
recvcls = lbuff_SysNetDataClsSck.Get();
recvcls->data_string = lbuff_cmystring.Get(MIDDLE_BFSZ); //接收
int readed = Receive(recvcls->data_string->data, recvcls->data_string->all_len);
if(readed == 0)
{
lbuff_cmystring.Put(recvcls->data_string);
lbuff_SysNetDataClsSck.Put(recvcls);
OnClose(GetLastError());//出错,远程机器被关闭,需要结束这台客户端的一切
return;
}
else if(readed == SOCKET_ERROR)
{
lbuff_cmystring.Put(recvcls->data_string);
lbuff_SysNetDataClsSck.Put(recvcls); //现在问题是在这里,这里为 -- [[[[[[问题A]]]]]]
nErrorCode = GetLastError();
if(nErrorCode == WSAEWOULDBLOCK)
{
break;
}
else
{
OnClose(nErrorCode);//出错,需要结束这台客户端的一切
return;
}
}
else
{
//正常情况,设置数据长度,放入已经接受的链表中
//这部分也可以不看.
recvcls->data_string->data_len = readed;
recvcls->is_fin_code = false;
recvcls->op_n = 0;
recvdata_list_PutLast_With_CES(recvcls, GO_SET_EVENT); //这里设置为 -- [[[[[[解决方法B]]]]]]
//如果数据缓存装满了,说明还有部分数据在socket栈中
//如果数据缓存没装满,说明socket栈中已经没有数据了.退出while循环
//if(readed != recvcls->data_string->all_len)
//{
// break;
//}
}
}
}
else
{
OnClose(nErrorCode);//发生了错误,需要结束这台客户端的一切
return;
}
CAsyncSocket::OnReceive(nErrorCode);
}
如上面的代码这样,错误发生在"[[[[[[问题A]]]]]]"的地方.错误发送的过程是:我的目的是每次数据过来之后(触发了OnReceive),就收走全部的Socket栈中的数据.所以我用while不停的Receive,按道理说,如果Socket栈中没有数据了,Receive将返回SOCKET_ERROR,然后用GetLastError(),将返回WSAEWOULDBLOCK,告诉我现在没办法收数据.然后我的程序退出本次OnReceive.等待下次触发.可是,事情没有像我预想的那样,如果Socket栈中没数据了,Receive将返回SOCKET_ERROR错误,然后用GetLastError(),返回的却是WSA_INVALID_HANDLE错误..只好关闭这条连接.我试过忽略这个WSA_INVALID_HANDLE错误,直接退出本次OnReceive,没想到发送方(另外一个程序却产生了OnClose错误,nErrorCode错误码也是WSA_INVALID_HANDLE).也就是说.无论如何,只要Socket栈中没有了数据,我再去Receive它,肯定会结束这条socket链接..我不知道为什么会这样,大家有没有碰到这样的情况呢?.....这是问题一.解决办法有两个,不过我经过了这样古怪的错误之后,不知道这样是不是能很好的解决,所以上来问问大家.就像解决办法[[[[[[解决方法A]]]]]]这样,不用while循环了,如果一次OnReceive收不完数据,就等待下次再触发OnReceive,反正Socket栈中只要有数据没有被Recv,就肯定会一直触发OnReceive事件,不知道是不是这样呢?...这是问题二..反正我把While去掉后收发大文件都很正常.[[[[[[解决方法B]]]]]]那样也行,每次增加一个判断,如果存在后续数据,才再一次执行Receive函数,不过我感觉这样的判断方式比较不可靠.虽然我测试中没发现问题,但是不保证实际应用上不会出差错.大家感觉呢?...这是问题三...就这三个问题,拜托大家了.我的意思是,有没有让while存在的情况下,能一次收完全部的Socket栈数据,而不会触发古怪的WSA_INVALID_HANDLE错误.或者是,我的代码其他部分写的有问题,大家都没有发现有WSA_INVALID_HANDLE错误的情况.
void CMy_CAsyncSocket::OnReceive(int nErrorCode)
{
// TODO: 在此添加专用代码和/或调用基类
if(nErrorCode == 0)
{
//这个是缓存池结构,可以不管
netdata_cls * recvcls;
//这个while循环去掉就没事了,这里设置为 -- [[[[[[解决方法A]]]]]]
while(true)
{
//到缓存池拿出一个结构和缓冲区
recvcls = lbuff_SysNetDataClsSck.Get();
recvcls->data_string = lbuff_cmystring.Get(MIDDLE_BFSZ); //接收
int readed = Receive(recvcls->data_string->data, recvcls->data_string->all_len);
if(readed == 0)
{
lbuff_cmystring.Put(recvcls->data_string);
lbuff_SysNetDataClsSck.Put(recvcls);
OnClose(GetLastError());//出错,远程机器被关闭,需要结束这台客户端的一切
return;
}
else if(readed == SOCKET_ERROR)
{
lbuff_cmystring.Put(recvcls->data_string);
lbuff_SysNetDataClsSck.Put(recvcls); //现在问题是在这里,这里为 -- [[[[[[问题A]]]]]]
nErrorCode = GetLastError();
if(nErrorCode == WSAEWOULDBLOCK)
{
break;
}
else
{
OnClose(nErrorCode);//出错,需要结束这台客户端的一切
return;
}
}
else
{
//正常情况,设置数据长度,放入已经接受的链表中
//这部分也可以不看.
recvcls->data_string->data_len = readed;
recvcls->is_fin_code = false;
recvcls->op_n = 0;
recvdata_list_PutLast_With_CES(recvcls, GO_SET_EVENT); //这里设置为 -- [[[[[[解决方法B]]]]]]
//如果数据缓存装满了,说明还有部分数据在socket栈中
//如果数据缓存没装满,说明socket栈中已经没有数据了.退出while循环
//if(readed != recvcls->data_string->all_len)
//{
// break;
//}
}
}
}
else
{
OnClose(nErrorCode);//发生了错误,需要结束这台客户端的一切
return;
}
CAsyncSocket::OnReceive(nErrorCode);
}
如上面的代码这样,错误发生在"[[[[[[问题A]]]]]]"的地方.错误发送的过程是:我的目的是每次数据过来之后(触发了OnReceive),就收走全部的Socket栈中的数据.所以我用while不停的Receive,按道理说,如果Socket栈中没有数据了,Receive将返回SOCKET_ERROR,然后用GetLastError(),将返回WSAEWOULDBLOCK,告诉我现在没办法收数据.然后我的程序退出本次OnReceive.等待下次触发.可是,事情没有像我预想的那样,如果Socket栈中没数据了,Receive将返回SOCKET_ERROR错误,然后用GetLastError(),返回的却是WSA_INVALID_HANDLE错误..只好关闭这条连接.我试过忽略这个WSA_INVALID_HANDLE错误,直接退出本次OnReceive,没想到发送方(另外一个程序却产生了OnClose错误,nErrorCode错误码也是WSA_INVALID_HANDLE).也就是说.无论如何,只要Socket栈中没有了数据,我再去Receive它,肯定会结束这条socket链接..我不知道为什么会这样,大家有没有碰到这样的情况呢?.....这是问题一.解决办法有两个,不过我经过了这样古怪的错误之后,不知道这样是不是能很好的解决,所以上来问问大家.就像解决办法[[[[[[解决方法A]]]]]]这样,不用while循环了,如果一次OnReceive收不完数据,就等待下次再触发OnReceive,反正Socket栈中只要有数据没有被Recv,就肯定会一直触发OnReceive事件,不知道是不是这样呢?...这是问题二..反正我把While去掉后收发大文件都很正常.[[[[[[解决方法B]]]]]]那样也行,每次增加一个判断,如果存在后续数据,才再一次执行Receive函数,不过我感觉这样的判断方式比较不可靠.虽然我测试中没发现问题,但是不保证实际应用上不会出差错.大家感觉呢?...这是问题三...就这三个问题,拜托大家了.我的意思是,有没有让while存在的情况下,能一次收完全部的Socket栈数据,而不会触发古怪的WSA_INVALID_HANDLE错误.或者是,我的代码其他部分写的有问题,大家都没有发现有WSA_INVALID_HANDLE错误的情况.
解决方案 »
- 如何截获自己创建窗口的鼠标移动
- 远程控制软件的控制功能怎么实现?给个思路?
- 找人开发即时通讯系统
- 高手低手都来看看,超级菜鸟问的问题~~~ 看下这代码,为什么不能完成我的目的~~~
- 一条业务逻辑数据分别保存到两个数据库,用两个模块分别进行,如何保证这条业务逻辑数据的完整性?
- [求助]如何获得空闲的磁盘盘符?在线等待.........
- 关于platform sdk
- 关于ODBC的问题
- 两个小问题,100分!
- 免费软件放送 一个小程序请各位大虾测试一下
- 提问报错:msvcrtd.lib(crtexe.obj) : error LNK2001: unresolved external symbol _main
- IHTMLDocument2::execCommand不工作问题
char tempMsg[1001]="";
int ByteCount;
bool endflages=true;
do
{
strcpy(tempMsg,"");
ByteCount=YourSocket->Receive(tempMsg,1000);
tempMsg[ByteCount]=0;
if(ByteCount<1000&&ByteCount>=0)
endflages=false;
CString temp="";
pMsg+=temp.Format("%s",tempMsg)
}while(endflages);
//这个样子所有的内容都在pMsg里面了~~~~
2.用CString效率上问题很大的,因为CString在while循环之内,作为局部变量,每一次循环都会构造和析构String类.
给出编译器汇编例子你看看: for(int i = 0; i < 100; i++)
0041A28C mov dword ptr [i],0
0041A293 jmp CMy_ZxClient_With_MFCDlg::OnInitDialog+7Eh (41A29Eh)
0041A295 mov eax,dword ptr [i]
0041A298 add eax,1
0041A29B mov dword ptr [i],eax
0041A29E cmp dword ptr [i],64h
0041A2A2 jge CMy_ZxClient_With_MFCDlg::OnInitDialog+0D7h (41A2F7h)
{
CString str;
0041A2A4 mov esi,esp
0041A2A6 lea ecx,[str]
0041A2A9 call dword ptr [__imp_ATL::CStringT<char,StrTraitMFC_DLL<char,ATL::ChTraitsCRT<char> > >::CStringT<char,StrTraitMFC_DLL<char,ATL::ChTraitsCRT<char> > > (42CB24h)]
//这里可以看到构造函数
0041A2AF cmp esi,esp
0041A2B1 call @ILT+2170(__RTC_CheckEsp) (41187Fh)
0041A2B6 mov dword ptr [ebp-4],0
str.Format("%d", i);
0041A2BD mov esi,esp
0041A2BF mov eax,dword ptr [i]
0041A2C2 push eax
0041A2C3 push offset string "%d" (424A24h)
0041A2C8 lea ecx,[str]
0041A2CB push ecx
0041A2CC call dword ptr [__imp_ATL::CStringT<char,StrTraitMFC_DLL<char,ATL::ChTraitsCRT<char> > >::Format (42CB1Ch)]
0041A2D2 add esp,0Ch
0041A2D5 cmp esi,esp
0041A2D7 call @ILT+2170(__RTC_CheckEsp) (41187Fh)
}
0041A2DC mov dword ptr [ebp-4],0FFFFFFFFh
0041A2E3 mov esi,esp
0041A2E5 lea ecx,[str]
0041A2E8 call dword ptr [__imp_ATL::CStringT<char,StrTraitMFC_DLL<char,ATL::ChTraitsCRT<char> > >::~CStringT<char,StrTraitMFC_DLL<char,ATL::ChTraitsCRT<char> > > (42CB20h)]
//这里是析构函数
0041A2EE cmp esi,esp
0041A2F0 call @ILT+2170(__RTC_CheckEsp) (41187Fh)
0041A2F5 jmp CMy_ZxClient_With_MFCDlg::OnInitDialog+75h (41A295h)
//这里是循环结束判断.否则跳回while.
3.一旦Receive返回SOCKET_ERROR,也就是-1,而你的这句:
tempMsg[ByteCount]=0;会发生什么事情你应该清楚吧.4.实际上socket是不可能send出0字节的数据,所以也就不可能recv到0字节的数据,所以如果Receive返回0,就说明是个错误,而你的代码中这里没有处理.5.其实我用的是异步的方式的socket,你这段代码明显是同步阻塞方式的.最后说一句,我不才,不敢说自己的代码具有工业强度,不过你这段代码未免太粗糙了点,可能自己做试验写写还好了.出产品可不敢用的.不过还是感谢你的回帖,希望我的建议对你有用.
我就奇怪为什么这么多人使用CsyncSocket等异步的socket接收数据时还用循环去接收数据.(今天回了三个以上贴子都是这样循环接收数据的).CsyncSocket使用的是消息机制,如果你使用这样的接收数据的方法也不出问题的话,那么这种模式还需要FD_READ,FD_WRITE等消息来做啥用?这种异步模型的消息机制接收数据就是要系统通知你的程序FD_READ,然后你的程序就知道有数据可读,才调用WSARecv()或recv()去接收数据.就算系统有很多数据可读,一次接不完,只要还有数据可读,系统还会继续地给你的程序发送FD_READ消息.但如果你使用循环去读,那么这些FD_READ等消息全会被过滤掉.反正有FD_READ消息来时,调用个WSARecv()或recv()接收就是.
2.socket栈刚刚好只有1000字节的数据,全部被接收了.对于第一种情况,再次循环过去,调用Receive是不会出错的.
对于第二种情况,我原先设想的是,就算没数据了,再次调用Receive也只不过触发WSAEWOULDBLOCK罢了(就像send,大量数据,就循环分批发送,如果暂时发送不了,返回WSAEWOULDBLOCK).没想到,对于第二种情况,实际上返回SOCKET_ERROR后,GetLastError获得的却是一个WSA_INVALID_HANDLE错误.而且是个不可忽略的错误,出现了这个错误,就意味着当前这条连接要断开了.对于上述问题,无法解决,所以我目前也是暂时选择不用while的recv.
"an application need not read all available data in response to an FD_READ message — a single recv in response to each FD_READ message is appropriate. If an application issues multiple recv calls in response to a single FD_READ, it can receive multiple FD_READ messages. Such an application can require disabling FD_READ messages before starting the recv calls by calling WSAAsyncSelect with the FD_READ event not set."以上是msdn关于FD_READ要注意的情况,很明确地做了说明,一个FD_READ,应该只是对应一个recv的操作.如果你想进行这样循环接收,先将FD_READ事件从socket中去除,然后接收数据,接收完后再将socket加上FD_READ事件.虽然msdn中没有明确说明为什么单个FD_READ事件触发时要执行单个的recv()操作,但我相信这样的操作关系是一个"压栈和弹出",例如触发FD_READ消息是一个"压栈"的信号,那么单个的recv操作就将这个"压栈"信号删除.但如果多个recv操作就足已将这样的配对关系搞到乱套.至于实际是否如我所想,我也不清楚了,微软没给出明确的信息,或者研究内核的会知道一二.