【问题标题】:Why does TTcpClient drop data on SendStream()?为什么 TTcpClient 在 SendStream() 上丢弃数据?
【发布时间】:2015-05-22 17:20:25
【问题描述】:
当我调用 TTcpClient.SendStream(MyStream) 时,它会将 MyStream.Position 推进到 8704,我必须反复调用 SendStream() 才能让它完全发送我的流。但是,接收到的数据大约每 8K 丢失 512 个字节的块。
注意:这个问题是修辞性的,因为我曾尝试在网络上找到解决方案但未能成功。我在 Delphi 7 Sockets.pas 中发现了该错误,并希望发布解决方案以造福社区。
【问题讨论】:
标签:
sockets
delphi
stream
delphi-7
tcpclient
【解决方案1】:
问题是 Delphi 7 Sockets.pas 中的一个编码错误。该错误会导致任何大于约 8K(确切大小取决于操作系统)的流丢失 512 字节的数据块。 SendStream 实现使用 repeat..until 循环从调用者的流中提取 512 字节的缓冲区以使用 SendBuf() 发送,只要流有数据并且 SendBuf() 不返回等于 SOCKET_ERROR,它就会继续。当 Windows 套接字缓冲区填满时会发生丢失,导致 SendBuf() 返回等于 SOCKET_ERROR,但此时已从调用者的流中读取了多达 512 个字节,并且流位置已提前 - 但该位置未恢复退出时。原始 Sockets.pas 代码:
函数 TBaseSocket.SendStream(AStream: TStream): 整数;
变量
BufLen:整数;
缓冲区:字节数组[0..511];
开始
结果:= 0;
如果已分配(AStream)然后开始
重复
BufLen := AStream.Read(Buffer, SizeOf(Buffer));
直到 (BufLen = 0) 或 (SendBuf(Buffer, BufLen) = SOCKET_ERROR);
结尾;
结尾;
这是一个修复:
函数 TBaseSocket.SendStream(AStream: TStream): 整数;
变量
退出:布尔值;
BufLen,OldPosition:整数;
缓冲区:字节数组[0..511];
开始
结果:= 0;
如果已分配(AStream)然后开始
重复
OldPosition := AStream.Position;
BufLen := AStream.Read(Buffer, SizeOf(Buffer));
如果 (BufLen > 0) 然后开始
退出 := (SendBuf(Buffer, BufLen) = SOCKET_ERROR);
如果退出则 AStream.Position := OldPosition; //恢复!
end else begin //BufLen = 0
退出:=真;
结尾;
直到退出;
结尾;
结尾;