-1

我是 XE5 用户。我有一个用 D7 编写的客户端/服务器应用程序。我已经升级到XE5。因为 D7 不是 unicode,所以我使用了以下类型:

TRap = array[0..254] of AnsiChar;

我正在通过 tcpip 将此数据发送到服务器。服务器具有相同的定义。现在我需要升级到 unicode,但大小必须相同。因为我正在使用以下模式:

PMovieData = ^TMovieData;
TMovieData = packed record
   rRap: TRap;
   rKey: string[7];
   iID: integer;
end;

我试图将 TRap 更改为:

TRap2 = array[0..127] of WChar;

但是大小并不相等。TRap 是 255,但 TRap2 是 256。我无法更改大小,因为它应该适用于 ex 版本。你有什么建议?

4

3 回答 3

1

您在问题中提到您正在将此数组数据类型中保存的数据发送到服务器。

如果该服务器实现在您的控制之下并且也正在升级到 Unicode,那么您只需要确保数据交换的双方就数据的内容达成一致。如果您需要一个数组来保存长度不超过 255 个字符的“字符串”(这是您的 ANSI 实现所支持的),那么您的 Unicode 版本也需要支持 255 个字符。变化的是每个字符所需的数据量 - 2 个字节,而不是 1 个(它并不那么简单,但我假设您的 ANSI 实现没有处理 MBCS 问题,并且您的 Unicode 实现同样不会' t 关心代理对(就影响有效“字符”的数量而言)。

即你的TRap数组应该是:

TRap = array[0..254] of WIDEChar;

但是,如果服务器实现不在您的控制之下(您观察到新代码必须继续与旧版本一起使用暗示了这一点),那么无论您在客户端应用程序中进行什么更改,服务器都将继续期待255 个ANSI字符。在这种情况下,您的TRap类型必须与以前保持相同(ANSIChar 数组),并且您必须确保在 WIDE 字符串字符传入和传出数组时将它们转换为 ANSI(反之亦然)。

注意:将 UTF-8 转换为该数组或任何其他设计只是为了使相同数量的字符“适合”是没有意义的,除非新代码将使用的旧版本服务器已经可以接收 UTF-8编码字符(根据手头的信息,它几乎肯定不会)。

回到服务器实现在您的控制之下的情况,您可能会保持对 ANSI TRap类型的支持,并通过在数据结构的新 WIDE char 版本中合并一个“magic cookie”来引入新的 WIDE char 实现允许服务器识别正在传递的数据结构类型并相应地进行调整。这些方面的东西:

TANSIRap = array[0..254] of ANSIChar;
TWIDERap = array[0..254] of WIDEChar;


// ANSIMovieData corresponds *exactly* to the old MovieData structure

PANSIMovieData = ^TANSIMovieData;
TANSIMovieData = packed record
   rRap: TANSIRap;
   rKey: string[7];
   iID: integer;
end;


// WIDEMovieData adds a magic cookie "header" identifying the new structure type

PWIDEMovieData = ^TWIDEMovieData;
TWIDEMovieData = packed record
   rCookie: Word;
   rRap: TWIDERap;
   rKey: string[7];
   iID: integer;
end;

发送TWIDEMovieData时,将rCookie字段设置为某个不会出现在有效TANSIRap数组中的常量值。例如,如果“空” TRAp数组(即 ANSI 版本)全为 0,则$00FF的 cookie可能是合适的,因为没有有效的 ANSI 电影数据结构可以以前导 #0 字符开头紧跟 #ff 字符:

const 
  MOVIEDATA_WIDECOOKIE = $00FF;



// new client pseudo-code:

data: TANSIMovieData;
wdata: TWIDEMovieData;


if ANSI Server then

  data...             // init ANSI movie data (TANSIRap ANSI chars converted from WIDE)
  SendToServer(data); // etc

else // WIDE server

  wdata.rCookie := MOVIEDATA_WIDECOOKIE;

  wdata.....           // init WIDE movie data
  SendToServer(wdata); // etc



// server pseudo-code:

var
  data: PANSIMovieData;
  wdata: PWIDEMovieData;


ReceiveDataIntoBuffer(data^);

wdata := PWIDEMovieData(data);

if wdata.rCookie = MOVIEDATA_WIDECOOKIE then
   HandleWideData(wdata)
else
   ExistingANSIDataHandler(data);

唯一的问题是新客户端如何确定与之通信的服务器是否能够支持 WIDE 电影数据。但这只有在服务器实现在您的控制之下时才会成为问题(如果不是,那么您无论如何只能继续使用 ANSI),因此您应该能够设计一些机制来识别服务器功能,以使旧服务器能够可靠地运行被新客户识别。

绝对最坏的情况,您可能需要一个客户端配置设置(请注意,如果您将客户端配置为使用 ANSI,即使对于新服务器,它仍将继续工作,只是没有 Unicode 支持)。

New client / New server : client and server both use WIDE (but will also work with ANSI)
New client / Old server : client uses ANSI
Old client / New server : server detects ANSI
Old client / Old server : no change

根据服务器实现,您可能需要分阶段读取数据,根据传递的数据结构读取前 2 个字节以获得前两个TANSIRap字符或WIDECOOKIE,然后根据是否读取数据包中的剩余字节你检测到cookie与否,但原理本质上是一样的。

于 2014-09-30T19:22:08.217 回答
1

您显示的代码在 XE5 中的工作方式与在 D7 中的工作方式完全相同。 AnsiChar和短字符串仍然可用,并且仍然是 8 位 Ansi,因此它们的大小始终相同。您根本不必更改定义...

...除非您想为移动设备编译,在这种情况下AnsiChar,短字符串不再可用(您可以安装RTL 补丁以将它们取回),在这种情况下,您可以将代码更改为以下内容以保持与服务器:

TRap = array[0..254] of Byte;

PMovieData = ^TMovieData;
TMovieData = packed record
   rRap: TRap;
   rKey: array[0..7] of Byte; // put the string length in rKey[0]
   iID: integer;
end;
于 2014-09-30T17:18:31.787 回答
1

好吧,因为宽字符是 2 个字节宽,所以宽字符数组的大小是偶数。255不是偶数。因此,您不能将宽字符数组覆盖在原始数组上。

我想你可以有一个 127 个宽字符和一个填充字节的数组:

TMovieData = packed record
  rRap: array [0..126] of WideChar;
  _reserved: Byte;
  rKey: string[7];
  iID: integer;
end;

我无法想象这会有多大帮助,因为旧组件会将宽字符数据解释为 8 位数据。这会产生意想不到的结果。基本上文字会乱码。

您可能会考虑其他一些选择:

  1. 继续使用 8 位字符数组,但编码为 UTF-8。这将在 ASCII 范围内兼容。但是 128-255 范围内的任何文本都会出现乱码。当然,目前您现有的解决方案只有在客户端和服务器都使用相同的 8 位字符集时才能工作。
  2. 切换所有组件以使用新的记录格式。事实上,如果你这样做了,你可以放弃你死板的记录格式并使用 JSON 序列化记录。将其编码为 UTF-8 并传输这些字节。这也将允许您解决当前实现的问题,即它不尊重网络字节顺序。
于 2014-09-30T15:19:34.793 回答