CSOCKET 收UDP包,只收一次,再不进onreceive函数了

The_Only_Name_2 2010-09-10 02:16:12
请教下,我从csocket派生了一个cmysocket类,然后覆盖cmysocket::onreceive,在里面加自己的代码,用Receive()收数据.
我的数据是很多张连续发送的图片,每张图被分割成很多个1024大小的UDP包。
现在的情况是,我程序刚起来的时候可以收到一张图,然后就停住不走。我单步跟踪,发现都没有进cmysocket::onreceive。
应该是没有FD_READ消息来,但是我的数据源确实是一直都在给我发UDP包啊,请教这个是怎么回事呢?
代码如下:
void CMysocket::OnReceive(int nErrorCode) 
{
// TODO: Add your specialized code here and/or call the base class
static CCsocketDlg *pDlg=(CCsocketDlg*)(AfxGetApp()-> m_pMainWnd);
int len = sizeof(SOCKADDR);
static int m=0;
if (count==0)
{
pDlg->sockServer.Receive(pDlg->recvBuf,pDlg->pack_size);
if(((pDlg->recvBuf)[0]==0x5a)&&((pDlg->recvBuf)[1]==0x4d)&&((pDlg->recvBuf)[3]==0x10))
{
lenth=(pDlg->recvBuf)[7]*256*256*256+(pDlg->recvBuf)[6]*256*256+(pDlg->recvBuf)[5]*256+(pDlg->recvBuf)[4];
packs=(lenth/pDlg->pack_size+(lenth%pDlg->pack_size?1:0));
count=1;
}
}
else
{
if (count!=packs)
{
pDlg->sockServer.Receive(pDlg->recvBuf+count*(pDlg->pack_size),((packs-count)==1)?(lenth-count*(pDlg->pack_size)):pDlg->pack_size);
count++;
}
else
{
CFile file;
CString filename;
filename.Format("%d.jpg",GetTickCount());
if (!file.Open(filename,CFile::modeCreate | CFile::modeReadWrite))
{
pDlg->MessageBox("open file error!");
}
file.Write(pDlg->recvBuf+8,lenth);
file.Close();
count=0;
}
}
}
...全文
443 27 打赏 收藏 转发到动态 举报
写回复
用AI写文章
27 条回复
切换为时间正序
请发表友善的回复…
发表回复
The_Only_Name_2 2010-09-16
  • 打赏
  • 举报
回复
煎熬这么多天,问题终于有些眉目了。

用CSOCKET只收一幅图,而后不进线程函数这个原因还没有找到,估计一时也找不到,先暂时搁置了。

用线程函数循环收会出现10040错误,是因为UDP包包序有问题,最后一包不足1024,我收的时候BUFFER设成取余,如果包序反了,倒数第二包可能在最后一次收到,那么给最后一包准备的BUFFER就放不下1024字节,于是出现10040错误,我根据接收返回值来判断接收数据是否成功,成功则显示图片,不成功则从新开始收下一张,所以这样就会造成我丢掉很多张图片,然后画面就非常慢。

开始以为是局域网,网络状况非常好,所以没考虑这个问题。但是实际情况是跨了有三四个交换机还有一个服务器(上面有软路由),这样就造成包序问题。

现在给每个包加上包序再组装就没问题了,只是包序,没有丢包情况。

问题还是出在上层应用逻辑。写在这里,希望对大家有所借鉴。

另外,感谢这么多朋友的帮助,非常感谢!!
The_Only_Name_2 2010-09-15
  • 打赏
  • 举报
回复
WSAGetLastError(),返回10040,网上查的解释如下:

WSAEMSGSIZE (10040) Message too long.
在数据报套接字上发送的一个消息大于内部消息缓冲区或一些其它网络限制,或者是用来接受数据报的缓冲区小于数据报本身。

不是很明白这里讲的什么意思,在数据报套接字上发送的一个消息,是指UDP包吧,大于内部消息缓冲区,这里的缓冲区指什么?网卡缓存?进程栈区?还是套接字的缓冲区?前面我说的几个是不是存在这样一个缓冲区呢,那么它们的长度怎么取得,能设置吗?怎样设?

