0

我试图将数据库记录从我的服务器端应用程序传递到我的客户端应用程序。在客户端,我需要将我的数据存储到一个TStrings集合中。

当我传递一个多行字段时,我在客户端收到两个单独的数据项,而不是一个多行数据项!我也尝试过使用基于 Unicode UTF8 的命令来做到这一点,但不幸的是结果是一样的。

服务器端代码:

procedure TForm1.IdCmdTCPServer1CommandHandlers0Command(ASender: TIdCommand);
var
  myData: TStrings;
begin
  myData := TStringList.Create;

  myData.Add('12'); // ID
  myData.Add('This is a multi line' + #13#10 + 'description.'); // Descriptions
  myData.Add('Thom Smith'); // Name

  try
    ASender.Context.Connection.Socket.Write(myData, True{, TIdTextEncoding.UTF8});
  finally
    myData.Free;
  end;
end;

myData服务器端的调试时间值是:

myData[0] = '12'
myData[1] = 'This is a multi line'#$D#$A'description.'
myData[2] = 'Thom Smith'

客户端代码:

procedure TForm1.Button1Click(Sender: TObject);
var
  myData: TStrings;
begin
  with TIdTCPClient.Create(nil) do
  begin
    Port := 1717;
    Host := 'localhost';

    try
      Connect;
      //IOHandler.DefStringEncoding := TIdTextEncoding.UTF8;

      myData := TStringList.Create;
      try
        SendCmd('greating');
        Socket.ReadStrings(myData, -1{, TIdTextEncoding.UTF8});

        eID.Text    := myData[0];  // ID TEdit
        mDes.Text   := myData[1];  // Descriptions TMemo
        eTelNo.Text := myData[2];  // Name TEdit
      finally
        myData.Free;
      end;
    finally
      Disconnect;
      Free;
    end;
  end;
end;

myData客户端的调试时值:

myData[0] = '12' myData 1 = '这是多行' myData[2] = '说明。'

远程登录结果:

在此处输入图像描述

实际上,myData[2]应该保留的内容'Thom Smith'已替换为描述字段的第二行!之后没有项目myData[2]myData[3]不再可访问。

我认为这个问题与 Indy 的WriteReadStrings程序有关,因为它将 ItemCount 发送为 3,但它发送了两个项目(一个正确,下一个喙到两个项目!)。

如何将回车符传递到另一端,而无需将Write过程myData[1]分成两行?

非常感谢。

4

2 回答 2

2

如果您想TStrings.Text忽略特殊字符 - 您应该在通过网络发送之前将它们转义,然后再取消转义。逃生的方式有很多种,选择适合自己的吧。

function EscapeString:(String): String --- your choice
function DeEscapeString:(String): String --- your choice

procedure SendEscapedStrings(const socket: TIdSocket; const data: TStrings);
var s: string; temp: TStringList;
begin
  temp := TStringList.Create;
  try
    temp.Capacity := data.Count;
    for s in data do
        temp.Add( EscapeString( s ) );
    socket.Write(temp);
  finally
    temp.Destroy;
  end;
end;

procedure ReadDeescapedStrings(const socket: TIdSocket; const data: TStrings);
var s: string; temp: TStringList;
begin
  temp := TStringList.Create;
  try
    Socket.ReadStrings(temp, -1);
    data.Clear;
    data.Capacity := temp.Count;
    for s in temp do
        temp.Add( DeEscapeString( s ) );
  finally
    temp.Destroy;
  end;
end;

现在的问题是你会选择DeEscapeString什么EscapeString?选项很多。

  • 您可以选择在发送前将字符串转换为 base64,在阅读后选择从 base64
  • escapgin 可以选择 UUEEncode,反转义可以选择 UUEDecode
  • 你可以选择yEnc:http ://en.wikipedia.org/wiki/YEnc
  • 或者您可以从Jedi CodeLib ( http://jcl.sf.netStrStringToEscaped )的单元中选择非常简单的函数:StrEscapedToStringJclString

什么样的逃避

如果您寻求建议,我建议不要使用原始 TCP 服务器。有众所周知的标准 HTTP 协议,有许多 Delphi 实现 HTTP 服务器和 HTTP 客户端的库。并且在协议(和库)中已经确定了诸如加密、压缩、语言支持等内容。如果出现问题 - 您可以使用任何 HTTP 嗅探器并查看谁在错误的客户端或服务器中 - 使用您自己的眼睛。调试要简单得多。

如果您刚刚开始,我建议您查看 HTTP+JSON Synopse mORMot 库,也许它会满足您的需求。例如,您可以从http://robertocschneiders.wordpress.com/2012/11/22/datasnap-analysis-based-on-speed-stability-tests/或从 lib 中的演示中获取示例服务器代码。

然后,如果围绕原始 TCP 服务器进行安排,我会发送压缩数据,这样它会更好地工作(网络通常比 CPU 慢)。请参阅http://docwiki.embarcadero.com/CodeExamples/XE5/en/ZLibCompressDecompress_(Delphi)

发送:

  • 1:发送到网络(int32) - TStringList.Count
  • 2:对于每一个字符串做
  • 2.1TStringStream从字符串创建[i]
  • 2.2 通过 TZCompressionStream 传递
  • 2.3 发送(int32)大小的压缩数据
  • 2.4 发送数据本身
  • 2.5 释放临时流

接收

  • 1:从网络接收(int32) - 数据包计数
  • 1.1 结果字符串列表.清除;ResultStringList.Capacity := read_count。
  • 2:对于每一个字符串做
  • 2.1 创建TBytesStream
  • 2.2 从net(int32)大小的压缩数据中读取
  • 2.3 从网络中读取N个字节到BytesStream中
  • 2.4 通过 TZDecompressionStream 解压成 TStringStream
  • 2.5 ResultStringList.Add(StringStream -> string);
  • 2.6 释放临时流

现在,如果你真的不想改变几乎任何东西,那么 JCL 转义对你来说就足够了。至少它对我有用,但我的任务非常不同,根本与网络无关。但是您可以对它们全部进行测试,看看它是如何为您工作的。

于 2014-01-20T14:18:15.757 回答
0

不要使用TStrings重载,因为它似乎使用换行符作为字符串之间的分隔符,如果您的字符串本身包含换行符,这将不起作用。

您可以轻松编写自己的包装器方法以通过线路发送字符串列表(将其作为伪代码):

procedure WriteStrings(IOHandler : TIdIOHandler; Strings : TStrings);
var
  Str : String;
begin
IOHandler.WriteBufferOpen;
try
  IOHandler.Write(Strings.Count);
  for Str in Strings do
    IOHandler.Write(Str);  
finally
  IOHandler.WriteBufferClose;
end;
end;

procedure ReadStrings(IOHandler : TIdIOHandler; Strings : TStrings);
var
  Count, I : Integer;
begin
Count := IOHandler.ReadInteger;
for I := 1 to Count do
  Strings.Add(IOHandler.ReadString);
end;
于 2014-01-20T10:01:32.983 回答