WinSocket Send 内存泄漏疑问

jingxs1202 2012-12-23 12:55:50
问题是这样的,我封装了socket和ODBC(大量借鉴了前辈的成果,表示感谢),单独使用都没有问题,但如果把数据集发送到客户端时,经过大约20次(与socket缓冲区大小有关)后,数据库就不能正常工作了。请教高人指点。
(全部代码贴不下,感兴趣的请给邮箱地址,只贴出关键函数)
Socket:

int CJXSSocket::Receive(char *recvbuff, int nBuffLen, int nFlag/*=0*/, int nTimeOut/*=0*/)
{
int nRecvCount=0;
if (nBuffLen<=0)
{
m_strError.Format(_T("接收缓冲区太小。"));
return nRecvCount;
}
if (!m_bConnected)
{
m_strError.Format(_T("尚未连接服务器。"));
return nRecvCount;
}
//准备超时判定
struct timeval timeout;
timeout.tv_sec=nTimeOut/1000;
timeout.tv_usec=nTimeOut%1000;
m_bTimeOut=FALSE;
fd_set fdRead;
FD_ZERO(&fdRead);
SOCKET sktSelect=m_socket;
FD_SET(sktSelect, &fdRead);
fd_set fdError=fdRead;
//setsockopt(m_socket,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(timeout));
int nSelectRet=0;
if (nTimeOut==0)
nSelectRet=select(sktSelect+1,&fdRead, 0, &fdError, NULL);
else
nSelectRet=select(sktSelect+1,&fdRead, 0, &fdError, &timeout);
switch(nSelectRet)
{
case 0:
m_bDataTooOld=TRUE;
m_bTimeOut=TRUE;
m_strError.Format(_T("接收数据超时,错误代码是:%d"), WSAGetLastError());
break;
case -1:
m_strError.Format(_T("接收数据失败,错误代码是:%d"), WSAGetLastError());
break;
default:
if(FD_ISSET(sktSelect,&fdRead))
{
nRecvCount=recv(m_socket, recvbuff, nBuffLen, nFlag);
//if (nRecvCount==0||nRecvCount == SOCKET_ERROR) //发送端关闭连接,或接收错误
// OnClose(WSAGetLastError());
if (nRecvCount==0) //发送端关闭连接
OnClose(WSAGetLastError());
else if (nRecvCount == SOCKET_ERROR) //接收错误
{
ULONG lErrorCode=WSAGetLastError();
if (lErrorCode>=WSAENETDOWN && lErrorCode<=WSAESHUTDOWN)
OnClose(lErrorCode);
//else if(lErrorCode==EAGAIN) //接收超时
//{
// m_bDataTooOld=TRUE;
// m_bTimeOut=TRUE;
// m_strError.Format(_T("接收数据超时,错误代码是:%d"), lErrorCode);
//}
else
m_strError.Format(_T("接收数据失败,错误代码是:%d"), lErrorCode);

//printf("Error code:%d\n", lErrorCode);
}
//上次接收操作超时,本次丢弃上次未做接收处理的数据
else
{
if (m_bDataTooOld)
{
m_bDataTooOld=FALSE;
//超时自动丢弃上次应该接收的数据
//Receive(recvbuff, nBuffLen, nFlag, nTimeOut);
}
}
}
else
m_strError.Format(_T("未知接收状态,错误码是:%d"), WSAGetLastError());
if (FD_ISSET(sktSelect,&fdError))
{
printf("Error this socket");
}
}
//printf("Receive count %d\n", nRecvCount);
return nRecvCount;
}
BOOL CJXSSocket::ReceiveOneFrame(char *pframe, int nFrameLen, int nFlag/*=0*/, int nTimeOut/*=0*/)
{
int nRecvCount=0;
int nRecv=0;
//char *pBuff;//=(char *)pframe;
char *pBuff=(char *)pframe;
//pBuff=new char[nFrameLen+1];
//memset(pBuff, 0, sizeof(char)*(nFrameLen+1));
while(nRecvCount<nFrameLen)
{
nRecv=Receive(pBuff+nRecvCount, nFrameLen-nRecvCount, nFlag, nTimeOut);
if (nRecv>0)
{
nRecvCount+=nRecv;
//pBuff+=nRecv;
if (nRecvCount>=nFrameLen)
break;
}
else
return FALSE;
}
//memcpy(pframe, pBuff, sizeof(char)*nFrameLen);
//delete []pBuff;
return TRUE;
}
int CJXSSocket::Send(char *sendbuff, int nBuffLen, int nFlag/*=0*/, int nTimeOut/*=0*/)
{
int nSend=0;
if (nBuffLen<=0)
{
m_strError.Format(_T("没有要发送的数据。"));
return nSend;
}
if (!m_bConnected)
{
m_strError.Format(_T("尚未连接服务器。"));
return nSend;
}
//准备超时判定
struct timeval timeout;
timeout.tv_sec=nTimeOut/1000;
timeout.tv_usec=nTimeOut%1000;
fd_set fdWrite;
FD_ZERO(&fdWrite);
SOCKET sktSelect=m_socket;
FD_SET(sktSelect, &fdWrite);
fd_set fdError=fdWrite;

//setsockopt(m_socket,SOL_SOCKET,SO_SNDTIMEO,(char *)&timeout,sizeof(timeout));
int nSelectRet=0;
if (nTimeOut==0)
nSelectRet=select(sktSelect+1, 0, &fdWrite, &fdError, NULL);
else
nSelectRet=select(sktSelect+1, 0, &fdWrite, &fdError, &timeout);
switch(nSelectRet)
{
case 0:
m_bDataTooOld=TRUE;
m_bTimeOut=TRUE;
m_strError.Format(_T("发送数据超时,错误代码是:%d"), WSAGetLastError());
break;
case -1:
m_strError.Format(_T("发送数据失败,错误代码是:%d"), WSAGetLastError());
break;
default:
if(FD_ISSET(sktSelect,&fdWrite))
{
nSend=send(m_socket, sendbuff, nBuffLen, nFlag);
m_bTimeOut=FALSE;
if (nSend==SOCKET_ERROR) //发送异常
{
ULONG lErrorCode=WSAGetLastError();
if (lErrorCode==EAGAIN)
{
m_bTimeOut=TRUE;
m_strError.Format(_T("发送数据超时,错误代码是:%d"), lErrorCode);
}
else
{
m_strError.Format(_T("数据发送失败,错误码是:%d"), lErrorCode);
if (lErrorCode!=WSAEINPROGRESS && lErrorCode!=WSAEWOULDBLOCK)
OnClose(lErrorCode);
}
}
else if (nSend==0)
OnClose(WSAGetLastError()); //接收端已关闭连接
}
if(FD_ISSET(sktSelect,&fdError))
{
m_strError.Format(_T("数据发送失败,错误码是:%d"), WSAGetLastError());
printf("OOB comming...\n");
}
}
//else
//{
// printf("%d", WSAGetLastError());
//}
return nSend;
}
BOOL CJXSSocket::SendOneFrame(char *pframe, int nFrameLen, int nFlag/*=0*/, int nTimeOut/*=0*/)
{
if (nFrameLen<=0)
return FALSE;
int nSendCount=0;
int nSend=0;
BOOL bRet=TRUE;
//char *pBuff;//=(char *)pframe;
char *pBuff=(char *)pframe;
//pBuff=new char[nFrameLen+1];//(char *)malloc(sizeof(char)*(nFrameLen+1));
////pDel=pBuff;
//memset(pBuff, 0, sizeof(char)*(nFrameLen+1));
//memcpy(pBuff, pframe, sizeof(char)*nFrameLen);

while(nSendCount<nFrameLen)
{
//WSASend(
nSend=Send(pBuff+nSendCount, nFrameLen-nSendCount, nFlag);
if(nSend>0)
{
nSendCount+=nSend;
//pBuff+=nSend;
if (nSendCount>=nFrameLen)
break;
}
else
bRet = FALSE;
}
//delete []pBuff;
return bRet;
}

