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

Delphi Indy TIdCmdTCPServer读取数据出现随机乱码问题排查

问题描述

我正在实现一个非常简易的 TIdCmdTCPServer 服务端,用于接收我方自研手机应用发送的数据。手机应用启动后会首先发送简单的 hello 指令,但服务端触发 OnBeforeCommandHandler 事件时收到的数据存在乱码。

AData 参数的内容格式为 '随机非ASCII字符 hello',乱码部分均为非ASCII字符。

我最初使用Delphi 2007自带的Indy 10.1.5组件开发,先后开展了如下排查工作:

  • 怀疑自身程序存在内存损坏问题,新建仅包含窗体和 TIdCmdTCPServer 组件的最简测试项目,问题复现。
  • 怀疑Delphi 2007自带Indy组件存在Bug,切换至Delphi 10.3环境测试,问题依旧存在。
  • 怀疑本机环境导致通信数据损坏,在多台物理、虚拟Windows 10设备上运行测试程序,问题仍未解决。
  • 怀疑手机应用发送数据异常,改用Putty直接向服务端发送指令,仍出现乱码。
  • 更换服务端监听端口测试,问题无变化。

我检索了相关问题资料,未找到同类问题记录,现有公开资料均显示 TIdCmdTCPServer 可正常稳定运行。

该问题存在特殊规律:

  • 连续发送5-6次指令后,数据读取就会恢复正常
  • 每次事件触发时的乱码内容均与上一次不同
  • 单步调试Indy源码发现,服务端首次执行读取操作时,输入缓冲区中就已经存在乱码字符
  • 不同指令需要的发送次数不同才能实现无乱码读取,例如"hello"指令通常需要发送8-9次(次数不固定)才能正常读取。

我仅通过如下代码启动服务,所有组件属性均保持默认值未做修改,不清楚是否需要为服务端单独开辟线程,Indy官方帮助文档未提及相关要求:

IdCmdTCPServer1.Active := True;

以下为最简测试应用的完整代码:
单元代码:

unit test;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, IdBaseComponent, IdComponent, IdCustomTCPServer, IdTCPServer,
  IdCmdTCPServer, IdCommandHandlers, IdContext, IdServerIOHandler,
  IdServerIOHandlerSocket, IdServerIOHandlerStack;

type
  TForm1 = class(TForm)
    cmdsMain: TIdCmdTCPServer;
    ioshMain: TIdServerIOHandlerStack;
    procedure FormShow(Sender: TObject);
    procedure cmdsMainCommandHandlers0Command(ASender: TIdCommand);
    procedure cmdsMainBeforeCommandHandler(ASender: TIdCmdTCPServer;
      var AData: string; AContext: TIdContext);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.cmdsMainBeforeCommandHandler(ASender: TIdCmdTCPServer;
  var AData: string; AContext: TIdContext);
begin
// 此处收到的数据存在乱码
end;

procedure TForm1.cmdsMainCommandHandlers0Command(ASender: TIdCommand);
begin
//
end;

procedure TForm1.FormShow(Sender: TObject);
begin
cmdsMain.Active := True
end;

end.

DFM窗体设计代码:

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 299
  ClientWidth = 635
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnShow = FormShow
  PixelsPerInch = 96
  TextHeight = 13
  object cmdsMain: TIdCmdTCPServer
    Bindings = <
      item
        IP = '0.0.0.0'
        Port = 55
      end>
    DefaultPort = 55
    IOHandler = ioshMain
    CommandHandlers = <
      item
        CmdDelimiter = ' '
        Command = 'hello'
        Disconnect = False
        Name = 'TIdCommandHandler0'
        NormalReply.Code = '200'
        ParamDelimiter = ' '
        ParseParams = True
        Tag = 0
        OnCommand = cmdsMainCommandHandlers0Command
      end>
    ExceptionReply.Code = '500'
    ExceptionReply.Text.Strings = (
      'Unknown Internal Error')
    Greeting.Code = '200'
    Greeting.Text.Strings = (
      'Welcome')
    HelpReply.Code = '100'
    HelpReply.Text.Strings = (
      'Help follows')
    MaxConnectionReply.Code = '300'
    MaxConnectionReply.Text.Strings = (
      'Too many connections. Try again later.')
    ReplyTexts = <>
    ReplyUnknownCommand.Code = '400'
    ReplyUnknownCommand.Text.Strings = (
      'Unknown Command')
    OnBeforeCommandHandler = cmdsMainBeforeCommandHandler
    Left = 312
    Top = 152
  end
  object ioshMain: TIdServerIOHandlerStack
    Left = 376
    Top = 168
  end
end

问题更新:我们修改手机应用逻辑,改为向公共回声服务器tcpbin.com发送指令,回显数据完全正常;使用Putty模拟手机端向该回声服务器发送数据,同样未出现乱码,可排除客户端发送数据异常的可能。


答案

核心问题点

  1. 多余的手动IOHandler配置:TIdCmdTCPServer激活时会自动创建适配的IOHandler实例,不需要手动在窗体上添加TIdServerIOHandlerStack组件。手动挂载的IOHandler工厂在Indy 10部分版本中存在初始化异常,会导致输入缓冲区读取到未初始化的内存垃圾,这是随机乱码的核心诱因。
  2. Telnet协商字节干扰:默认开启的服务端Greeting消息会在客户端连接后主动推送200 Welcome响应,Putty如果使用Telnet模式连接,会误将服务端识别为Telnet服务器,自动回复0xFF开头的Telnet协商控制字节(非ASCII二进制数据),这些字节会被当作命令前缀读入形成乱码。
  3. 测试端口选择问题:当前使用的55端口属于系统保留特权端口(<1024),部分Windows系统的网络过滤组件会对这类端口的数据包做额外处理,可能引入异常数据。
  4. 命令结束符校验缺失:TIdCmdTCPServer默认以CRLF(\r\n)作为单条命令的结束标记,如果客户端发送指令时未携带正确的换行符,会导致缓冲区数据累计错位,出现解析异常。

修复步骤

  1. 删掉窗体上的ioshMain组件,清空cmdsMain.IOHandler属性,让组件自动创建IOHandler,不要手动指定IOHandler工厂。
  2. 更换监听端口为1024以上的高端口(如8080、12345),排除特权端口的系统过滤影响。
  3. Putty测试时必须选择Raw连接模式,禁止使用Telnet、SSH等其他模式,避免客户端自动发送协议协商字节。如果不需要服务端发送欢迎消息,直接将cmdsMain.Greeting.Code设为空字符串,关闭Greeting推送。
  4. 确认自研手机端发送每条指令时,结尾必须追加CRLF换行符,不要只发纯指令内容。
  5. 不需要手动为服务端开辟额外线程,IdCmdTCPServer1.Active := True;的启动方式完全符合Indy设计要求,组件内部会自动维护监听线程和工作线程池。

验证方式

在OnConnect事件中添加日志,读取AContext.Connection.IOHandler.InputBuffer的原始字节转16进制输出:如果输出字节以FF开头,说明是未过滤的Telnet协商字节,按上述步骤关闭Greeting、使用Raw模式连接即可解决;如果输出是无规律随机字节,确认已删除手动添加的IOHandler组件后重启程序测试。


内容的提问来源于stack exchange,提问作者alvaroc

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 06:27:14