大家帮我分析一下socket数据接收代码,解决问题给400分

银点 2010-05-13 09:55:46
PackHeader定义如下:

typedef struct
{
ULONG PackType;
ULONG DataSize;
ULONG DataType;
void ntoh()
{
PackType = ntohl(PackType);
DataSize = ntohl(DataSize);
DataType = ntohl(DataType);
}
void hton()
{
PackType = htonl(PackType);
DataSize = htonl(DataSize);
DataType = htonl(DataType);
}
}PackHeader;



void CClientSocket::OnRead()
{
//检查缓冲数据长度,如果长度小于包头,返回
DWORD DataLen = 0;
if(ioctlsocket(m_Socket,FIONREAD,&DataLen) != 0)
return;
if(DataLen < sizeof(PackHeader) )
return;

//从缓冲区中预读包头,以确定数据内数据是否收满。
PackHeader ph;
ZeroMemory(&ph,sizeof(ph));
DWORD dwRet=recv(m_Socket,(char*)&ph,sizeof(PackHeader),MSG_PEEK);//疑问1:上边的recv带MSG_PEEK标志执行时,是不是每次都从缓冲区开始PEEK出指大小?
if(dwRet==SOCKET_ERROR)
{
WSAGetLastError();
MessageBox(NULL,"recvfrom failed","sock",MB_OK);
OnClose();
return;
}
ph.ntoh();
//数据长度不够,返回继续等后续数据
//问题出在这里。收了几个包后PEEK出来的包头就是错的了。从下边直接返回,导致缓冲区快速填满,服务器端就发不出数据来了。
if(DataLen < ph.DataSize + sizeof(PackHeader))
return;

//数据收齐,读出处理先收头
dwRet=recv(m_Socket,(char*)&ph,sizeof(PackHeader),0);
if(dwRet==SOCKET_ERROR)
{
WSAGetLastError();
MessageBox(NULL,"recvfrom failed","sock",MB_OK);
OnClose();
return;
}
ph.ntoh();
//再收数据内容
UCHAR* pbFormat = NULL;
WORD cbFormat = ph.DataSize;
pbFormat = new UCHAR[cbFormat];
ZeroMemory(pbFormat,cbFormat);

dwRet=recv(m_Socket,(char*)pbFormat,cbFormat,0);
if(dwRet==SOCKET_ERROR)
{
WSAGetLastError();
MessageBox(NULL,"recvfrom failed","sock",MB_OK);
OnClose();
delete[] pbFormat;
return;
}
if(m_pOutputPin == NULL)
{
OnClose();
delete[] pbFormat;
return;
}

if(ph.DataType == PT_VIDEO)
{
m_pOutputPin->ProcessData((char*)pbFormat,cbFormat);
}

delete[] pbFormat;

}


大家帮我分析一下,上边的代码有什么问题么?
...全文
259 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
「已注销」 2010-05-13
  • 打赏
  • 举报
回复
有钱人◎_◎
周药师 2010-05-13
  • 打赏
  • 举报
回复
这个不能少
//示接收没有结束 还有数据 要你继续去接收
if (WSAEWOULDBLOCK == WSAGetLastError())
{
continue;
}

还有lz一定要注意的问题:
WSAEventSelect模型的不足之处是:每个WSAEventSelect模型最多只能管理64个套接字。
当应用程序中需要管理多余64个套接字时,就需要额外创建线程
银点 2010-05-13
  • 打赏
  • 举报
回复
按周版的方法,不停的报10035错误,是不是我的代码哪里还有问题?

void CClientSocket::OnRead()
{
WSAEventSelect(m_Socket,SocketEvent[0],0);//取消网络事件
DWORD DataLen = 0;
char OutputStr[128] ={'\0'};

if(ioctlsocket(m_Socket,FIONREAD,&DataLen) != 0)
return;
if(DataLen < sizeof(PackHeader) )
return;


PackHeader ph;
ZeroMemory(&ph,sizeof(ph));

//数据收齐,读出处理先收头
DWORD dwRet=recv(m_Socket,(char*)&ph,sizeof(PackHeader),0);
if(dwRet==SOCKET_ERROR)
{
WSAGetLastError();
MessageBox(NULL,"recvfrom failed","sock",MB_OK);
OnClose();
return;
}
ph.ntoh();
//再收数据内容
UCHAR* pbFormat = NULL;
WORD cbFormat = ph.DataSize;
pbFormat = new UCHAR[cbFormat];
ZeroMemory(pbFormat,cbFormat);
DWORD got = 0;
while(got >= 0 && got < cbFormat)
{
dwRet = recv(m_Socket,(char*)pbFormat,cbFormat,0);
if(dwRet==SOCKET_ERROR)
{
dwRet = WSAGetLastError();
sprintf(OutputStr,"Socket Error,Error Code is :%d\n\r",dwRet);
OutputDebugString(OutputStr);
//OnClose();
//delete[] pbFormat;
//return;
}
got += dwRet;
}
WSAEventSelect(m_Socket,SocketEvent[0],FD_CONNECT|FD_READ|FD_CLOSE);

if(m_pOutputPin == NULL)
{
OnClose();
delete[] pbFormat;
return;
}

if(ph.PackType == PT_MEDIATYPE )
{

}
else if(ph.DataType == PT_VIDEO)
{
m_pOutputPin->ProcessData((char*)pbFormat,cbFormat);
}

delete[] pbFormat;

}
周药师 2010-05-13
  • 打赏
  • 举报
