在UDP中使用IOCP,内存以4K的速度增长

zwz1984 2011-09-17 08:32:19
大家好。我在一个项目中,用到了IOCP模型,但是采用的是UDP协议。

设计思路:
客户端:将请求投入队列中,开一后台线程,阻塞的发送。
服务端:利用IOCP模型,收到数据后,存入队列中,马上投递新的请求;后台开一个线程,对队列中的请求进行处理。

出现问题:
在通信过程中,服务器的内存会以4K的速度增长。

请各位大哥,帮小弟看下问题出在哪?非常感谢!

源码如下:

class CInitSock // 初始化Winsock库
{
public:
CInitSock(BYTE minorVer = 2, BYTE majorVer = 2)
{
WSADATA wsaData;
WORD sockVersion = MAKEWORD(minorVer, majorVer);
if(::WSAStartup(sockVersion, &wsaData) != 0)
{
exit(0);
}
}
~CInitSock()
{
::WSACleanup();
}
} initSocket;

CIUSocket::CIUSocket(void)
{
m_sUdp = INVALID_SOCKET;
m_hCompletion = NULL;
m_hHandlePacket = NULL;
m_bExitFlag = FALSE;
m_pPerIO = NULL;

// 获取文件路径
TCHAR szRoot[MAX_PATH];
GetModuleFileName(NULL, szRoot, MAX_PATH);
PathRemoveFileSpec(szRoot);

// 设置日志路径
CStringA strA(szRoot);
strA = strA + "\\ServerComsLog.txt";
m_ComsLog.SetLogPath(strA);

// 打开日志
m_ComsLog.OpenLog();

// 开始通信
m_ComsLog.WriteLog("开始通信");
}

CIUSocket::~CIUSocket(void)
{
// 停止通信
m_ComsLog.WriteLog("停止通信");

// 关闭文件
m_ComsLog.CloseLog();
}

DWORD WINAPI CIUSocket::ServerThread(LPVOID lpParam)
{
CIUSocket *pThis = static_cast<CIUSocket*>(lpParam);

DWORD dwTrans = 0, dwKey = 0;
PPER_IO_DATA pPerIO;
while(TRUE)
{
if (pThis->m_bExitFlag)
{
break;
}

// 在关联到此完成端口的所有套节字上等待I/O完成
BOOL bOK = ::GetQueuedCompletionStatus(
pThis->m_hCompletion,
&dwTrans,
&dwKey,
(LPOVERLAPPED*)&pPerIO,
WSA_INFINITE);

// 在此套节字上有错误发生
if(!bOK)
{
// 关闭时,此处会出现异常,故跳过
if (pThis->m_bExitFlag)
{
break;
}

pThis->m_ComsLog.WriteLog("GetQueuedCompletionStatus 执行错误");

::GlobalFree(pPerIO);
continue;
}

if(dwTrans == 0 && (pPerIO->nOperationType == OP_READ || pPerIO->nOperationType == OP_WRITE))
{
pThis->m_ComsLog.WriteLog("读写长度为零");

::GlobalFree(pPerIO);
continue;
}

switch(pPerIO->nOperationType)
{
case OP_READ:
{
pThis->m_csThread.Lock();

int nOffset = 0;

// 投递到一个等待处理的队例中
REQ_DATA *pReqListData = new REQ_DATA;

// 请求地址
memcpy((char*)&pReqListData->addr, (char*)&pPerIO->addr, sizeof(sockaddr_in));

// 请求长度
pReqListData->addrLen = pPerIO->addrLen;

if (dwTrans >= MIN_PACKET_SIZE)
{
// 请求索引
pReqListData->ulReqIndex = *(unsigned long*)pPerIO->buf;

// 请求时间
pReqListData->llReqTime = *(unsigned short*)(pPerIO->buf + 4);

// 请求次数
pReqListData->usReqTimes = *(unsigned short*)(pPerIO->buf + 12);

// 请求缓冲
memcpy(pReqListData->szReqBuf, pPerIO->buf + 14, dwTrans - 14);

// 请求长度
pReqListData->usReqLen = (unsigned short)dwTrans - 14;

// 添加请求
pThis->m_cReqList.AppendReqData(pReqListData);
}

pThis->m_csThread.Unlock();

pThis->PostRecv( pPerIO ); //重新投递这个PER_IO_DATA
}
break;

case OP_WRITE:
{
}

break;
}

if (pThis->m_bExitFlag)
{
break;
}

Sleep(10);
}

return 0;
}