ODBC


SQLRETURN CJXSSQLDirect::ExecuteSQL(LPCTSTR srtCommand)
{
//SetBusy();
BOOL bRet=TRUE;
SQLRETURN nRet=SQL_ERROR;
if (m_hStmt!=INVALID_HANDLE_VALUE && m_hStmt!=NULL)
TRYODBC(m_hStmt, SQL_HANDLE_STMT, nRet=SQLFreeStmt(m_hStmt,SQL_CLOSE));
if (bRet)
TRYODBC(m_hDBC, SQL_HANDLE_DBC, SQLAllocHandle(SQL_HANDLE_STMT, m_hDBC, &m_hStmt));
if (bRet)
TRYODBC(m_hStmt, SQL_HANDLE_STMT, SQLSetStmtAttr(m_hStmt, SQL_ATTR_CURSOR_TYPE, SQL_CURSOR_FORWARD_ONLY, SQL_IS_INTEGER));
if (bRet)
TRYODBC(m_hStmt, SQL_HANDLE_STMT, SQLSetStmtAttr(m_hStmt, SQL_ATTR_CONCURRENCY, (SQLPOINTER)SQL_CONCUR_READ_ONLY, SQL_IS_INTEGER));
if (bRet)
TRYODBC(m_hStmt, SQL_HANDLE_STMT, SQLSetStmtAttr(m_hStmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)1, SQL_IS_INTEGER));
if (bRet)
TRYODBC(m_hStmt, SQL_HANDLE_STMT, nRet=SQLExecDirect(m_hStmt, (SQLCHAR *)srtCommand, SQL_NTS));
if (bRet)
bRet=InitCols(nRet);
return nRet;
}
SQLRETURN CJXSSQLDirect::Skip(void)
{
BOOL bRet=TRUE;
SQLRETURN nRet=SQL_NO_DATA;
if (m_hStmt==INVALID_HANDLE_VALUE ||!m_hStmt)
bRet=FALSE;
if (bRet)
TRYODBC(m_hStmt,SQL_HANDLE_STMT, nRet=SQLFetch(m_hStmt));
//InitCols(nRet);
if (bRet)
{
CJXSSQLColumn *pCol;
for (int idx=0; idx<m_arrCols.GetCount(); idx++)
{
if (!m_hStmt)
AfxMessageBox(_T("Skip 导致了句柄空"));
pCol=m_arrCols.GetAt(idx);
if (pCol->m_nIndex>=0)
{
TRYODBC(m_hStmt, SQL_HANDLE_STMT, SQLGetData(m_hStmt, pCol->m_nIndex, pCol->m_nDataType, pCol->m_pValue,pCol->m_nWidth, &pCol->m_nDataSize));
if (bRet)
{
if (pCol->m_nDataSize==SQL_NULL_DATA)
memset(pCol->m_pValue, 0, sizeof(SQLCHAR)*pCol->m_nWidth);
continue;
}
else
break;
}
}
}
return nRet;
}
void *CJXSSQLDirect::GetCol(int nIdx)
{
if (nIdx>=0 && nIdx<m_arrCols.GetCount())
return m_arrCols.GetAt(nIdx)->m_pValue;
return NULL;
}
void *CJXSSQLDirect::GetCol(LPCTSTR strColName)
{
for (int idx=0; idx<m_arrCols.GetCount(); idx++)
{
if (m_arrCols.GetAt(idx)->m_strName.Trim()==strColName)
return GetCol(idx);
}
return NULL;
}

