急救:Delphi中IdUDPServer局域网内严重丢包的问题
本帖最后由 zhangdaodong 于 2013-06-24 13:06:06 编辑 本人想写一个局域网监控程序,有发消息,屏幕截图等。接收代码如下:
procedure TForm1.IdUDPServer1UDPRead(AThread: TIdUDPListenerThread;
AData: array of Byte; ABinding: TIdSocketHandle);
var
a:TMemoryStream;
Command:TCommand; //这是我自己定义的一个record
begin
a := TMemoryStream.Create;
a.WriteBuffer(AData[0],Length(AData));
a.Position := 0;
a.Read(Command,SizeOf(Command));
FreeAndNil(a);
ExcuteCommand(Command); //通过函数处理接收到的record包
end;
record原型如下:
TCommand = record
CommandType : TOP; //这中我定义的一个操作枚举类型
CommandText : string;
Timeout : Integer;
FileStream : TMemoryStream; //这里面主要是存放截的图,大小不会超过3MB
FormIP : string;
ToIP : string;
end;
现在是这么个情况,给本机发送文本和截屏都很正常,给局域网别的机子发送文本正常,截图没反应,我猜可能是含图片的record比较大,发送过程中丢包了。请问大神们,我的IdUDPServer1UDPRead事件应该如何优化?最好能带点代码,我对网络这方面不太熟悉,谢谢了。
[解决办法]
随手给你写了一个简单的演示
TTOP = (one, two);
TCommand = packed record
PackageSize: DWORD; // 整个包的大小,占4字节
CommandType: TTOP; // 枚举类型,可以,因为其实质就是Byte类型。占1字节
Command: string[9]; // 命令行文本,占10字节。string的第1个字节用来描述长度,可用字符为9 byte
PicSize: DWORD; // 图片Size,占4字节
end;
// 调用
var
Value: TCommand;
PicBuf: TBytesStream;
PicData: array of Byte;
SendBuf: TMemoryStream;
begin
Buffer.CommandType := two;
Buffer.Command := 'Hello';
// 模拟一个图片数据,为了方便说明,假设图片只有5字节
SetLength(PicData, 5);
PicData[0] := $11;
PicData[1] := $22;
PicData[2] := $33;
PicData[3] := $44;
PicData[4] := $55;
Buffer.PicSize := Length(PicData);
Buffer.PackageSize := SizeOf(Buffer) + Length(PicData); // 这里包的总长度为19
// 将要发送的数据写入缓冲区
SendBuf := TMemoryStream.Create;
SendBuf.WriteBuffer(Buffer, SizeOf(Buffer));
SendBuf.WriteBuffer(PicData[0], Length(PicData));
SendBuf.Free;
end;
最后SendBuf的内存区域如图

开始的18 00 00 00 就是整个包的长度,这样能让接收方知道一个包到底有多长
接着的 01 是枚举类型,再接着的 05 就是string的长度
包末尾的 11 22 33 44 55 就是图片数据了,在此前的05 00 00 00就是图片数据的长度
这样,一个完整的包就OK了,接收方根据包提供的长度等信息读出数据
当然在实际运用中,你图片的数据可能是很大的,会有分包的情况
不是你设个100MB,就真的能一次发个100MB的包过去
而且UDP是可能会乱序的,可能你发的包,图片数据先到了,包头后来才到……
传输文件,建议用TCP,因为你这里要传输文件,所以要考虑分包的情况
[解决办法]
UDP包的大小得合理,太大肯定收不到,可以把图切成小块发,附带小块的位置
[解决办法]
不管什么方式,数据包大小都要合适,不要太大,否则存在分包的情况,这样你接收的数据可能不连续。。。