// 处理线程
DWORD CIUSocket::HandleThread( LPVOID lpParam )
{
CIUSocket *pThis = static_cast<CIUSocket*>(lpParam);

while (1)
{
if (pThis->m_bExitFlag)
{
break;
}

BOOL bIsValid = FALSE;
PREQ_DATA pReqListData= pThis->m_cReqList.RemoveReqData(bIsValid);

if (pReqListData != NULL)
{
pThis->OnProcessPacket(pReqListData);

delete pReqListData;
pReqListData = NULL;
}

if (pThis->m_bExitFlag)
{
break;
}

Sleep(100);
}

return 0;
}

// 启动服务
BOOL CIUSocket::StartServer(USHORT usPort)
{
BOOL bCreateSocket = FALSE;

try
{
int errorCode = 1;

// 创建完成端口对象,创建工作线程处理完成端口对象中事件
m_hCompletion = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
if (m_hCompletion == NULL)
{
throw CSrvException(_T("CreateIoCompletionPort1执行失败"), GetLastError());
}

// 开启服务线程
for (UINT i(0); i<THREAD_COUNT; i++ )
{
m_hWaitEvents[i] = ::CreateThread(NULL, 0, ServerThread, this, 0, 0);
}

// 开启处理线程
m_hHandlePacket = CreateThread(NULL, 0, HandleThread, this, 0, 0);

// 创建监听套节字,绑定到本地地址,开始监听
m_sUdp = ::WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, WSA_FLAG_OVERLAPPED );
if( m_sUdp == INVALID_SOCKET )
{
throw CSrvException(_T("WSASocket执行失败"), WSAGetLastError());
}

// 设置标志
bCreateSocket = TRUE;

//把receiveform及sendto设置为异步非阻塞模式,因为用WSAStartup初始化默认是同步
ULONG ul = 1;
errorCode = ioctlsocket(m_sUdp, FIONBIO, &ul);
if(SOCKET_ERROR == errorCode)
{
throw CSrvException(_T("ioctlsocket FIONBIO执行失败"), WSAGetLastError());
}

//设置为地址重用,优点在于服务器关闭后可以立即启用
int nOpt = 1;
errorCode = setsockopt(m_sUdp, SOL_SOCKET, SO_REUSEADDR, (char*)&nOpt, sizeof(nOpt));
if(SOCKET_ERROR == errorCode)
{
throw CSrvException(_T("setsockopt SO_REUSEADDR执行失败"), WSAGetLastError());
}

//关闭系统缓存,使用自己的缓存以防止数据的复制操作
INT nBufferLen = 0;
errorCode = setsockopt(m_sUdp, SOL_SOCKET, SO_SNDBUF, (char*)&nBufferLen, sizeof(nBufferLen));
if(SOCKET_ERROR == errorCode)
{
throw CSrvException(_T("setsockopt SO_SNDBUF执行失败"), WSAGetLastError());
}

nBufferLen = 60 * 1024;
errorCode = setsockopt(m_sUdp, SOL_SOCKET, SO_RCVBUF, (CHAR*)&nBufferLen, sizeof(nBufferLen));
if(SOCKET_ERROR == errorCode)
{
throw CSrvException(_T("setsockopt SO_RCVBUF执行失败"), WSAGetLastError());
}

unsigned long dwBytesReturned = 0;
int bNewBehavior = FALSE;

//下面的函数用于解决远端突然关闭会导致WSARecvFrom返回10054错误导致服务器完成队列中没有reeceive操作而设置
errorCode = WSAIoctl(m_sUdp, SIO_UDP_CONNRESET,&bNewBehavior, sizeof(bNewBehavior),NULL, 0, &dwBytesReturned,NULL, NULL);
if (SOCKET_ERROR == errorCode)
{
throw CSrvException(_T("WSAIoctl SIO_UDP_CONNRESET执行失败"), WSAGetLastError());
}

// 绑定端口
sockaddr_in si;
si.sin_family = AF_INET;
si.sin_port = ::ntohs(usPort);
si.sin_addr.S_un.S_addr = INADDR_ANY;

if (SOCKET_ERROR == ::bind(m_sUdp, (sockaddr*)&si, sizeof(si)))
{
throw CSrvException(_T("bind执行失败"), WSAGetLastError());
}

//把监听线程和完成端口邦定
if (NULL == ::CreateIoCompletionPort((HANDLE)m_sUdp, m_hCompletion, 0, 0))
{
throw CSrvException(_T("CreateIoCompletionPort2执行失败"), GetLastError());
}

//投递UDP_WAIT_COUNT个接收
for ( UINT i(0); i<UDP_WAIT_COUNT; i++ )
{
m_pPerIO = (PPER_IO_DATA)::GlobalAlloc(GPTR, sizeof(PER_IO_DATA));
PostRecv( m_pPerIO );
}

return TRUE;
}
catch(CSrvException& e)
{
CString strErrorTip;
strErrorTip.Format(_T("%s\t错误码为:%d"), e.GetExpDesc(), e.GetExpCode());
::MessageBox(NULL, strErrorTip, _T("启动服务异常"), MB_OK);
}
catch(...)
{
::MessageBox(NULL, _T("未知异常"), _T("启动服务异常"), MB_OK);
}

