我在TServerSocket与TClientSocket的OnRead事件中发生的怪事!!!!

晓衡的成长日记 2008-08-07 10:24:42
现象:
发送文件,发送端循环读取一片组装成我自己定义的数据包(有包头,长度,压缩数据 等信息)发送出去。
接收端在OnRead事件中读取。
1.读取到大于一个我自己的数据包就去拆分数据包(可能接收到多个数据包粘连在一起的情况),可能是>=1个
2.拆分出来m个数据包后循环解压数据。
3.将解压出的真实数据传入一个外部回调函数,
4.外部回调函数将数据写到文件里。

-----
问题出现:
发送端,我在循环中每个数据包里加了个序号:
接收时,将序号显示出来时,是乱序的。比如:
------------------------
序号 本次数据包个数
1 4
2 4
5 1
6 1
3 4
4 4
------------------
数据包顺序不正确了。感觉是在接收到数据后,拆分、解压、响应回调相对比较慢,在此时又响应了一次OnRead事件,而在第一次OnRead事件中,收到了多个,还没来的急全部写到文件中,第二次只收到1个数据包,就写到文件中了。导致数据顺序不对。
我读了一下delphi的源码,使用的是WSAAsyncSelect(FSocket, Wnd, Msg, Longint(Byte(FAsyncStyles)))模式,也就是从Wnd窗口消息中获得有数据到来,而消息是异步的,相当于postmessage发出来的一样。
我之前的数据没处理完成,第二次消息又来了,第二次消息中收到的数据比第一次的少,先完成,导致数据顺序不正确。
不知道我的分析对不对?目前不知道如何解决这个问题,请大家多多帮助,指点,在此非常感谢!
...全文
206 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复
我的问题搞定了! 在调试过程中,我发现只要一进入回调函数就跳到FD_READ的消息处,百思不得其解。一但把回调函数出了就对了。在无意的跟踪过程中,我跟进了FOnReceviceCommand回调函数中, 里面有一句为了使窗口不卡住,写了一句Application.pocessmessage,一执行这句又跳到FD_READ消息。
这下问题找到了....
Application.pocessmessage中是执行的peekmessage,TranslateMessage,DispatchMessage 将消息队列中的消息又分派出来,导致响应网络接收消息。。使用数据顺序不正确!!
  • 打赏
  • 举报
回复
我把代码贴出来,大家帮我看看

TADLTcpClient是继承TClientSocket的
---------------------------------------
procedure TADLTcpClient.Read(Sender: TObject; Socket: TCustomWinSocket); //响应OnRead事件
begin
ReceviceCommand(Socket);
end;
----------------------------------------
procedure TADLTcpClient.ReceviceCommand(Socket: TCustomWinSocket);
var
NetCommand: TADLNetCommand;
RecLen: Integer;
i, PackageCount: Integer;
pData: Pointer;
NetCommandList:TList;
begin

NetCommand := nil;

//FNetInOutPackage类是用来专门接收、发送、加压、解压数据的,传了socket进去,服务器端和客户端都用这个类。
RecLen := FNetInOutPackage.ReciveNetPackage(Socket);

if RecLen <= 0 then
Exit;
//定义了一个指针,用来保存半截包
pData := nil;
//折分数据包,得到包的个数,如果有半包,返回pData不为nil
PackageCount := FNetInOutPackage.GetNetPackageCount(pData);
//将半截包,保存到Socket.Data 指针中,
Socket.Data := pData;

//NetCommandList 用来保存解压还原后的数据数据,每一个item是一个类(我发送过来的数据会还原成一个类)
NetCommandList := TList.Create;
//循环将PackageCount个包,解压,还原
for i := 0 to PackageCount - 1 do
begin
NetCommand := FNetInOutPackage.OutOfPackage(i); // 这里解压、还原成一个类对象
//还原出错,或解压出错为nil,
if NetCommand = nil then
begin
Socket.Close;
NetCommandList.Free;
Exit;
end;
NetCommandList.Add(NetCommand);//将返回的对象指针放到list, 还原完了再同时响应外部回调函数
end;

//这里清空FNetInOutPackage内部的拆分数据包链表。上面的循环就是循环的这个链表中的数据
FNetInOutPackage.ClearPackageList;

//下面响应回调函数
if Assigned(FOnReceviceCommand) then
begin
for i := 0 to NetCommandList.Count - 1 do
begin
NetCommand := TADLNetCommand(NetCommandList.Items[i]);
NetCommand.Tag := NetCommandList.Count;
//if NetCommandList.Count > 1 then
// MessageBox(Application.Handle, pchar(inttostr(Integer(@Socket))),'',MB_OK);
FOnReceviceCommand(Socket, FNetInOutPackage, NetCommand);
end;
end;
NetCommandList.Free;
end;
  • 打赏
  • 举报
回复
半个包的情况我也做了处理的,我会把半包保存到一个地方,下次onread的时候,先将上次的半包数据放到接收缓冲区中,再继续接收。
我现在很多程序中都用到了这个类, 并且不是服务器才有这个问题,只要发送方,循环发送,接收端有就有可能粘包,一粘包解析就就慢了,慢了就会再次接收到onread事件,顺序就乱了。
手指风 2008-08-08
  • 打赏
  • 举报
回复
既然有粘包,那应该会有拆成半个的包吧,这个你有没有先缓存再重新组包?TCP是顺序发送的,要不你不要用
TClientSocket和TServerSocket组件来写,自己直接用api来写,如果数据包快的话用iocp试试.
数据收到先缓存一些数据,再一次性写.
  • 打赏
  • 举报
回复
我写的这个网络通信本来不是用来发文件的,只是用来发送一些自己定义的数据结构或一些小数据,在之前的使用过程中,发送端连续发送多个数据包接收端会出现粘包现象,导致解析不正确。 现在粘包解决了,但包被解析出来的顺序又不一样了。
在我定义的数据包结构中是有校验的,我敢肯定我接收到的每一个数据包是正确的,不然解压一定会出错
kernelj 2008-08-08
  • 打赏
  • 举报
回复
关注
老之 2008-08-07
  • 打赏
  • 举报
回复
文件传输,可以用应答式的发送,每个包带时间标志,用毫秒为单位。发送端按顺序一个个包地发,接收端接收到一个包,就回一个消息,发送端收到这个消息才继续发下一个包。直到收到结束包,校验、写文件。

1,593

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 网络通信/分布式开发
社区管理员
  • 网络通信/分布式开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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