vcl*_*per 2 delphi indy delphi-2010 indy10
今天我使用Indy 10(Delphi 2010附带)遇到了一种奇怪的行为.这是问题所在:
假设我们的客户端有一个IdTcpClient,服务器应用程序中有一个IdTcpServer,而我们的IdTcpServer的OnExecute事件处理程序中有这个代码:
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
AStream: TStringStream;
S: string;
begin
AStream := TStringStream.Create;
try
AContext.Connection.IOHandler.ReadStream(AStream);
S := AStream.DataString;
finally
AStream.Free;
end;
end;
Run Code Online (Sandbox Code Playgroud)
现在,当客户端尝试连接到服务器时,使用TIdTcpClient.Connect; 在服务器上,调用TIdTcpServer.OnExecute,并在执行到达AContext.Connection.IOHandler.ReadStream(AStream)行时阻止在OnExecute事件处理程序中运行的线程!
当我跟踪代码时,在ReadStream中调用ReadLongInt以获取字节数时会引起问题.ReadLongInt调用ReadBytes.在ReadBytes内部,FInputBuffer.Size为零.在那里,在循环中调用ReadFromSource,最终执行到达TIdSocketListWindows.FDSelect,它从WinSock2调用"select"函数,并且执行在此处停止,并且不会从该客户端连接接收任何内容.我也尝试过赋予AByteCount和AReadUntilDisconnect参数值,但它没有改变行为.
如果我用ReadLn替换ReadStream,那么连接到服务器不会阻止代码执行,并且服务器会读取从客户端发送的数据.
代码有什么问题吗?或者这是一个错误?
问候
问题在于您的代码,而不是ReadStream().它按照设计行事.
它接受3个输入参数:
procedure ReadStream(AStream: TStream; AByteCount: TIdStreamSize = -1; AReadUntilDisconnect: Boolean = False); virtual;
Run Code Online (Sandbox Code Playgroud)
您只为第一个参数提供值,因此其他两个参数使用默认值.
当AByteCount参数设置为-1并且AReadUntilDisconnect参数设置为False时,ReadStream()设计为假设接收的前4个字节(或8个字节,如果IOHandler.LargeStream属性设置为True)是发送数据的长度,则后跟之后的实际数据.这就是为什么ReadStream()要打电话ReadLongInt().这不仅告诉您ReadStream()何时停止读取,而且还允许ReadStream()在接收数据之前预先调整目标TStream的大小以实现更好的内存管理.
如果客户端实际上没有在其数据之前发送4字节(或8字节)长度值,那么ReadStream()仍然会将实际数据的起始字节解释为长度.这通常(但不总是,取决于数据)导致ReadLongInt()(或ReadInt64())返回一个大整数值,这将导致ReadStream()期望大量数据永远不会实际到达,从而无限制地阻止读取(或直到超时如果IOHandler.ReadTimeout属性设置为非无限超时,则会发生.
为了ReadStream()有效地使用,它需要知道何时停止读取,或者通过被告知提前有多少数据(即:)AByteCount >= 0,或者通过要求发送者在发送其数据后断开连接(即:)AReadUtilDisconnect = True.当长度直接在流式传输中编码时,AByteCount = -1和的组合AReadUtilDisconnect = False是特殊情况.主要使用(但不限于)发送方调用IOHandler.Write(TStream)其AWriteByteCount参数设置为True(默认情况下为False).
处理非文本数据时,尽可能在实际数据之前发送数据长度始终是一个好主意.它优化了阅读操作.
ReadStream()的不同参数组合符合以下逻辑:
AByteCount = -1,AReadUtilDisconnect = False:读取4/8字节,解释为长度,然后继续读取,直到收到该长度.
AByteCount <-1,AReadUtilDisconnect = False:假设AReadUntilDisconnect为True并继续读取,直到断开连接.
AByteCount> -1,AReadUtilDisconnect = False:预先调整目标TStream的大小并继续读取,直到收到AByteCount字节数.
AByteCount <= -1,AReadUtilDisconnect = True:继续读取,直到断开连接.
AByteCount> -1,AReadUtilDisconnect = True:预先调整目标TStream的大小并继续读取直到断开连接.
根据客户端实际上首先发送到服务器的数据类型,ReadStream()可能不是读取该数据的最佳选择.IOHandler有许多不同类型的阅读方法.例如,如果客户端正在发送分隔文本(特别是如果它正在发送IOHandler.WriteLn()),那么这ReadLn()是一个更好的选择.