或者这里的缓冲区是我在recvfrom里面传进的参数recvbuffer的本身长度?还是后面带的接收长度?我上面问的也是我一直不知道的,这个数据存取是怎样一个过程呢,先到哪里,然后谁去取,取到哪里,是怎样一个步骤?

或者我这个问题要怎么解决呢?
The_Only_Name_2 2010-09-15
  • 打赏
  • 举报
回复
大约每四张图片就有一张接收失败
接收失败的这幅图recefrom收尾包的时候,返回值是-1
WSAGetLastError(),返回10040
问题范围越来越小了,希望能解决!
Eleven 2010-09-14
  • 打赏
  • 举报
回复
为啥都喜欢用CSocket类,直接用socket API不是更好,WSAAysncSelect或WSAEventSelect(...,FD_READ|....);
bragi523 2010-09-14
  • 打赏
  • 举报
回复
csocket的每一个receive后面都要有AsyncSelect(FD_READ|FD_CLOSE);


最好,每个onreceive里面只有一次receive和一个AsyncSelect(FD_READ|FD_CLOSE);
The_Only_Name_2 2010-09-14
  • 打赏
  • 举报
回复
好几天都没人回答呢,不能沉了,顶起!
regainworld 2010-09-14
  • 打赏
  • 举报
回复
一般接收线程里不能做复杂耗时操作,否则这段时间内来的UDP包都丢了

用CSocket应该是写文件的时候,来的包堵塞了
用SOCKET显然是处理图像的时候丢包了

要么打开一个文件,边收边往里写;要么缓冲区队列,另一个线程负责写盘。

楼主可以试着把第一段代码里的写文件那些都去掉,只设置count=0;试试,加个TRACE,如果一直能收到,那就是写盘的事儿。
VCLIFE 2010-09-14
  • 打赏
  • 举报
回复
顶部长的话,用CSOCKET真的是灾难....不建议用这个,如果是玩玩的话,可以用
The_Only_Name_2 2010-09-14
  • 打赏
  • 举报
回复
void CWSAAsyncSelect_moduleDlg::WndProc (WPARAM wParam, LPARAM lParam)
{
char a[1024];
CString lenth_log;
// str.Format("%d",++number);
if (WSAGETSELECTERROR(lParam))
{
return;
}

switch(WSAGETSELECTEVENT(lParam))
{
case FD_READ:
{
//收头包
if (count==0)
{
m+=recvfrom(sockServer,recvBuf,pack_size*2,0,(SOCKADDR *)&(addrClient),&len);
if (m!=1024)
{
//
}
if(((recvBuf)[0]==0x5a)&&((recvBuf)[1]==0x4d)&&((recvBuf)[3]==0x10))
{
lenth=(recvBuf)[7]*256*256*256+(recvBuf)[6]*256*256+(recvBuf)[5]*256+(recvBuf)[4];
packs=((11+lenth)/pack_size);//求得整包数有多少
count=1;
}
else
{
m=0;
break;
}
}
//收的不是头包
else
{
//收pack_size的整包
if (count<packs)
{
m+=recvfrom(sockServer,recvBuf+count*pack_size,pack_size*2,0,(SOCKADDR *)&(addrClient),&len);
if (m!=1024)
{
// lenth_log.Format("%d\t",m);
// str+=lenth_log;
}
count++;
}
//整包收完,如果还有残余数据,收完显示,否则直接显示
else
{
//收不足整包的尾包
if ((lenth+11)%pack_size)
{
m+=recvfrom(sockServer,recvBuf+count*pack_size,pack_size*2,0,(SOCKADDR *)&(addrClient),&len);
if (m!=((lenth+11)%pack_size))
{
// AfxMessageBox("3");
}
if (m==SOCKET_ERROR)
{
//AfxMessageBox("SOCKET_ERROR");
}
}
//检查图片数据是否接受完整
if(m==(lenth+11))
{
lenth=(recvBuf)[7]*256*256*256+(recvBuf)[6]*256*256+(recvBuf)[5]*256+(recvBuf)[4];
//显示图片
pData=GlobalLock(hMem);
memcpy(pData,recvBuf+8,lenth);
GlobalUnlock(hMem);
CreateStreamOnHGlobal(hMem,TRUE,&m_pStream);
OleLoadPicture(m_pStream,lenth,TRUE,IID_IPicture,(LPVOID*)&m_pPictrue);
m_pPictrue->get_Height(&m_JPGHeight);
m_pPictrue->get_Width(&m_JPGWidth);
GetDlgItem(IDC_STATIC_PICTURE)->GetWindowRect(&lpRec);
ScreenToClient(&lpRec);
m_pPictrue->Render(GetDC()->m_hDC,lpRec.left,lpRec.top,(int)(m_JPGWidth/26.45),(int)(m_JPGHeight/26.45),0,m_JPGHeight,m_JPGWidth,-m_JPGHeight,NULL);
m_total++;
//显示图片结束
// pDlg->show_ready.ResetEvent();
}
else
{
m_drop++;
// break;
}
UpdateData(FALSE);
count=0;
m=0;
memset(recvBuf,0,buff_size);
// memset(pData,0,buff_size);
}
}
GetDlgItem(IDC_STATIC1)->SetWindowText(str);
}
}
}
The_Only_Name_2 2010-09-14
  • 打赏
  • 举报