回复
在我3楼的代码里加上
WSAEventSelect(m_s, m_hEvent, 0);//取消网络事件
之所以这样是因为多次调用recv,会引发系统再次发送FD_READ事件。
最后通讯结束再调用
//注册网络事件
WSAEventSelect(m_s, m_hEvent, FD_READ|FD_CLOSE);
银点 2010-05-13
  • 打赏
  • 举报
回复
谢谢,周版的方法有效。
corispo11 2010-05-13
  • 打赏
  • 举报
回复
学习下
周药师 2010-05-13
  • 打赏
  • 举报
回复

//获取数据包体的长度
PACKETHDR packetHdr;
reVal = recv(m_s, (char*)&packetHdr, PACKETHDRLEN, 0);

if ( 0 == reVal)
{
return FALSE;
}else if (SOCKET_ERROR == reVal)//网络错误
{
int nErrCode = WSAGetLastError();
if (WSAEWOULDBLOCK == nErrCode)
{
return TRUE;
}
AfxMessageBox("接收用户列表错误!");
return FALSE;
}else
{
CString strUserInfo; //用户信息
int nTotalLen = 0; //已经读取字符数量
int nDataLen = packetHdr.len; //数据长度
int nReadLen = 0; //每次读取字符数量
while ( nTotalLen != nDataLen )
{
char cRecv; //接收字符
nReadLen = recv(m_s, &cRecv, Number,0); //每次接收Number个字符
if (SOCKET_ERROR == nReadLen) //网络错误
{
if (WSAEWOULDBLOCK == WSAGetLastError())
{
continue;
}

reVal = FALSE;
}else if (0 == nReadLen)
{
AfxMessageBox(_T("客户端关闭了连接!"));
reVal = FALSE;
}else if (nReadLen > 0)
{
if ('<' == cRecv) //开始字符
{
strUserInfo.Empty();

}else if ('>' == cRecv) //结束字符
{
break;
}else
{
strUserInfo += cRecv; //添加字符
}
nTotalLen += nReadLen;
}
}
//注册网络事件
WSAEventSelect(m_s, m_hEvent, FD_READ|FD_CLOSE);
}
周药师 2010-05-13
  • 打赏
  • 举报
回复

//从缓冲区中预读包头,以确定数据内数据是否收满。
PackHeader ph;
ZeroMemory(&ph,sizeof(ph));
DWORD dwRet=recv(m_Socket,(char*)&ph,sizeof(PackHeader),MSG_PEEK);//疑问1:上边的recv带MSG_PEEK标志执行时,是不是每次都从缓冲区开始PEEK出指大小?
if(dwRet==SOCKET_ERROR)
{
WSAGetLastError();
MessageBox(NULL,"recvfrom failed","sock",MB_OK);
OnClose();
return;
}


把这你这部分代码屏蔽掉试试

你的"从缓冲区中预读包头"这个就是的数据应该是数据包体的长度,就一个长度数据
不需要MSG_PEEK来

通常应避免使用与MSG_PEEK recv()。它是一种非常低效的方法来检索数据,并明确不推荐如果您关心的性能。一个更好的解决方案是只需接收到用户缓冲区的数据并在那里直接使用它

周药师 2010-05-13
  • 打赏
  • 举报
回复

//使用MSG_PEEK将极大地降低系统的性能!!!
DWORD dwRet=recv(m_Socket,(char*)&ph,sizeof(PackHeader),0);

1,317

社区成员

发帖
与我相关
我的任务
社区描述
C++ Builder 网络及通讯开发
社区管理员
  • 网络及通讯开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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