You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Indy IdTCPServer的OnExecute方法中处理未知格式与长度的客户端连接

Indy TCP服务端处理非法客户端数据的方案

问题背景

服务端现有代码逻辑为:

procedure TFServer.IdTCPServerExecute(AContext: TIdContext);
begin
  ...
  size:=AContext.Connection.IOHandler.ReadLongInt;
  Context.Connection.IOHandler.ReadStream(msgStream, size);
  ...
end;

该逻辑仅适配客户端用IdTCPClient.IOHandler.Write(MemoryStream1,0,True)发送的二进制流数据(此方法会先发送流长度,再发送内容)。但如果客户端发送IdTCPClient.IOHandler.WriteLn(string_message)这类不符合协议的文本数据,服务端读取的前4字节会被解析成一个异常的size值,后续ReadStream会尝试读取远超实际的数据量,直接导致崩溃。即使改用ReadStream(msgStream,-1)也无法解决,因为这种情况下会持续读取直到连接断开,同样会耗尽内存。

核心应对思路

TCP是无边界的字节流协议,服务端必须通过严格的协议校验+异常防护来处理非法输入,不能假设客户端会遵守协议。

具体实现方案

  1. 给所有IO操作加异常捕获
    Indy的IO方法在遇到数据异常、连接中断、超时等情况时都会抛出异常,必须用try...except包裹,避免崩溃:

    procedure TFServer.IdTCPServerExecute(AContext: TIdContext);
    var
      size: Integer;
      msgStream: TMemoryStream;
    begin
      msgStream := TMemoryStream.Create;
      try
        try
          // 设置读取超时,防止恶意客户端占着连接不发数据
          AContext.Connection.IOHandler.ReadTimeout := 5000;
          // 先读取长度
          size := AContext.Connection.IOHandler.ReadLongInt;
          // 校验长度合理性:禁止负数,同时限制最大可处理的字节数(比如10MB)
          if (size < 0) or (size > 10 * 1024 * 1024) then
          begin
            AContext.Connection.Disconnect;
            Exit;
          end;
          // 按指定长度读取流,第三个参数设为False(因为我们已经自己处理了长度)
          AContext.Connection.IOHandler.ReadStream(msgStream, size, False);
          // 正常处理数据的逻辑...
        except
          on E: Exception do
          begin
            // 记录错误日志,直接断开异常连接
            // LogError('客户端异常: ' + E.Message);
            AContext.Connection.Disconnect;
          end;
        end;
      finally
        msgStream.Free;
      end;
    end;
    
  2. 添加协议校验位
    在协议头部增加固定的魔法标识(比如4字节的$DEADBEEF),服务端先读取该标识,不匹配则直接断开连接,过滤完全不符合协议的请求:

    // 服务端先读取魔法头
    magic := AContext.Connection.IOHandler.ReadLongInt;
    if magic <> $DEADBEEF then
    begin
      AContext.Connection.Disconnect;
      Exit;
    end;
    // 再读取长度和流数据...
    

    如果需要同时兼容不同类型的请求,可以再加一个类型标识(比如1字节),服务端根据类型选择对应的读取逻辑(比如0=二进制流,1=文本行)。

  3. 限制资源占用

    • 设置合理的ReadTimeout,避免恶意客户端长期占用连接。
    • 严格限制单个请求的最大数据长度,防止内存被耗尽。
  4. 正确使用ReadStream参数
    ReadStream(msgStream, -1)会持续读取直到连接关闭,这在恶意攻击场景下非常危险,绝对不能在无防护的情况下使用。必须结合协议约定的长度来控制读取量,或者配合超时机制使用。


内容的提问来源于stack exchange,提问作者R.Schirru

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 15:33:30