回复
[Quote=引用 18 楼 visualeleven 的回复:]
为啥都喜欢用CSocket类,直接用socket API不是更好,WSAAysncSelect或WSAEventSelect(...,FD_READ|....);
[/Quote]

开始是用SOCKET做的,效果不理想,丢包严重,然后才用CSOCKET,SOCKET的实现代码贴在上面,高手可以帮忙看看,哪里问题。
The_Only_Name_2 2010-09-14
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 bragi523 的回复:]
csocket的每一个receive后面都要有AsyncSelect(FD_READ|FD_CLOSE);


最好,每个onreceive里面只有一次receive和一个AsyncSelect(FD_READ|FD_CLOSE);
[/Quote]

加了也不行
The_Only_Name_2 2010-09-11
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 lijianli9 的回复:]
m+=recvfrom(pDlg->sockServer,pDlg->recvBuf,pack_size,0,(SOCKADDR *)&(pDlg->addrClient),&len);
if (*(pDlg->recvBuf)!=0x5a||*(pDlg->recvBuf+1)!=0x4d||*(pDlg->recvBuf+3)!=0x10)
{
continu……
[/Quote]

没有的,我断点打在函数开头,连函数都没进,怎么会到这里
The_Only_Name_2 2010-09-11
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 skfeng36 的回复:]
你的意思是收到了第一个数据包,还是第一幅完整的图之后后面的数据就收不到。。。。如果是第一种情况那么很可能是后面的数据丢失了。。第二种的话就是你代码逻辑的问题了。。。
[/Quote]

是你说的第二种情况,收到一副图就没了,不进onreceive函数
  • 打赏
  • 举报
回复
你的意思是收到了第一个数据包,还是第一幅完整的图之后后面的数据就收不到。。。。如果是第一种情况那么很可能是后面的数据丢失了。。第二种的话就是你代码逻辑的问题了。。。
The_Only_Name_2 2010-09-11
  • 打赏
  • 举报
回复
贴出来的第一段代码是收一幅图就不进onreceive函数了
第二段代码是会一直在循环里面,一直在收图,只是慢
log到文件中,这个方法不是太了解,跟踪调试我一般都是大断点看的
lijianli9 2010-09-11
  • 打赏
  • 举报
回复
m+=recvfrom(pDlg->sockServer,pDlg->recvBuf,pack_size,0,(SOCKADDR *)&(pDlg->addrClient),&len);
if (*(pDlg->recvBuf)!=0x5a||*(pDlg->recvBuf+1)!=0x4d||*(pDlg->recvBuf+3)!=0x10)
{
continue;
}
有没有到这里,你还是多log到文件中分析吧,
lijianli9 2010-09-11
  • 打赏
  • 举报