return FALSE;
}

// 停止服务
void CIUSocket::StopServer()
{
// 设置退出标志
m_bExitFlag = TRUE;

::CloseHandle(m_hCompletion);
::closesocket(m_sUdp);
m_sUdp = INVALID_SOCKET;
::PostQueuedCompletionStatus(m_hCompletion, -1, 0, NULL);

// 等待I/O处理线程退出
::WaitForMultipleObjects(THREAD_COUNT, m_hWaitEvents, TRUE, INFINITE);

// 释放内存
if (m_pPerIO != NULL)
::GlobalFree(m_pPerIO);

for(UINT i(0); i<THREAD_COUNT; i++)
{
::CloseHandle(m_hWaitEvents[i]);
m_hWaitEvents[i] = NULL;
}

if (m_hHandlePacket != NULL)
{
CloseHandle(m_hHandlePacket);
m_hHandlePacket = NULL;
}

}

// 投递接收请求
BOOL CIUSocket::PostRecv( PPER_IO_DATA pPerIO )
{
try
{
pPerIO->nOperationType = OP_READ;
pPerIO->len = MAX_BUF_SIZE;
memset( pPerIO->buf, 0, MAX_BUF_SIZE );

pPerIO->addrLen = sizeof(sockaddr);
memset( &pPerIO->addr, 0, sizeof(sockaddr) );

WSABUF buf = { MAX_BUF_SIZE, pPerIO->buf };
DWORD dwRecv = 0;
DWORD dwFlags = 0;

int nRecv = ::WSARecvFrom(
m_sUdp,
&buf,
1,
&dwRecv,
&dwFlags,
(sockaddr*)(&pPerIO->addr),
&pPerIO->addrLen,
&(pPerIO->ol),
NULL);

if( nRecv != NO_ERROR )
{
int nErr = ::WSAGetLastError();
if( nErr != WSA_IO_PENDING)
{
throw CSrvException(_T("WSARecvFrom 执行失败"), nErr);
}
}

return TRUE;
}
catch(CSrvException& e)
{
m_ComsLog.WriteLog("%s\t错误码为:%d", e.GetExpDesc(), e.GetExpCode());
}
catch(...)
{
m_ComsLog.WriteLog("PostRecv出现未知错误");
}

return FALSE;
}

