【问题标题】:Send bytes using Indy 10使用 Indy 10 发送字节
【发布时间】:2021-05-12 08:52:27
【问题描述】:

我正在尝试使用 Indy 10 在两个 delphi 2010 应用程序之间发送字节,但没有成功。接收的字节与发送的字节不同。这是我的示例代码:
应用程序 1,发送按钮点击:

var s:TIdBytes;
begin
    setlength(s,3);
    s[0]:=48;
    s[1]:=227;
    s[2]:=0;
    IdTCPClient.IOHandler.WriteDirect(s);  // or .Write(s);
end;

应用 2,idtcpserver 执行事件(第一次尝试):

procedure TForm1.IdTCPServerExecute(AContext: TIdContext);
var rec:TIdBytes;
     b:byte;
begin
    SetLength(rec,0);
    b:=AContext.Connection.IOHandler.ReadByte;
    while b<>0 do
    begin 
         setlength(rec, length(rec)+1);
         rec[length(rec)-1]:=b;
         b:=AContext.Connection.IOHandler.ReadByte;
    end;

    // Here I expect rec[0] = 48, rec[1]=227, rec[2]=0.
    // But I get: rec[0] = 48, rec[1]=63, rec[2]=0.   Rec[1] is incorrect
    // ... rest of code
end;

应用程序 2,idtcpserver 执行事件(第二次尝试):

procedure TForm1.IdTCPServerExecute(AContext: TIdContext);
var c:ansistring;
begin
    c:= AContext.Connection.IOHandler.ReadLn(Char(0));

    // Here I expect c[0] = 48, c[1]=227, c[2]=0.
    // But I get: c[0] = 48, c[1]=63, c[2]=0.   c[1] is incorrect
    // ... rest of code
end;

最奇怪的是,这些应用程序是几年前用 Delphi 5 开发的,它们运行良好(使用 readln(char(0))。当我将这两个应用程序翻译到 Delphi 2010 时,它们停止工作。我想这是由于 unicode字符串,但我还没有找到解决方案。

【问题讨论】:

    标签: delphi-2010 indy10


    【解决方案1】:

    首先,不要直接使用TIdIOHandler.WriteDirect() ,只使用TIdIOHandler.Write() 重载。

    其次,您的第一次尝试代码不可能产生您声称的结果。 TIdIOHandler.Write[Direct](TIdBytes)TIdIOHandler.ReadByte() 仅对原始字节进行操作,字节按原样传输。因此,您可以保证得到您发送的准确信息,字节值不可能按照您的建议进行更改。

    但是,您的 第二次尝试 代码肯定会产生您声称的结果,因为 TIdIOHandler.ReadLn() 将读取字节并将它们转换为 UnicodeString,然后您将其分配给AnsiString,表示接收到的数据正在经历2次有损字符集转换

    • 首先,使用TIdIOHandler.DefStringEncoding 属性作为解码字符集,将接收到的字节按原样解码为UTF-16 UnicodeString,默认情况下设置为IndyTextEncoding_ASCII(您可以将其更改为IndyTextEncoding_8bit)。字节 227 超出了 ASCII 范围,因此该字节被解码为 Unicode 字符 '?' (#63),这表明发生了数据丢失。

    • 然后,UnicodeString 分配给AnsiString,它使用 RTL 的默认字符集(默认设置为用户的操作系统区域设置,但可以使用 @987654335 更改)将 UTF-16 数据转换为 ANSI @ 功能)。在这种情况下,不会再发生丢失,因为 UnicodeString 包含 US-ASCII 范围内的字符,这些字符按原样转换为 ANSI。


    话虽如此,我不建议像您那样在手动循环中使用TIdIOHandler.ReadByte()。尽管TIdIOHandler 没有用于字节的WaitFor() 方法,但仍有一种更有效的方法来解决此问题,例如:

    procedure TForm1.IdTCPServerExecute(AContext: TIdContext);
    var
      rec: TIdBytes;
      LPos: Integer;
    begin
      SetLength(rec, 0);
    
      // adapted from code in TIdIOHandler.WaitFor()...
      with AContext.Connection.IOHandler do
      begin
        LPos := 0;
        repeat
          LPos := InputBuffer.IndexOf(Byte(0), LPos);
          if LPos <> -1 then begin
            InputBuffer.ExtractToBytes(rec, LPos+1);
            { or, if you don't want the terminating $00 byte put in the TIdBytes:
            InputBuffer.ExtractToBytes(rec, LPos);
            InputBuffer.Remove(1);
            }
            Break;
          end;
          LPos := InputBuffer.Size;
          ReadFromSource(True, IdTimeoutDefault, True);
        until False;
      end;
    
      // ... rest of code
    end;
    

    或者:

    procedure TForm1.IdTCPServerExecute(AContext: TIdContext);
    var
      rec: string
    begin
      rec := AContext.Connection.IOHandler.ReadLn(#0, IndyTextEncoding_8Bit);
      // ... rest of code
    end;
    

    最奇怪的是,这些应用程序是几年前用 Delphi 5 开发的,它们运行良好(使用 readln(char(0))。当我将这两个应用程序翻译到 Delphi 2010 时,它们停止工作。我想这是由于 unicode字符串,但我还没有找到解决方案。

    是的,Delphi 5 中的 string 类型是 AnsiString,但在 Delphi 2009 中改为 UnicodeString

    但即便如此,在 Delphi 的预 Unicode 版本中,Indy 10 中的 TIdIOHandler.ReadLn() 仍将读取原始字节并使用 TIdIOHanlder.DefStringEncoding 将它们转换为 Unicode。然后,它会在退出之前将该 Unicode 数据转换为 AnsiString,使用 TIdIOHandler.DefAnsiEncoding 属性作为转换字符集,默认情况下设置为 IndyTextEncoding_OSDefault

    故事的精神是 - 每当您以字节为单位读取并将它们转换为字符串字符时,必须使用正确的字符集对字节进行解码,否则您将丢失数据。这在 Delphi 5 中是正确的(但没有严格执行),在 Delphi 2009+ 中更是如此。

    【讨论】:

    • 感谢您的回答,但它不起作用。实际上,我使用 write(s),但作为最后一次尝试,我尝试使用 writedirect(s)。它们都不起作用,我总是改变字节,正如我在 Delphi 的 watch 检查窗口中看到的那样。我尝试添加 IndyTextEnconding_8Bit 但结果相同。我尝试将变量定义为字符串、ansistring、ansichar,但它们都不起作用。另一方面,在您的代码示例中,未找到 ReadFromSource,我无法编译它。这种方法不存在(几乎是公开的)。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多