【问题标题】:Why do I get an AccessViolation in Indy Sockets 9 IdTcpServer ServerExecute?为什么我在 Indy Sockets 9 IdTcpServer ServerExecute 中出现 AccessViolation?
【发布时间】:2009-06-19 08:56:07
【问题描述】:

第一个问题:

以下例程是 Indy 9 IdTcpServer.OnExecute 例程的正确实现吗?

procedure TMyConnServer.ServerExecute(AContext: TIdPeerThread);
var
  buffSize: integer;
  str:      string;
begin
  AContext.Connection.ReadFromStack(True, 600, False);
  buffSize := AContext.Connection.InputBuffer.Size;
  if (buffSize > 0) then
    { Extract input buffer as string }
    str := AContext.Connection.ReadString(buffSize);

    { Notify connection object of received data }
    if (AContext.Data <> nil) then
    begin
      TConnectionHandler(AContext.Data).Read(str);
    end;
  end;
end;

第二个(实际上更重要的)问题:

现在偶尔会出现访问冲突(从地址 000000 读取)。显然在行:

  AContext.Connection.ReadFromStack(True, 600, False);

但检查 AContext / Connection / InputBuffer / IOHandler = nil BEFORE 是否为假。 调用之后(以及引发异常之后)IOHandler 为 nil。

我们使用的是 RAD Studio / Delphi 2007。

【问题讨论】:

    标签: delphi tcp indy access-violation


    【解决方案1】:

    IOHandler 可以像您描述的那样变为 nil 的唯一方法是,如果您的应用程序中的另一个线程在您的工作线程仍在运行时在连接上调用 Disconnect()。

    【讨论】:

    • 嗯,是的,我想这是正确的。由于 ServerExecute 函数不重叠(是吗?)我可以在 ServerExecute 内部使用单个 CriticalSection 并锁定,并且在断开连接时也锁定吗?
    • TIdTCPServer 是一个多线程组件。每个客户端连接都在自己的线程中运行。因此,OnExecute 事件处理程序可以同时运行多次,每次都在不同的线程上下文中。
    • 您不需要锁定对服务器的各个客户端连接的访问​​,除非您需要在多个线程中读取它们,或者在多个线程中写入它们(这通常表明您可能有错误的代码设计开始)。
    【解决方案2】:

    嗯,我拥有的最简单的 onExecute 处理程序是这样的。 (请原谅 C++ 而不是 Delphi,但你会明白的。

    void __fastcall MyPanel::DoTCPExecute(TIdPeerThread * AThread)
    {
      AnsiString text =AThread->Connection->ReadLn();
      // now do something with text
    }
    

    我能看到的唯一明显问题是您正试图使用​​数据的“时间”来确定何时拥有完整的字符串。这对 TCP 来说是一个真正的禁忌。您可能只有字符串的第一个字节,或者您可能同时发送了多个字符串。使用 TCP 并不能保证每个“发送”在另一端都以单个“接收”结束。

    您需要以其他方式“分隔”您的字符串。 Readln 使用换行符作为终止符——另一种方法是为每个数据块添加一个长度字段的前缀。您读取长度,然后读取剩余数据。

    【讨论】:

    • 嗨,我担心 ReadLn() 是没有选择的,因为数据没有换行符。我知道数据可能不完整(而且通常不完整),但重建是稍后在 .Read(str) 方法中完成的。实际上,数据类型“字符串”仅用于传输原始字节(因为这是我们以后需要数据的方式)。还有一个例外:ReadLn() 也可以在内部调用 ReadFromStack() 这将再次导致 EAccessViolation...
    • 如果数据有任何类型的分隔符,您可以将该分隔符传递给 ReadLn(),它不仅限于 CR/LF 字符。如果数据以另一种方式分隔(例如通过指定如何读取剩余数据的前面的标题),那么您必须单独处理。将字符串用作原始字节缓冲区并不是一个好主意,尤其是当您升级到 D2009 时,字符串行为已发生变化。如果您需要对原始字节进行操作,则使用实际的原始字节缓冲区,例如 TBytes。 Indy 有可用于读取/写入原始字节的方法。
    【解决方案3】:

    代码就是这样工作的,但我认为这不是一个干净的选择:

      if (AContext.Connection.Connected) then
      begin
        try
          AContext.Connection.ReadFromStack(false, 1, false);
        except on E: EAccessViolation do
          // ignore
        end;
      end; 
      buffSize := AContext.Connection.InputBuffer.Size;
    

    【讨论】:

    • Indy 在内部为您处理 Connected() 调用。如果 InputBuffer 中仍有未读数据(这是设计使然),即使套接字可能已经关闭,Connected() 也会返回 True。您应该摆脱对 Connected() 的调用以及异常处理程序(因此 TIdTCPServer 可以为您处理错误(这是正确的套接字管理所必需的)。只需正常调用 ReadFromStack() 并让它报告任何错误需要。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-21
    • 2017-04-25
    • 2014-05-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多