// 投递发送请求
BOOL CIUSocket::PostSend(sockaddr_in addr, char* buf, int len )
{
PPER_IO_DATA pPerIO = (PPER_IO_DATA)::GlobalAlloc(GPTR, sizeof(PER_IO_DATA));

ASSERT(pPerIO != NULL);

try
{
pPerIO->nOperationType = OP_WRITE;
pPerIO->len = MAX_BUF_SIZE;
pPerIO->addrLen = sizeof(sockaddr);
memcpy( &pPerIO->addr, &addr, sizeof(sockaddr) );
memcpy( &pPerIO->buf, buf, len );
pPerIO->len = len;

WSABUF wbuf = { pPerIO->len, pPerIO->buf };
DWORD dwSend= 0, dwFlags = 0;

int nSend = ::WSASendTo(
m_sUdp,
&wbuf,
1,
&dwSend,
dwFlags,
(sockaddr*)(&pPerIO->addr),
pPerIO->addrLen,
&pPerIO->ol,
NULL);

if( nSend != NO_ERROR )
{
int nErr = ::WSAGetLastError();
if( nErr != WSA_IO_PENDING)
{
throw CSrvException(_T("WSASendTo 执行失败"), nErr);
}
}

return TRUE;
}
catch(CSrvException& e)
{
m_ComsLog.WriteLog("%s\t错误码为:%d", e.GetExpDesc(), e.GetExpCode());
}
catch(...)
{
m_ComsLog.WriteLog("PostSend出现未知错误");
}

return FALSE;
}
...全文
378 1 打赏 收藏 转发到动态 举报
写回复
用AI写文章
1 条回复
切换为时间正序
请发表友善的回复…
发表回复
zwz1984 2011-09-17
  • 打赏
  • 举报
回复
头文件:

#pragma once

#include <winsock2.h>
#pragma comment(lib, "WS2_32") // 链接到WS2_32.lib

#include "Packet.h"

#include "RequestList.h"
#include "../Log/ServerLog.h"

#define S_PORT 6000 //端口号
#define UDP_WAIT_COUNT 2497 //投递的总接收数
#define THREAD_COUNT 4 //要开启的线程数

#define OP_READ 1
#define OP_WRITE 2

#pragma pack(push)
#pragma pack(1)

// per-I/O数据
typedef struct _PER_IO_DATA
{
OVERLAPPED ol; // 重叠结构
int nOperationType; // 操作类型

sockaddr_in addr;
int addrLen;

char buf[MAX_BUF_SIZE];// 数据缓冲区
int len; // 数据长度
} PER_IO_DATA, *PPER_IO_DATA;

