最近用Winows socket基于TCP协议写一个接收socket字符串的程序,偶尔会出现数据掉失的问题。我也不清楚是否我的程序有问题,特把代码贴出来向大家请教。下面是函数说明:
//有关接收socket消息字符串的函数的说明
// GlocalReceiveMsg功能为接收整个socket字符串,pClientSocket为windows socket类的派生类的指针,// pResultBuf为返回的数据缓冲区指针,iResultLen为实际接收的字节数BOOL GlocalReceiveMsg(CMIClientSocket* pClientSocket,BYTE** pResultBuf,int&
iResultLen){ int nsize=0; // 接收包的剩余长度 DWORD dwSize = 0; // 包体长度 BYTE *pReceiveInfo = NULL; BYTE* pRstbuffer = NULL; int result = 0; BYTE PackLenBuf[STDATABAND_SIZE]; // 用于存放包头的缓冲区 // STDATABAND_SIZE为预定义的包头长度,具体数值为12 int nRecPackLen = STDATABAND_SIZE; // 接收包头的剩余长度// 初始化实际接收的长度为0 iResultLen = 0; // 接收包头 do {// Receive函数第一个参数为缓冲区指针,第二个参数为缓冲区长度,返回值为// 实际接收的字节数。注意这里有缓冲区指针的移动和缓冲区长度的变化,变化规律就是// 首先设定缓冲区长度为STDATABAND_SIZE(即12),若实际接收字节数为4,那么第二// 次设定缓冲区指针移动4个字节,长度为12-4=8,照此方法循环接收 result = pClientSocket->Receive(PackLenBuf+STDATABAND_SIZE-nRecPackLen,nRecPackLen); if(result<STDATABAND_SIZE) { if(result == SOCKET_ERROR) { AfxMessageBox("接收数据失败"); return false; } else if(0 == result) { pClientSocket->m_hSocket = INVALID_SOCKET; return FALSE; } else { nRecPackLen = nRecPackLen - result; } } else { break; } }while(nRecPackLen!=0); // 解析数据包头,获取数据包长度。数据包体长度(数据包除开包头的长度)放在包头的// 第DATA_LENGTH_INDEX字节。 memcpy(&dwSize,PackLenBuf+DATA_LENGTH_INDEX,sizeof(DWORD));// 开辟相应内存。pRstbuffer返回的外部使用的缓冲区,pReceiveInfo为临时接收缓冲区 pRstbuffer = new BYTE[dwSize+STDATABAND_SIZE]; pReceiveInfo = new BYTE[dwSize+STDATABAND_SIZE]; // 拷贝包头数据到外部缓冲区 memcpy(pRstbuffer,PackLenBuf,STDATABAND_SIZE);// 设置剩余接收的数据长度为包体长度 nsize = dwSize;// 若包体长度为0,就退出 if(nsize==0) return TRUE;// 实际接受数据的长度加上包头长度,因为已经成功接收包头 iResultLen = iResultLen + STDATABAND_SIZE; do {// 循环接收包体数据,其原理和接收包头数据类似 result = pClientSocket->Receive(pReceiveInfo,dwSize+STDATABAND_SIZE); iResultLen = iResultLen + result; if(result == SOCKET_ERROR) { AfxMessageBox("接收数据失败"); return false; } else if(0 == result) { pClientSocket->m_hSocket = INVALID_SOCKET; return FALSE; } else { // 将实际接受的包体数据从接收缓冲区拷贝到外部输出缓冲区。注意 // 这里的pRstbuffer是向前偏移的 memcpy(pRstbuffer+iResultLen-result,pReceiveInfo,result); // 现在剩余接收的长度为上次剩余接收的长度减去本次实际接受的长度 nsize-=result; // 假如剩余接收的长度为0,表示完全接收整个包,就退出循环 if(nsize==0) break; } } while(true); // 设定外部缓冲区指针 *pResultBuf = pRstbuffer;// 释放临时缓冲区的内存 delete []pReceiveInfo; return TRUE;}
//有关接收socket消息字符串的函数的说明
// GlocalReceiveMsg功能为接收整个socket字符串,pClientSocket为windows socket类的派生类的指针,// pResultBuf为返回的数据缓冲区指针,iResultLen为实际接收的字节数BOOL GlocalReceiveMsg(CMIClientSocket* pClientSocket,BYTE** pResultBuf,int&
iResultLen){ int nsize=0; // 接收包的剩余长度 DWORD dwSize = 0; // 包体长度 BYTE *pReceiveInfo = NULL; BYTE* pRstbuffer = NULL; int result = 0; BYTE PackLenBuf[STDATABAND_SIZE]; // 用于存放包头的缓冲区 // STDATABAND_SIZE为预定义的包头长度,具体数值为12 int nRecPackLen = STDATABAND_SIZE; // 接收包头的剩余长度// 初始化实际接收的长度为0 iResultLen = 0; // 接收包头 do {// Receive函数第一个参数为缓冲区指针,第二个参数为缓冲区长度,返回值为// 实际接收的字节数。注意这里有缓冲区指针的移动和缓冲区长度的变化,变化规律就是// 首先设定缓冲区长度为STDATABAND_SIZE(即12),若实际接收字节数为4,那么第二// 次设定缓冲区指针移动4个字节,长度为12-4=8,照此方法循环接收 result = pClientSocket->Receive(PackLenBuf+STDATABAND_SIZE-nRecPackLen,nRecPackLen); if(result<STDATABAND_SIZE) { if(result == SOCKET_ERROR) { AfxMessageBox("接收数据失败"); return false; } else if(0 == result) { pClientSocket->m_hSocket = INVALID_SOCKET; return FALSE; } else { nRecPackLen = nRecPackLen - result; } } else { break; } }while(nRecPackLen!=0); // 解析数据包头,获取数据包长度。数据包体长度(数据包除开包头的长度)放在包头的// 第DATA_LENGTH_INDEX字节。 memcpy(&dwSize,PackLenBuf+DATA_LENGTH_INDEX,sizeof(DWORD));// 开辟相应内存。pRstbuffer返回的外部使用的缓冲区,pReceiveInfo为临时接收缓冲区 pRstbuffer = new BYTE[dwSize+STDATABAND_SIZE]; pReceiveInfo = new BYTE[dwSize+STDATABAND_SIZE]; // 拷贝包头数据到外部缓冲区 memcpy(pRstbuffer,PackLenBuf,STDATABAND_SIZE);// 设置剩余接收的数据长度为包体长度 nsize = dwSize;// 若包体长度为0,就退出 if(nsize==0) return TRUE;// 实际接受数据的长度加上包头长度,因为已经成功接收包头 iResultLen = iResultLen + STDATABAND_SIZE; do {// 循环接收包体数据,其原理和接收包头数据类似 result = pClientSocket->Receive(pReceiveInfo,dwSize+STDATABAND_SIZE); iResultLen = iResultLen + result; if(result == SOCKET_ERROR) { AfxMessageBox("接收数据失败"); return false; } else if(0 == result) { pClientSocket->m_hSocket = INVALID_SOCKET; return FALSE; } else { // 将实际接受的包体数据从接收缓冲区拷贝到外部输出缓冲区。注意 // 这里的pRstbuffer是向前偏移的 memcpy(pRstbuffer+iResultLen-result,pReceiveInfo,result); // 现在剩余接收的长度为上次剩余接收的长度减去本次实际接受的长度 nsize-=result; // 假如剩余接收的长度为0,表示完全接收整个包,就退出循环 if(nsize==0) break; } } while(true); // 设定外部缓冲区指针 *pResultBuf = pRstbuffer;// 释放临时缓冲区的内存 delete []pReceiveInfo; return TRUE;}
解决方案 »
- 怎么发布一个由ATL编写的控件
- Url编码针对TCHAR类型,给个例子吧,最好不要用MFC,不熟悉
- 一个问题.关于程序员的感情,想了好久.听说这里人气好.问问大家.
- 谁能给点反弹式木马的原代码??
- 用VC+Oracle怎样使得要求输入用户名和密码的对话框不出现,我用了自己的方法没有成功,希望朋友给个提示
- 有关CHtmlView中加载Bitmap图片
- 有谁知道IWebBrowser2::Navigate2()的用法么??
- 请用C++或JAVA翻译这段FORTRAN语句,俺看不懂!
- 专业问题,百思不得其解,求教!
- 跪求shellexecute打开的网页文件在哪儿??
- CFrameWnd的派生类怎么才可以绑定资源文件里的窗体资源啊
- 怎么使对话框中的控件跟随对话框的大小改变位置?
result = pClientSocket->Receive(pReceiveInfo,dwSize+STDATABAND_SIZE);
应该这样:
result = pClientSocket->Receive(pReceiveInfo+STDATABAND_SIZE,dwSize);
因为你不是已经读完包头了吗?怎么读包体时的长度是包头+包体的长度.而且pReceiveInfo已有包头的数据,会被覆盖的
接收包体应该这样:
result = pClientSocket->Receive(pReceiveInfo, nsize); --------
实际上不用临时变量pReceiveInfo是可以的,就在pRstbuffer内移动就可以了
比如:
result = 0;
result = pClientSocket->Receive(pReceiveInfo+STDATABAND_SIZE+ result , nsize); 注释掉//memcpy(pRstbuffer+iResultLen-result,pReceiveInfo,result);大概这样.你调试一下
result = pClientSocket->Receive(pReceiveInfo+ iResultLen , nsize);
因此pClientSocket->m_hSocket = INVALID_SOCKET;应该替换为pClientSocket->Close(); 2.临时接收缓冲区是不必要的,完全可以在接收函数里搞定。
3.变量命名还不够标准,应该改为更有意义的变量名。 综上所述,接收函数代码改为: BOOL GlocalReceiveMsg(CMIClientSocket* pClientSocket,BYTE** pResultBuf,int& iResultLen)
{
// 待接收的数据包的长度
int nLeftDataLen = 0;
// 数据包体长度
int nPackBodyLen = 0;
// 外部接收缓冲区
BYTE* pRstbuffer = NULL;
// 接收函数一次实际收到的数据长度
int nRevOnceLen = 0;
// 包头缓冲区
BYTE PackLenBuf[STDATABAND_SIZE];
// STDATABAND_SIZE为网络数据包头,具体数值为12
int nRecPackLen = STDATABAND_SIZE;
// 初始化实际接收的长度为0
iResultLen = 0;// 接收包头数据
do
{
// Receive函数第一个参数为缓冲区指针,第二个参数为缓冲区长度,返回值为// 实际接收的字节数。注意这里有缓冲区指针的移动和缓冲区长度的变化,变化规律就是// 首先设定缓冲区长度为STDATABAND_SIZE(即12),若实际接收字节数为4,那么第二// 次设定缓冲区指针移动4个字节,长度为12-4=8,照此方法循环接收
nRevOnceLen = pClientSocket->Receive(PackLenBuf+STDATABAND_SIZE-nRecPackLen,nRecPackLen);
if(result<STDATABAND_SIZE)
{
if(nRevOnceLen == SOCKET_ERROR)
{
AfxMessageBox("接收数据失败");
return FALSE;
}
else if(0 == nRevOnceLen)
{
pClientSocket->Close();
return FALSE;
}
else
{
nRecPackLen = nRecPackLen - nRevOnceLen;
}
}
else
{
break;
}
}while(nRecPackLen!=0);
// 解析数据包头,获取数据包长度。数据包体长度(数据包除开包头的长度)放在包头的// 第DATA_LENGTH_INDEX字节。 memcpy(&nPackBodyLen,PackLenBuf+DATA_LENGTH_INDEX,sizeof(DWORD));
pRstbuffer = new BYTE[nPackBodyLen +STDATABAND_SIZE];
nLeftDataLen = nPackBodyLen +STDATABAND_SIZE;
// 拷贝包头数据到外部缓冲区
memcpy(pRstbuffer,PackLenBuf,STDATABAND_SIZE);
// 设置剩余接收的数据长度为包体长度
nLeftDataLen -=STDATABAND_SIZE;
// 若包体长度为0,就退出
if(nLeftDataLen ==0)
return TRUE; // 实际接受数据的长度加上包头长度,因为已经成功接收包头
iResultLen = iResultLen + STDATABAND_SIZE; do
{
// 循环接收包体数据,其原理和接收包头数据类似,
//注意这里的缓冲区指针pRstbuffer是向前偏移的
nRevOnceLen = pClientSocket->Receive(pRstbuffer + iResultLen, nPackBodyLen);
iResultLen = iResultLen + nRevOnceLen; if(nRevOnceLen == SOCKET_ERROR)
{
AfxMessageBox("接收数据失败");
return FALSE;
}
else if(0 == nRevOnceLen)
{
pClientSocket->Close();
return FALSE;
}
else
{
// 现在剩余接收的长度为上次剩余接收的长度减去本次实际接受的长度
nLeftDataLen -= nRevOnceLen;
// 假如剩余接收的长度为0,表示完全接收整个包,就退出循环
if(nLeftDataLen ==0)
break;
}
} while(true);
// 赋值给外部缓冲区指针
*pResultBuf = pRstbuffer; return TRUE;
}
nRevOnceLen = pClientSocket->Receive(pRstbuffer + iResultLen, nLeftDataLen); 同时感谢cnzdgs的指出.