...全文
238 8 打赏 收藏 转发到动态 举报
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
jingxs1202 2012-12-24
  • 打赏
  • 举报
回复
观察各变量情况,发现m_hStmt=0,查找整个工程,没有将其置0的语句,很是奇怪!
jingxs1202 2012-12-24
  • 打赏
  • 举报
回复
单独测试ODBC结果:重复运行select name from spt_values,然后遍历结果集(2506条记录),到第98214次后,反馈:"[HY001] [Microsoft][ODBC SQL Server Driver]内存分配失败 (0)",请高手帮忙分析分析。
jingxs1202 2012-12-24
  • 打赏
  • 举报
回复
是的,我也估计是socket的问题,但说来也就那么简单地处理了下,确实看不出来问题在哪里。现在甚至连检查的思路都没有了。
UDX协议 2012-12-24
  • 打赏
  • 举报
回复
我估计是SOCKET的问题,一般ODBC这些玩意,出错率低
jingxs1202 2012-12-24
  • 打赏
  • 举报
回复
我又测试了一下,ODBC在1.7万次的时候会出问题。
jingxs1202 2012-12-23
  • 打赏
  • 举报
回复
感谢你们的回答,这个问题困扰我2个星期了。2楼的缓冲类是什么意思?我在测试代码中,分别只用socket和只操作数据库,12万次均无任何问题,但只要把数据库内容发送到客户端就会在交换大约2万次后Send减速一半,在4万次以后就出现数据库不确定的故障(一会儿是句柄无效,一会儿是句柄不合法...)。很奇怪的是,偶尔有一次是完全正确的。
xumaojun 2012-12-23
  • 打赏
  • 举报