回复
从你的线程没有看出来什么代码逻辑问题?
建议LOG
The_Only_Name_2 2010-09-11
  • 打赏
  • 举报
回复
DWORD WINAPI RecvProc(LPVOID lpParameter)//参数lpParameter为主对话框指针
{
CUdp_transport_svrDlg *pDlg=(CUdp_transport_svrDlg *) lpParameter;
CUdp_transport_svrDlg *pt=(CUdp_transport_svrDlg*)CWnd::FromHandle(((CUdp_transport_svrDlg*)lpParameter)->m_hWnd);//get the handle to post message

int len = sizeof(SOCKADDR);
UINT lenth=0;
ULONG lenthWritten;
int n,m=0;
int pack_size=1024;
IStream *m_pStream;
IPicture *m_pPictrue;
OLE_XSIZE_HIMETRIC m_JPGWidth,m_JPGHeight;
HGLOBAL hMem;
CRect lpRec;
long timeTest1,timeTest2;
LPVOID pData=NULL;
FD_SET fdread;
int ret;
unsigned long arg = 0;
if(SOCKET_ERROR==ioctlsocket(pDlg->sockServer, FIONREAD, &arg))//设置阻塞式
pDlg->MessageBox("ioctlsocket set error.");

//循环接收图片数据并显示
while(1)
{
m=0;
//接收第一包数据并检查是否为头包,若是则取出图片长度,否则跳过,收下一包
m+=recvfrom(pDlg->sockServer,pDlg->recvBuf,pack_size,0,(SOCKADDR *)&(pDlg->addrClient),&len);
if (*(pDlg->recvBuf)!=0x5a||*(pDlg->recvBuf+1)!=0x4d||*(pDlg->recvBuf+3)!=0x10)
{
continue;
}
lenth=pDlg->recvBuf[7]*256*256*256+pDlg->recvBuf[6]*256*256+pDlg->recvBuf[5]*256+pDlg->recvBuf[4];
//循环接收图片
for (n=1;n<(lenth+11)/pack_size;n++)
{
m+=recvfrom(pDlg->sockServer,pDlg->recvBuf+n*pack_size,pack_size,0,(SOCKADDR *)&(pDlg->addrClient),&len);
}
//收尾包
if ((lenth+11)%pack_size)
{
m+=recvfrom(pDlg->sockServer,pDlg->recvBuf+n*pack_size,(lenth+11)%pack_size,0,(SOCKADDR *)&(pDlg->addrClient),&len);
}

//检查图片属否完整,不完整则丢弃
if (m!=lenth+11)
{
continue;
}

//显示图片
hMem=GlobalAlloc(GMEM_MOVEABLE,lenth);
pData=GlobalLock(hMem);
memcpy(pData,pDlg->recvBuf+8,lenth);
GlobalUnlock(hMem);
CreateStreamOnHGlobal(hMem,TRUE,&m_pStream);
OleLoadPicture(m_pStream,len,TRUE,IID_IPicture,(LPVOID*)&m_pPictrue);
m_pPictrue->get_Height(&m_JPGHeight);
m_pPictrue->get_Width(&m_JPGWidth);
pDlg->GetDlgItem(IDC_STATIC_PICTURE)->GetWindowRect(&lpRec);
pDlg->ScreenToClient(&lpRec);
m_pPictrue->Render(pDlg->GetDC()->m_hDC,lpRec.left,lpRec.top,(int)(m_JPGWidth/26.45),(int)(m_JPGHeight/26.45),0,m_JPGHeight,m_JPGWidth,-m_JPGHeight,NULL);
//显示图片结束
}
return 0;
}
「已注销」 2010-09-11
  • 打赏
  • 举报
回复
把你所谓很慢的线程代码贴上来。
The_Only_Name_2 2010-09-10
  • 打赏
  • 举报
回复
用线程循环接收是可以一直收的,问题只在费时,收一个1024字节的UDP包要100MS,收十几个要500MS,对方发快一点就会丢包,不知道是哪里的问题
加载更多回复(6)

18,356

社区成员

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

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