前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >DELPHI中完成端口(IOCP)的简单

DELPHI中完成端口(IOCP)的简单

作者头像
py3study
发布2020-01-06 18:09:33
发布2020-01-06 18:09:33
1.2K0
举报
文章被收录于专栏:python3python3

最近太忙,所以没有机会来写IOCP的后续文章。今天好不容易有了时间来写IOCP的粘包处理问题。

TCP数据粘包的产生原因在于TCP是一种流协议。在以太网中一个TCP的数据包长度是1500位。其中20位的IP包头,20位的TCP包头,其余的1460都是我们可以发送的数据。在数据发送的时候,我们发送的数据长度有可能比1460短,这样在TCP来说它还是以一个数据包来发送。从而降低了网络的利用率。所以TCP在发送数据包的时候,会将下一个数据包和这个数据包合在一起发送以增加网络利用率(虽然SOCKET 中可以强制关闭这种合并发送,但是我不建议使用)。这样以来,在我们接受到一个数据包以后,就会发现在这个数据包中含有其它的数据包,从而很难处理。

处理粘包现象有多种方法。我的方法是在每发送一个数据的前面加入这次发送的数据长度(4位)。以char的方式加入。这样以来我们的数据包结构就变成了:

数据包长度(4位)+实际数据。

在接收到数据包以后,我们首先得到数据包的长度,然后根据这个数据包长度来得到实际的数据。

以下是我的粘包处理函数实现(这个函数是对于多个套接字来处理的所以在这里我使用了TList链表):

//用于处理粘包的数据结构

tagPacket = record

Socket:TSocket; //处理粘包的套接字

hThread:THANDLE; //线程句柄

ThreadID:DWORD; //线程ID

DataBuf:array[0..DATA_BUFSIZE-1] of char; //处理粘包的包

DataLen:Integer; //处理粘包的包长度

end;

TDealPacket = tagPacket;

PDealPacket = ^tagPacket;

{粘包处理函数}

function TClientNet.ComminutePacket(SorucePacket:array of char;SPLen:Integer;var Destpacket:array of char;

var DPLen:Integer;var SparePacket:array of char;

var SpareLen:Integer;var IsEnd:Boolean;socket:Tsocket):Boolean;

const

MaxPacket = 1024;

PacketLength = 4;

var

Temp:pchar;

TempLen,PacketHeader:Integer;

I,J:Integer;

TempArray:array[0..MaxPacket-1] of char;

TempCurr:Integer;

CurrListI:Integer;

SocketData:PDealPacket;

  t_Ord:Integer;

begin

Result:=true;

try

//首先根据套接字来得到上次遗留的数据 Fillchar(TempArray,sizeof(TempArray),#0);

for I:=0 to DealDataList.Count-1 do

begin

SocketData:=DealDataList.Items[I];

if SocketData.Socket = socket then

begin

strmove(TempArray,SocketData.DataBuf,sizeof(SocketData.DataBuf));

TempCurr:=SocketData.DataLen;

CurrListI:=I;

break;

end;

end;

//我们将每次处理粘包以后剩余的数据保存在一个TDealPacket的链表中DealDataList。每次根据套接字先得到上次是否有剩余的数据。如果有则将这个数据拷贝到一个临时处理的缓存中。

