今天,我在使用Indy10(Delphi2010附带的)时遇到了一个奇怪的行为。以下是问题所在:
假设我们的客户端中有一个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;
现在,当客户机尝试使用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,那么连接到服务器不会阻止代码执行,并且从客户端发送的数据将由服务器读取。
代码有什么问题吗?或者这是一个bug?
问候
发布于 2010-07-27 06:41:37
问题出在您的代码中,而不是ReadStream()
中。它正在按照设计的方式运行。
它接受3个参数作为输入:
procedure ReadStream(AStream: TStream; AByteCount: TIdStreamSize = -1; AReadUntilDisconnect: Boolean = False); virtual;
您只提供了第一个参数的值,因此其他两个参数使用默认值。
当AByteCount
参数设置为-1并且AReadUntilDisconnect
参数设置为False时,ReadStream()
被设计为假定接收的前4个字节(如果IOHandler.LargeStream
属性设置为True,则为8个字节)是要发送的数据的长度,随后是实际数据的长度。这就是ReadStream()
调用ReadLongInt()
的原因。这不仅告诉ReadStream()
何时停止读取,而且还允许ReadStream()
在接收数据之前预先调整目标TStream的大小,以便更好地进行内存管理。
如果客户端实际上没有在其数据之前发送4字节(或8字节)的长度值,那么ReadStream()
仍然会将实际数据的开始字节解释为长度。这通常(但并非总是,取决于数据)会导致ReadLongInt()
(或ReadInt64()
)返回一个大整数值,这将导致ReadStream()
预计将有大量数据实际上永远不会到达,从而无限期地阻止读取(或者直到发生超时,如果IOHandler.ReadTimeout
属性设置为非无限超时)。
为了有效地使用ReadStream()
,它需要知道何时停止读取,要么提前被告知需要多少数据(例如:AByteCount >= 0
),要么要求发送者在发送数据后断开连接(例如:AReadUtilDisconnect = True
)。当长度直接在流中编码时,AByteCount = -1
和AReadUtilDisconnect = False
的组合是一个特例。这主要用于(但不限于)发送方在其AWriteByteCount
参数设置为True (默认情况下为False )的情况下调用IOHandler.Write(TStream)
。
在处理非文本数据时,尽可能在实际数据之前发送数据长度总是一个好主意。它优化了读取操作。
ReadStream()的不同参数组合符合以下逻辑:
disconnected.
根据客户机最初实际发送到服务器的数据类型,ReadStream()
可能不是读取该数据的最佳选择。IOHandler提供了许多不同类型的读取方法。例如,如果客户端发送分隔文本(尤其是使用IOHandler.WriteLn()
发送的文本),则ReadLn()
是更好的选择。
https://stackoverflow.com/questions/3328752
复制相似问题