浅析Delphi实现IOCP后的优化.doc_第1页
浅析Delphi实现IOCP后的优化.doc_第2页
浅析Delphi实现IOCP后的优化.doc_第3页
浅析Delphi实现IOCP后的优化.doc_第4页
浅析Delphi实现IOCP后的优化.doc_第5页
已阅读5页,还剩5页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

【转】浅析Delphi实现IOCP后的优化在我的BLOG中有几篇文章是关于如何用DLEPHI来实现IOCP,详见我的BLOGDELPHI中完成端口(IOCP)的简单分析。在这几篇文章中介绍了如何编写一个简单的IOCP的方法。最近我重新对这些文章中的一些BUG和效率低下的部分做了修正(其实相当于重新编写),通过几个不同的途径对IOCP进行了实现。下面我就来说一下我对以前代码的优化方法。1:结构定义部分。首先我们必须定义一个IO数据结构,在我的BLOG中我当时是这样定义的。(1):单IO数据结构LPVOID = Pointer;LPPER_IO_OPERATION_DATA = PER_IO_OPERATION_DATA ;PER_IO_OPERATION_DATA = packed record Overlapped: OVERLAPPED; DataBuf: TWSABUF; Buffer: array 0.1024 of CHAR; BytesSEND: DWORD; BytesRECV: DWORD;end;和一个(2):“单句柄数据结构”LPPER_HANDLE_DATA = PER_HANDLE_DATA;PER_HANDLE_DATA = packed record Socket: TSocket;end;其实为什么我们不能将他们进行合并定义成一个结构呢?/IO结构PIOData = TIOData;TIOData = record Overlapped: OVERLAPPED; DataBuf: TWSABUF; Socket:TSocket; /套接字 OperationType:TOperation; /操作类型 BufferLen:Integer; /数据长度 Buffer:array0.DATA_BUFSIZE-1 of char;/数据信息,包括数据头信息end;这种结构当我们调用GetQueuedCompletionStatus函数的时候,用该函数的第4个参数来返回这个结构,这样一来我们就不用定义2个结构来处理不知道大家是否还记得在我的BLOG中关于粘包的文章(我们暂且不说它是否应该叫这个名字)。关于粘包的造成原理我这里就不讲述了,如有需要可以参看我的BLOG。这里只是说明一下,粘包的处理是我们将通过IOCP得到的数据,和这个套接字上次处理并剩余的数据合并在一起,看新合并后的数据包中是否包含一个完整的数据结构,如果包含则进行相关处理,并将处理后的剩余数据进行再次判断,反复如此。一般我们会将这个合并的数据放在一个TList链表中,有的时候为了加快它的查找速度,我们会将它放在一个HASH表中,以套接字做为KEY。自然放在HASH表中的速度要比放在单纯的链表中快一些。可是我们有没有想过直接放在上面的这个IO结构中呢?也就是说将粘包处理的数组放在IO结构中,这样当GetQueuedCompletionStatus返回的时候就会直接将数据进行粘包处理,又可以免去一次的数据查找过程。这样一来,上面的数据结构就变成了:/IO结构PIOData = TIOData;TIOData = record Overlapped: OVERLAPPED; DataBuf: TWSABUF; Socket:TSocket; /套接字 OperationType:TOperation; /操作类型 BufferLen:Integer; /数据长度 Buffer:array0.DATA_BUFSIZE-1 of char;/数据信息,包括数据头信息 SpareBuffer:array0.2*DATA_BUFSIZE - 1 of char;/处理粘包数组 SpareBufferlen:Integer; /粘包数组中剩余的数据长度end;这样做从理论上来说IOCP的速度会提高不少。但是由于我们指定了粘包处理的数组大小,这样就会出现当我们发送过来的数据结构的长度大于粘包数数组长度的时,粘包处理就会出现问题。这个时候我觉得处理方法有两个:1:加大粘包数组长度,这个数组的长度设置成你的所有数据结构中最大者长度的2倍。2:使用链表来进行粘包处理,我们可以将粘包设置成一个链表,这样就避免了粘包数组长度的限制,我们可以发送一个很大的数据结构。但是这样的设置又会带来新的问题,即每次需要申请新的内存。不过这也算是一种方法,适合于数据包大小变化很大的情况。具体结构为:PSpareBuffer = TSpareBuffer;TSpareBuffer = record Postion: Integer; SpareBuffer:array0.DATA_BUFSIZE-1 of char; Next: PSpareBuffer;end;/IO结构PIOData = TIOData;TIOData = record Overlapped: OVERLAPPED; DataBuf: TWSABUF; Socket: TSocket; /套接字 OperationType: TOperation; /操作类型 BufferLen:Integer; /数据长度 Buffer:array0.DATA_BUFSIZE-1 of char;/数据信息,包括数据头信息 FirstBuffer: PSpareBuffer; /粘包处理链表的第一个指针 LastBuffer: PSpareBuffer; /粘包处理链表的最后一个指针end;具体的实现方法,我这里就不讲述了,大家有时间可以自行实现。还有可以提高效率的地方吗?我们来看看以前对于粘包的处理,以前我在对粘包的处理部分使用的是将一个代表数据包长度的Integer类型,转换成一个4位的char并加入到数据包的头部,然后根据这个包头长度来处理,所以代码中就出现了不少这样的代码:PacketHeader:=StrToInt(StrPas(Temp);这个可以优化吗?当然可以,将我们发送的数据格式修改成这样:TNetPacketed = recordDataLen:Integer;end;PNetPacketed = TNetPacketed;procedure TIOCPServer.DataProcess(PerIoData: PRecvIOData);varOffset:Word;pPacketed: PNetPacketed;Data: PChar;beginOffset:=0;while PerIoData.SpareBufferLen- Offset = SizeOf(TNetPacketed) dobegin pPacketed:=PNetPacketed(PerIoData.SpareBufferOffset); if (PerIoData.SpareBufferLen - Offset) = (pPacketed.DataLen) then begin GetMem(Data,pPacketed.DataLen - SizeOf(TNetPacketed); StrMove(Data,PerIoData.SpareBufferOffset+SizeOf(TNetPacketed),pPacketed.DataLen - SizeOf(TNetPacketed); if Assigned(OnRecive) then begin OnRecive(Data,pPacketed.DataLen - SizeOf(TNetPacketed),PerIoData.Socket); end; FreeMem(Data); Inc(Offset,pPacketed.DataLen); end else begin Break; end;end;if (Offset0)thenbegin Dec(PerIoData.SpareBufferLen,Offset); Move(PerIoData.SpareBufferOffset,PerIoData.SpareBuffer,PerIoData.SpareBufferLen);end;end;本来在这里想写一些比较详细的文字来说明粘包的处理过程,可是后来觉得使用代码应该更能说明问题。所以就将我现用的代码贴了出来,我想大家看到代码就应该明白我的意思了呵呵。2:使用内存池。使用内存池来提高IOCP的效率,这几乎是大家的共识。可是如何使用内存池呢?有人喜欢使用环形内存池,有人喜欢用链表内存池,也有人直接使用FASTMM来做内存池的。我觉得这些方法都可以达到一定的目的,我使用内存池的类是这样的:/发送内存池管理类TMemPoolControl = classprivate Private declarations FMemFirst: PIOData; FMemCS: TRTLCriticalSection; procedure CreateMem; procedure AddMem(p_IOData: PIOData);public Public declarations FMemCount:Integer; constructor Create; destructor Destroy; override; /申请一个发送空间 procedure AllocateBuffer(var p_IOData: PIOData); procedure ReleaseBuffer(p_IOData: PIOData);end; TMemPoolControl procedure TMemPoolControl.AddMem(p_IOData:PIOData);varp_MoveIOData,p_OldIOData: PIOData;beginif FMemCount MAXPOOLNUMS thenbegin /内存池数据太多,直接释放 HeapFree(GetProcessHeap, 0, p_IOData); Exit;end;/初始化此内存块FillChar(p_IOData.Buffer,SizeOf(p_IOData.Buffer),#0);p_IOData.BufferLen:=0;p_IOData.Next:=nil;p_OldIOData:=nil;p_MoveIOData:=FMemFirst;if not Assigned(FMemFirst) thenbegin FMemFirst:=p_IOData;endelsebegin /循环查找最后一个内存指针 while Assigned(p_MoveIOData) do begin p_OldIOData:=p_MoveIOData; p_MoveIOData:=p_MoveIOData.Next; end; p_OldIOData.Next:=p_IOData;end;Inc(FMemCount);end;procedure TMemPoolControl.AllocateBuffer(var p_IOData: PIOData);beginEnterCriticalSection(FMemCS);try if Assigned(FMemFirst) then begin p_IOData:=FMemFirst; FMemFirst:=FMemFirst.Next; p_IOData.Next:=nil; Dec(FMemCount); end else begin CreateMem; p_IOData:=FMemFirst; FMemFirst:=FMemFirst.Next; p_IOData.Next:=nil; Dec(FMemCount); end;finally LeaveCriticalSection(FMemCS);end;end;constructor TMemPoolControl.Create;beginFMemFirst:=nil;FMemCount:=0;InitializeCriticalSection(FMemCS);end;procedure TMemPoolControl.CreateMem;varI:Integer;Buf: PIOData;beginfor I:=1 to (MAXPOOLNUMS - FMemCount) dobegin Buf:=PIOData(HeapAlloc(GetProcessHeap,HEAP_ZERO_MEMORY,sizeof(TIOData); Buf.Next:=nil; if Assigned(Buf) then begin AddMem(Buf); end;end;end;destructor TMemPoolControl.Destroy;varp_IOData: PIOData;beginEnterCriticalSection(FMemCS);try /清空接收缓冲池 while Assigned(FMemFirst) do begin p_IOData:=FMemFirst; FMemFirst:=FMemFirst.Next; p_IOData.Next:=nil; HeapFree(GetProcessHeap, 0, p_IOData); end; FMemCount:=0; FMemFirst:=nil;finally LeaveCriticalSection(FMemCS); DeleteCriticalSection(FMemCS);end;inherited;end;procedure TMemPoolControl.ReleaseBuffer(p_IOData: PIOData);beginEnterCriticalSection(FMemCS);try if Assigned(p_IOData) then begin p_IOData.BufferLen:=0; FillChar(p_IOData.Buffer,SizeOf(p_IOData.Buffer),#0); p_IOData.Next:=nil; AddMem(p_IOData); end;finally LeaveCriticalSection(FMemCS);end;end;这个代码中有一个比较慢的地方,我只定义了这个内存池的头指针,而没有定义尾指针。所以在加入一个新内存的时候就要从头查找一遍,降低了效率。大家可以在这里定义一个尾指针用于加快插入速度。3:连接池。连接池的时候主要是使用ACCEPTEX函数来代替WSAAccept函数。这个函数最大的好处是可以实现创建出多个套接字。但是在我实际使用中却发现,它有几个不好的地方(1):控制麻烦:我相信使用过ACCEPTEX的朋友应该会同意我的观点。较之WSAAccept函数来说,ACCEPTEX函数使用起来繁琐很多。首先要将此函数引入,然后预先创建多个套接字并将这些套节字都投递accept请求,并将这些套接字放在一个链表中。投递请求后,设置2个事件放在事件数组中,这时创建工作者线程,并将工作者线程的句柄保存在事件数组中,然后使用WSAWaitForMultipleEvents函数WSAWaitForMultipleEvents( FNetServer.EventSerial + 1, FNetServer.Eventarray0, FALSE, 1000, False );来等待相应的事件出发,对于超时事件我们需要对链表中的套接字进行检测是否超时,对于.说着我就头大。实现的代码,和我写的其它版本的IOCP对比了一下,复杂程度和可控制程度麻烦了许多,代码越多出错几率就会越大,所以我不建议大家使用ACCEPTEX。(2):连接判断:对于使用ACCEPTEX函数最大的问题,我觉得是它无法做到对于连接请求是否允许连接进行的判断。我们知道在WS

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论