FillChar(Destpacket,sizeof(Destpacket),#0);

FillChar(SparePacket,sizeof(SparePacket),#0);

IsEnd:=false;

{以下就是对数据包的整合,其算法很简单,读者可以参考我的注释来理解}

//对临时缓存进行检测

if TempCurr<>0 then //缓存中存在数据

begin

if TempCurr<PacketLength then //缓存中包含的数据包长度不足一个4位的数据包长度。

begin

TempLen:=PacketLength-TempCurr;

if TempLen>SPLen then //数据包中含有的数量不足包头数量

begin

strmove(TempArray+TempCurr,SorucePacket,SPLen);

TempCurr:=TempCurr+SPLen;

//分解完毕,

IsEnd:=true;

end

else

begin

strmove(TempArray+TempCurr,SorucePacket,TempLen);

TempCurr:=TempCurr+TempLen;

GetMem(Temp,PacketLength+1);

Fillchar(Temp^,PacketLength+1,#0);

strmove(Temp,TempArray,PacketLength);

          //最近在检查代码的时候发现这里转换包头长度的时候,只是使用异常来判断是不合适的。所以这里进行了修改 (2008年3月24日)

          {try

PacketHeader:=StrToInt(StrPas(Temp));

except

Result:=false;

exit;

end;

          }

          for J := 1 to 4 do           begin             t_Ord:=Ord(StrPas(Temp)[J]);             if (t_Ord<48) or (t_Ord>57) then             begin               Result := false;               IsEnd := true;               Exit;             end;           end;

if PacketHeader>SPLen-TempLen then //此包是不全包

begin

strmove(TempArray+TempCurr,SorucePacket+TempLen,SPLen-TempLen);

TempCurr:=TempCurr+SPLen-TempLen;

//已经将数据拷贝完成

IsEnd:=true;

end

else //此包是过包

begin

strmove(TempArray+TempCurr,SorucePacket+TempLen,PacketHeader);

strmove(Destpacket,TempArray,PacketHeader+PacketLength);

DPLen:=PacketHeader+PacketLength;

Strmove(SparePacket,SorucePacket+TempLen+PacketHeader,SPLen-(TempLen+PacketHeader));

SpareLen:=SPLen-(TempLen+PacketHeader);

FillChar(TempArray,sizeof(TempArray),#0);

TempCurr:=0;

IsEnd:=false;

end;

FreeMem(Temp);

end;

end

else //缓存中已经含有数据头

begin

GetMem(Temp,PacketLength+1);

Fillchar(Temp^,PacketLength+1,#0);

strmove(Temp,TempArray,PacketLength);

        //最近在检查代码的时候发现这里转换包头长度的时候,只是使用异常来判断是不合适的。所以这里进行了修改 (2008年3月24日)

        {try

PacketHeader:=StrToInt(StrPas(Temp));

except

Result:=false;

exit;

end;

        }

        for J := 1 to 4 do           begin             t_Ord:=Ord(StrPas(Temp)[J]);             if (t_Ord<48) or (t_Ord>57) then             begin               Result := false;               IsEnd := true;               Exit;             end;           end;

if PacketHeader>TempCurr-PacketLength then //数据包包头

begin

TempLen:=(PacketHeader+PacketLength)-TempCurr;

if TempLen>SPLen then

begin

strmove(TempArray+TempCurr,SorucePacket,SPLen);

TempCurr:=TempCurr+SPLen;

IsEnd:=true;

end

else

begin

strmove(TempArray+TempCurr,SorucePacket,TempLen);

strmove(Destpacket,TempArray,PacketHeader+PacketLength);

DPLen:=PacketHeader+PacketLength;

Strmove(SparePacket,SorucePacket+TempLen,SPLen-TempLen);

SpareLen:=SPLen-TempLen;

TempCurr:=0;

FillChar(TempArray,sizeof(TempArray),#0);

IsEnd:=false;

end;

end

else

begin

strmove(TempArray+TempCurr,SorucePacket,TempLen+PacketLength);

strmove(Destpacket,TempArray,TempCurr+TempLen+PacketLength);

DPLen:=TempCurr+TempLen+PacketLength;

Strmove(SparePacket,SorucePacket+TempLen+PacketLength,SPLen-TempLen);

SpareLen:=SPLen-TempLen-PacketLength;

TempCurr:=0;

FillChar(TempArray,sizeof(TempArray),#0);

IsEnd:=false;

end;

FreeMem(Temp);

end;

end

else //缓存中不存在数据

begin

Fillchar(TempArray,sizeof(TempArray),#0);

if SPLen>=PacketLength then

begin

strmove(TempArray,SorucePacket,PacketLength);

GetMem(Temp,PacketLength+1);

Fillchar(Temp^,PacketLength+1,#0);

strmove(Temp,TempArray,PacketLength);

        //最近在检查代码的时候发现这里转换包头长度的时候,只是使用异常来判断是不合适的。所以这里进行了修改 (2008年3月24日)

        {try

PacketHeader:=StrToInt(StrPas(Temp));

except

Result:=false;

exit;

end;}

        for J := 1 to 4 do           begin             t_Ord:=Ord(StrPas(Temp)[J]);             if (t_Ord<48) or (t_Ord>57) then             begin               Result := false;               IsEnd := true;               Exit;             end;           end;

if PacketHeader>SPLen-PacketLength then

begin

strmove(TempArray+PacketLength,SorucePacket+PacketLength,SPLen-PacketLength);

TempCurr:=SPLen;

IsEnd:=true;

end

else

begin

strmove(TempArray+PacketLength,SorucePacket+PacketLength,PacketHeader);

strmove(Destpacket,TempArray,PacketHeader+PacketLength);

DPLen:=PacketHeader+PacketLength;

Strmove(SparePacket,SorucePacket+PacketHeader+PacketLength,SPLen-(PacketHeader+PacketLength));

SpareLen:=SPLen-(PacketHeader+PacketLength);

TempCurr:=0;

FillChar(TempArray,sizeof(TempArray),#0);

IsEnd:=false;

end;

FreeMem(Temp);

end

else

begin

strmove(TempArray,SorucePacket,SPLen);

TempCurr:=SPLen;

IsEnd:=true;

end;

end;

//恢复数据

SocketData.DataLen:=TempCurr;

Fillchar(SocketData.DataBuf,sizeof(SocketData.DataBuf),#0);

strmove(SocketData.DataBuf,TempArray,TempCurr);

except

Result:=false;

end;

end;

上面的函数就是对TCP协议中粘包的处理DLEPHI代码,对于UDP数据来说是不存在粘包现象的。

我写的IOCP的代码已经在我编写的网络游戏中使用,运行稳定。

下次我会讲使用IOCP发送数据的方法。

同时祝大家新年快乐!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019/09/22 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档