浅析Delphi实现IOCP后的优化

2026/1/13 16:07:33

【转】浅析Delphi实现IOCP后的优化

在我的BLOG中有几篇文章是关于如何用DLEPHI来实现IOCP,详见我的BLOG《DELPHI中完成端口(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:array[0..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:array[0..DATA_BUFSIZE-1] of char; //数据信息,包括数据头信息 SpareBuffer:array[0..2*DATA_BUFSIZE - 1 ] of char; //处理粘包数组 SpareBufferlen:Integer; //粘包数组中剩余的数据长度 end;

这样做从理论上来说IOCP的速度会提高不少。但是由于我们指定了粘包处理的数组大小,这样就会出现——当我们发送过来的数据结构的长度大于粘包数数组长度的时,粘包处理就会出现问题。这个时候我觉得处理方法有两个:

1:加大粘包数组长度,这个数组的长度设置成你的所有数据结构中最大者长度的2倍。 2:使用链表来进行粘包处理,我们可以将粘包设置成一个链表,这样就避免了粘包数组长度的限制,我们可以发送一个很大的数据结构。但是这样的设置又会带来新的问题,

即每次需要申请新的内存。不过这也算是一种方法,适合于数据包大小变化很大的情况。具体结构为:

PSpareBuffer = ^TSpareBuffer; TSpareBuffer = record Postion: Integer;

SpareBuffer:array[0..DATA_BUFSIZE-1] of char; Next: PSpareBuffer; end;

//IO结构

PIOData = ^TIOData; TIOData = record

Overlapped: OVERLAPPED; DataBuf: TWSABUF;

Socket: TSocket; //套接字 OperationType: TOperation; //操作类型 BufferLen:Integer; //数据长度

Buffer:array[0..DATA_BUFSIZE-1] of char; //数据信息,包括数据头信息 FirstBuffer: PSpareBuffer; //粘包处理链表的第一个指针

LastBuffer: PSpareBuffer; //粘包处理链表的最后一个指针 end;

具体的实现方法,我这里就不讲述了,大家有时间可以自行实现。

还有可以提高效率的地方吗?我们来看看以前对于粘包的处理,以前我在对粘包的处理部分使用的是将一个代表数据包长度的Integer类型,转换成一个4位的char并加入到数据包的头部,然后根据这个包头长度来处理,所以代码中就出现了不少这样的代码:PacketHeader:=StrToInt(StrPas(Temp));这个可以优化吗?当然可以,将我们发送的数据格式修改成这样: TNetPacketed = record DataLen:Integer; end;

PNetPacketed = ^TNetPacketed;

procedure TIOCPServer.DataProcess(PerIoData: PRecvIOData); var

Offset:Word;

pPacketed: PNetPacketed; Data: PChar; begin Offset:=0;

while PerIoData.SpareBufferLen - Offset >= SizeOf(TNetPacketed) do begin

pPacketed:=PNetPacketed(@PerIoData.SpareBuffer[Offset]);

if (PerIoData.SpareBufferLen - Offset) >= (pPacketed.DataLen) then begin

GetMem(Data,pPacketed.DataLen - SizeOf(TNetPacketed));

StrMove(Data,@PerIoData.SpareBuffer[Offset+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 (Offset>0)then begin

Dec(PerIoData.SpareBufferLen,Offset);

Move(PerIoData.SpareBuffer[Offset],PerIoData.SpareBuffer,PerIoData.SpareBufferLen); end; end;

本来在这里想写一些比较详细的文字来说明粘包的处理过程,可是后来觉得使用代码应该更能说明问题。所以就将我现用的代码贴了出来,我想大家看到代码就应该明白我的意思了呵呵。


浅析Delphi实现IOCP后的优化.doc 将本文的Word文档下载到电脑
搜索更多关于: 浅析Delphi实现IOCP后的优化 的文档
相关推荐
相关阅读
× 游客快捷下载通道(下载后可以自由复制和排版)

下载本文档需要支付 10

支付方式:

开通VIP包月会员 特价:29元/月

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信:xuecool-com QQ:370150219