回复
楼主先定位一下是socket通信内存泄露还是数据库操作内存泄露。
ouyh12345 2012-12-23
  • 打赏
  • 举报
回复
socket和数据库操作分开,加个缓冲类
源码下载地址: https://pan.quark.cn/s/8d2c461c797c JavaWeb程序设计构成了掌握Web交互式应用程序开发的核心领域,对于初学者来说,精通这一技术具有决定性意义。在“JavaWeb程序设计(第三版)作业答案”中,我们可以预期获得针对该教材习题的一系列深入解析,从而协助学习者强化知识体系。 JavaWeb所包含的技术组件涵盖了Servlet、JSP(JavaServer Pages)、JDBC(Java Database Connectivity)以及各类框架如Spring MVC、Struts等。Servlet是Java平台提供的一种扩展服务器功能的接口,能够处理HTTP请求并生成相应的反馈。JSP则是一种用于构建动态网页的工具,它支持开发者将HTML代码与Java代码进行整合编写,从而简化了Web应用程序的开发流程。 作业答案通常会涉及以下几个核心内容: 1. **Servlet基础**:可能包含Servlet生命周期、init(), service(), destroy()方法的应用,以及如何在web.xml文件中设定Servlet的映射关系。 2. **JSP基础**:JSP的九大内置对象,如request、response、session、application等的使用,以及EL(Expression Language)和JSTL(JavaServer Pages Standard Tag Library)的实际操作。 3. **HTTP协议理解**:GET和POST请求方法的差异,请求头与响应头的应用,以及会话管理的概念阐释。 4. **JDBC数据库操作**:与数据库建立连接,执行SQL指令,处理查询结果集,以及...
源码链接: https://pan.quark.cn/s/a4b39357ea24 斐讯K2是一款广受用户青睐的无线路由器,其运行表现稳定且具备较高的可操作性,在DIY爱好者群体中拥有极高的声誉。本资料将系统性地阐述斐讯K2的固件刷机方法及其关联的技术要点。固件升级是路由器爱好者改善设备性能、扩展功能的一种普遍手段,经由替换出厂固件,能够达成更加个性化的网络配置、增强安全防护等目标。斐讯K2固件资源库涵盖了多种知名的非官方固件,诸如Tomato Pheonix 不死鸟、高恪、PandoraBox 潘多拉等,这些固件均具备独特的优势,能够适配不同用户的需求。 1. Tomato Pheonix 不死鸟:Tomato是一款立足于Linux的开源固件,以其精巧、高效而备受推崇。不死鸟版本是专门为华硕及斐讯路由器优化的分支,提供了卓越的QoS(服务质量)配置、详尽的图表监控以及便捷的固件升级途径。对于那些需要精准调控带宽和监测网络状态的用户而言,这是一个理想的选项。 2. 高恪:高恪固件是OpenWrt的定制化版本,着重于操作的便捷性和运行的可靠性,特别适合对路由器操作不甚熟悉的用户群体。它提供了一些实用的功能,例如内置的广告屏蔽、快速测速工具等,同时保留了OpenWrt的适应性。 3. PandoraBox 潘多拉:潘多拉盒是另一款基于OpenWrt的固件,它以丰富的插件库和强大的自定义潜力而闻名。用户能够依据个人需求安装各类插件,实现更多功能,如远程接入、DDNS(动态域名解析服务)等。 4. 官方固件的纯净版本与定制版本:官方固件通常更侧重于稳定性,纯净版意味着未预置额外的应用或服务,适合注重稳定性的用户。定制版则可能包含了制造商的特色功能或优...

18,356

社区成员

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

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