#pragma pack(pop)
内容概要:本文提出了一种基于非合作博弈理论的居民负荷分层调度模型,并结合双层鲸鱼优化算法(Two-level Whale Optimization Algorithm)进行高效求解,模型与算法均通过Matlab代码实现。研究针对电力系统居民侧用电负荷的复杂调度问题,引入非合作博弈机制刻画各用户之间的利益竞争关系,实现负荷的分层优化分配;同时设计双层优化架构,上层优化资源配置,下层模拟用户自主决策行为,提升了模型的实用性与合理性。通过智能优化算法求解多层级、非凸非线性的博弈模型,有效提高了调度方案的收敛性与全局寻优能力,适用于现代智能电网的需求侧管理与能源优化场景。; 适合人群:具备电力系统基础理论知识和Matlab编程能力,从事智能电网、能源优化调度、需求侧管理、博弈论应用等方向的科研人员、高校研究生及工程技术人员。; 使用场景及目标:①应用于居民区电力负荷的分层优化调度系统设计与仿真分析;②为非合作博弈在多主体能源系统建模的应用提供方法论支持;③利用双层鲸鱼算法解决具有嵌套结构的复杂双层优化问题,提升求解效率与调度方案的可行性。; 阅读建议:建议读者结合提供的Matlab代码深入理解模型构建逻辑与算法实现流程,重点关注博弈模型的效用函数设计、纳什均衡求解思路以及双层优化结构的迭代机制,宜配合实际用电数据开展复现实验以验证模型有效性与鲁棒性。
内容概要:本文围绕基于自适应神经模糊推理系统(ANFIS)智能控制器的可再生能源微电网功率管理系统展开研究,结合Simulink仿真实现,深入探讨了微电网功率的智能调控与经济机组组合调度问题。通过引入ANFIS控制器,有效应对风能、光伏等可再生能源出力的波动性与不确定性,提升系统运行的稳定性与电能质量。研究内容涵盖微电网多源协调控制策略、功率平衡管理、优化调度模型构建及仿真验证,实现了对分布式电源、储能系统和负荷的协同优化,兼顾经济性与可靠性目标,并通过仿真平台验证了所提方法的有效性与优越性。; 适合人群:具备电力系统、自动化或新能源相关专业背景,熟悉Matlab/Simulink仿真环境,从事微电网能量管理、智能控制、能源优化等领域研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①用于高比例可再生能源接入场景下的微电网能量管理系统研发与教学实践;②为实现微电网功率稳定控制与经济高效运行提供先进的智能控制解决方案;③支撑高水平学术论文复现、科研课题攻关及实际工程项目的仿真验证与方案优化。; 阅读建议:建议结合提供的Simulink模型与相关代码进行动手实践,重点关注ANFIS控制器的设计流程、规则库构建与参数调优方法,并通过与传统PID或MPC控制策略的对比实验,深入理解其在动态响应与鲁棒性方面的优势。同时可进一步拓展文提出的优化调度逻辑,应用于多目标、多约束的复杂实际应用场景
内容概要:本文档聚焦于“直流电机双闭环控制Matlab仿真”,系统阐述了基于Matlab/Simulink平台实现直流电机双闭环控制系统(主要包括速度环与电流环)的设计与仿真全过程。通过构建直流电机的数学模型,结合PI控制器进行调控,实现对电机转速和电枢电流的高精度动态控制,验证控制策略的稳定性与响应性能。文档详细介绍了仿真模型的搭建流程、关键参数的整定方法、系统动态波形的分析手段以及仿真结果的有效性验证,体现了经典自动控制理论在实际电机系统的工程应用,是电机控制与电力电子技术相结合的典型研究案例。; 适合人群:具备自动控制原理、电机与拖动基础、电力电子技术和Matlab/Simulink仿真能力的电气工程、自动化、机电一体化等专业的本科生、研究生及从事电机驱动系统研发的工程技术人员。; 使用场景及目标:①作为高校课程设计或实验教学材料,帮助学生深入理解双闭环调速系统的工作机理与工程实现;②服务于科研项目,为新型电机控制算法(如滑模、模糊PID等)的开发与性能对比提供基础仿真验证平台;③作为工业界产品前期设计的仿真工具,用于评估不同控制策略在动态响应、抗干扰能力和稳态精度方面的可行性。; 阅读建议:建议读者在学习过程紧密结合自动控制理论知识,亲手在Simulink环境搭建完整的双闭环仿真模型,通过反复调整PI控制器的比例与积分参数,观察并分析转速、电流的阶跃响应曲线,从而深刻理解反馈控制的本质、系统稳定性条件以及参数整定对动态性能的影响,进而掌握电机控制系统的设计精髓。

18,356

社区成员

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

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