代码之家  ›  专栏  ›  技术社区  ›  vcldeveloper

为什么iohandler.readstream会在客户端连接到indy中的服务器时阻塞线程?

  •  2
  • vcldeveloper  · 技术社区  · 14 年前

    今天,我使用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,并且当执行达到context.connection.iohandler.readstream(astream)行时,会阻止在onexecute事件处理程序内运行的线程!

    当我跟踪代码时,问题是在readstream内部调用readlongnt以获取字节数。readlongint调用readbytes。在readbytes内,finputBuffer.Size为零。在那里,在一个循环中调用readfromsource,并最终执行到tidsocketlistwindows.fdselect,该函数从winsock2调用“select”函数,执行在这里停止,并且不会从该客户机连接收到任何内容。我也尝试给abytecount和areadUntilDisconnect参数赋值,但它没有改变行为。

    如果我用readln替换readstream,那么连接到服务器不会阻止代码执行,从客户机发送的数据由服务器读取。

    密码有什么问题吗?或者这是个虫子?

    当做

    1 回复  |  直到 14 年前
        1
  •  6
  •   Remy Lebeau    14 年前

    问题出在你的代码里,而不是 ReadStream() . 它按照设计的方式工作。

    它接受3个输入参数:

    procedure ReadStream(AStream: TStream; AByteCount: TIdStreamSize = -1; AReadUntilDisconnect: Boolean = False); virtual;
    

    您只提供第一个参数的值,因此其他两个参数使用默认值。

    AByteCount 参数设置为-1,并且 AReadUntilDisconnect 参数设置为假, 读流() 假设接收到的前4个字节(或8个字节,如果 IOHandler.LargeStream 属性设置为true)是正在发送的数据的长度,之后是实际数据。这就是为什么 读流() 正在呼叫 ReadLongInt() . 这不仅说明 读流() 何时停止阅读,但也允许 读流() 在接收数据之前,预先调整目标数据流的大小,以便更好地管理内存。

    如果客户机实际上没有在其数据之前发送4字节(或8字节)的长度值,那么 读流() 仍然将实际数据的起始字节解释为一个长度。这通常(但不总是,取决于数据)会导致 Read LangTin() (或) ReadInt64() )返回一个大的整数值,这将导致 读流() 期望大量的数据永远不会真正到达,从而不确定地阻止读取(或者直到发生超时,如果 IOHandler.ReadTimeout 属性设置为非无限超时)。

    为了使用 读流() 实际上,它需要知道什么时候停止阅读,或者被告知提前需要多少数据(即: AByteCount >= 0 或者要求发送方在发送数据后断开连接(即: AReadUtilDisconnect = True )结合 AByteCount = -1 AReadUtilDisconnect = False 是一种特殊情况,当长度直接在流中编码时。这主要用于(但不限于)发送方呼叫时 IOHandler.Write(TStream) 用它 AWriteByteCount 参数设置为真(默认为假)。

    处理非文本数据时,最好尽可能将数据长度发送到实际数据之前。它优化了读取操作。

    readstream()的不同参数组合符合以下逻辑:

    1. abytecount=-1,areadatildisconnect=false:读取4/8字节,解释为长度,然后继续读取,直到收到该长度为止。

    2. abytecount<-1,aredutildisconnect=false:假设areduntildisconnect为true,并一直读取直到断开。

    3. abytecount>-1,areadutildisconnect=false:预先调整目标t流的大小,并继续读取,直到接收到abytecount字节数。

    4. abytecount<=-1,areadultildisconnect=true:保持读数直到断开。

    5. abytecount>-1,areaDutildisconnect=true:预先调整目标t流的大小,并一直读取,直到断开连接。

    根据客户机最初实际发送到服务器的数据类型,可能是 读流() 可能不是读取该数据的最佳选择。iohandler有许多不同的读取方法。例如,如果客户机正在发送带分隔符的文本(尤其是与 IOHandler.WriteLn() ) ReadLn() 是更好的选择。