经过对一段时间对IOCP的学习和研究。对IOCP的流程有了一定的了解。
后面对一段时间的IOCP优化和实际项目的使用,在DIOCP中加入了日志文件记录功能,利用api编写了一个客户端通信类,等解决了很多bug,并已使用在公司的项目中,现运行稳定。今天对IOCP退出时资源的释放,做了最后一步研究和优化。
为了方便大家学习,我把这个项目命名为DIOCP,上传在google的SVN项目中
https://code.google.com/p/diocp/
大家可以通过svn下载代码,里面我写了一个Echo的测试Demo,准备后续加入聊天等Demo。
特地为DIOCP开设了一个群:320641073,欢迎学习的IOCP的童鞋进入讨论。
--IOCP退出资源的管控研究
现在在项目中使用的IOCP退出的过程是。
procedure TIOCPConsole.close; begin //关闭服务端端口 FIOCPObject.closeSSocket; //停止监听 stopListener; //断开所有连接 FIOCPObject.DisconnectAllClientContext; //等待资源的回归 FIOCPObject.WaiteForResGiveBack; //停止工作线程 stopWorkers; //标志状态 FActive := false; end;
对IO内存池也进行了一些优化,主要加入了资源回归的监控
unit uMemPool; interface uses JwaWinsock2, Windows, SyncObjs, uIOCPProtocol; type TIODataMemPool = class(TObject) private //全部归还的信号灯 FCanCloseSingle: THandle; FCs: TCriticalSection; //第一个可用的内存块 FHead: POVERLAPPEDEx; //最后一个可用的内存卡 FTail: POVERLAPPEDEx; //可用的内存个数 FUseableCount:Integer; //正在使用的个数 FUsingCount:Integer; /// <summary> /// 将一个内存块添加到尾部 /// </summary> /// <param name="pvIOData"> (POVERLAPPEDEx) </param> procedure AddData2Pool(pvIOData:POVERLAPPEDEx); /// <summary> /// 得到一块可以使用的内存 /// </summary> /// <returns> POVERLAPPEDEx /// </returns> function getUsableData: POVERLAPPEDEx; /// <summary> /// 创建一块内存空间 /// </summary> /// <returns> POVERLAPPEDEx /// </returns> function InnerCreateIOData: POVERLAPPEDEx; procedure clearMemBlock(pvIOData:POVERLAPPEDEx); //释放所有的内存块 procedure FreeAllBlock; public class function instance: TIODataMemPool; constructor Create; destructor Destroy; override; function waiteForGiveBack: Boolean; //借一块内存 function borrowIOData: POVERLAPPEDEx; //换会一块内存 procedure giveBackIOData(const pvIOData: POVERLAPPEDEx); function getCount: Cardinal; function getUseableCount: Cardinal; function getUsingCount:Cardinal; end; implementation uses uIOCPFileLogger; var __IODATA_instance:TIODataMemPool; constructor TIODataMemPool.Create; begin inherited Create; FCs := TCriticalSection.Create(); FUseableCount := 0; FUsingCount := 0; FCanCloseSingle := CreateEvent(nil, True, True, nil); end; destructor TIODataMemPool.Destroy; begin CloseHandle(FCanCloseSingle); FreeAllBlock; FCs.Free; inherited Destroy; end; { TIODataMemPool } procedure TIODataMemPool.AddData2Pool(pvIOData:POVERLAPPEDEx); begin if FHead = nil then begin FHead := pvIOData; FHead.next := nil; FHead.pre := nil; FTail := pvIOData; end else begin FTail.next := pvIOData; pvIOData.pre := FTail; FTail := pvIOData; end; Inc(FUseableCount); end; function TIODataMemPool.InnerCreateIOData: POVERLAPPEDEx; begin Result := POVERLAPPEDEx(GlobalAlloc(GPTR, sizeof(OVERLAPPEDEx))); GetMem(Result.DataBuf.buf, MAX_OVERLAPPEDEx_BUFFER_SIZE); Result.DataBuf.len := MAX_OVERLAPPEDEx_BUFFER_SIZE; //清理一块内存 clearMemBlock(Result); end; function TIODataMemPool.borrowIOData: POVERLAPPEDEx; begin FCs.Enter; try Result := getUsableData; if Result = nil then begin //生产一个内存块 Result := InnerCreateIOData; //直接借走<增加使用计数器> Inc(FUsingCount); end; //变成没有信号 if FUsingCount > 0 then ResetEvent(FCanCloseSingle); finally FCs.Leave; end; end; procedure TIODataMemPool.clearMemBlock(pvIOData: POVERLAPPEDEx); begin //清理一块内存 pvIOData.IO_TYPE := 0; pvIOData.WorkBytes := 0; pvIOData.WorkFlag := 0; //ZeroMemory(@pvIOData.Overlapped, sizeof(OVERLAPPED)); //还原大小<分配时的大小> pvIOData.DataBuf.len := MAX_OVERLAPPEDEx_BUFFER_SIZE; //ZeroMemory(pvIOData.DataBuf.buf, pvIOData.DataBuf.len); end; procedure TIODataMemPool.FreeAllBlock; var lvNext, lvData:POVERLAPPEDEx; begin lvData := FHead; while lvData <> nil do begin //记录下一个 lvNext := lvData.next; //释放当前Data FreeMem(lvData.DataBuf.buf, lvData.DataBuf.len); GlobalFree(Cardinal(lvData)); //准备释放下一个 lvData := lvNext; end; FHead := nil; FTail := nil; FUsingCount := 0; FUseableCount := 0; end; function TIODataMemPool.getCount: Cardinal; begin Result := FUseableCount + FUsingCount; end; procedure TIODataMemPool.giveBackIOData(const pvIOData: POVERLAPPEDEx); begin FCs.Enter; try if (pvIOData.pre <> nil) or (pvIOData.next <> nil) or (pvIOData = FHead) then begin TIOCPFileLogger.logErrMessage('回收内存块是出现了异常,该内存块已经回收!'); end else begin //清理内存块 clearMemBlock(pvIOData); //加入到可以使用的内存空间 AddData2Pool(pvIOData); //减少使用计数器 Dec(FUsingCount); end; //有信号 if FUsingCount = 0 then SetEvent(FCanCloseSingle); finally FCs.Leave; end; end; function TIODataMemPool.getUsableData: POVERLAPPEDEx; var lvPre:POVERLAPPEDEx; begin if FTail = nil then begin Result := nil; end else begin Result := FTail; lvPre := FTail.pre; if lvPre <> nil then begin lvPre.next := nil; FTail := lvPre; end else //FTail是第一个也是最后一个,只有一个 begin FHead := nil; FTail := nil; end; Result.next := nil; Result.pre := nil; Dec(FUseableCount); Inc(FUsingCount); end; end; function TIODataMemPool.getUseableCount: Cardinal; begin Result := FUseableCount; end; function TIODataMemPool.getUsingCount: Cardinal; begin Result := FUsingCount; end; class function TIODataMemPool.instance: TIODataMemPool; begin Result := __IODATA_instance; end; function TIODataMemPool.waiteForGiveBack: Boolean; var lvRet:DWORD; begin Result := false; lvRet := WaitForSingleObject(FCanCloseSingle, INFINITE); if lvRet = WAIT_OBJECT_0 then begin Result := true; end; end; initialization __IODATA_instance := TIODataMemPool.Create; finalization if __IODATA_instance <> nil then begin __IODATA_instance.Free; __IODATA_instance := nil; end; end.