【问题标题】:Send Delphi Record via Socket to C#通过 Socket 将 Delphi 记录发送到 C#
【发布时间】:2015-08-11 10:36:50
【问题描述】:

正如主题所述,我尝试通过套接字将 Delphi 记录发送到 C# 并尝试将其编组为结构。

IDE:XE7 和 MS Visual Stidio 2010

Type Data = record 
intDiscipline:         Integer;
intNumberOfSets:       Integer;
strPlayer1ID:          string[50];
strPlayer2ID:          string[50];
strPlayer3ID:          string[50];
strPlayer4ID:          string[50];
strPlayer1Name:        string[50];
strPlayer2Name:        string[50];
strPlayer3Name:        string[50];
strPlayer4Name:        string[50];
intTournamentProgress: Integer;
intTableNumber:        Integer;
end;

C# 结构:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
    public struct structTiFuRecord
    {
        public int intDiscipline;
        public int intNumberOfSets;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
        public string strPlayer1ID;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
        public string strPlayer2ID;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
        public string strPlayer3ID;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
        public string strPlayer4ID;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
        public string strPlayer1Name;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
        public string strPlayer2Name;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
        public string strPlayer3Name;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
        public string strPlayer4Name;
        public int intTournamentProgress;
        public int intTableNumber;
    }

这是与此代码一起发送的 Delphi 记录:

IdTCPClient1.Connect;

Data.intDiscipline := 1;
Data.intNumberOfSets := 5;
Data.strPlayer1ID := 'Detlef';
Data.strPlayer2ID := 'Test';
Data.strPlayer3ID := 'Test';
Data.strPlayer4ID := 'Test';
Data.strPlayer1Name := 'Test';
Data.strPlayer2Name := 'Test';
Data.strPlayer3Name := 'Test';
Data.strPlayer4Name := 'Test';
Data.intTournamentProgress := 99;
Data.intTableNumber := 1;

networkHeaderRecord.intFlag := 1;
networkHeaderRecord.intPayload := SizeOf(TiFuRecord);
networkHeaderRecord.intPacketnumber := 1;

Buffer := RawToBytes(networkHeaderRecord, SizeOf(networkHeaderRecord));
IdTCPClient1.IOHandler.Write(Buffer);

Buffer := RawToBytes(TiFuRecord, SizeOf(TiFuRecord));
IdTCPClient1.IOHandler.Write(Buffer);

记录大小为 424 字节。这些正在发送。 在 C# 中(抱歉,代码只是用于测试 - 它闻起来很臭 ;-( 但因为它们只有 3-4 行应该没问题 - 希望如此)

byte[] arrHeader = new byte[intHeaderLength];

            networkStream.Read(arrHeader, 0, intHeaderLength);

            phReceive = new ProtocolHeader(arrHeader);

            byte[] testArr = new byte[phReceive.Payload];

            networkStream.Read(testArr, 0, (int)phReceive.Payload);

            test2 = (HelperClass.structTiFuRecord)ByteArrayToStructure(testArr, TiFuRecord, 0);

最后但同样重要的是,这里是编组字节数组的函数代码:

public static object ByteArrayToStructure(byte[] bytearray, object structureObj, int position)
    {
        int length = Marshal.SizeOf(structureObj);
        IntPtr ptr = Marshal.AllocHGlobal(length);
        Marshal.Copy(bytearray, 0, ptr, length);
        structureObj = Marshal.PtrToStructure(Marshal.UnsafeAddrOfPinnedArrayElement(bytearray, position), structureObj.GetType());
        Marshal.FreeHGlobal(ptr);
        return structureObj;
    } 

我非常接近一个好结果。对于开头的整数,值是正确的。但是第一个字符串已经是错误的,并且从他们那里滚雪球。我检查了发送 424 字节的记录的大小和我在 c# 中执行 marshal.sizeOf(struct) ==> 416 字节的结构。它们的大小不匹配。我认为字符串的大小是错误的。我尝试了几种不同的方法在 delphi 端进行套接字写入,也尝试了从这里的不同帖子更改 c# 结构。打包参数、布局等

感谢支持。非常感谢和最好的, 马菲尼奥

【问题讨论】:

  • IMO,这就是为什么我们有像 SOAP 和 JSON 这样的协议。如果您可以控制这两个应用程序,则应该认真考虑重新构建它。
  • 谢谢,但事实上我不会同时开发这两种应用程序......否则我可能不会混合开发语言。无论如何。感谢您的支持!

标签: c# sockets delphi marshalling


【解决方案1】:

碰巧 Delphi 记录没有填充,无论编译器设置如何。但要匹配 C# 代码,您应该将记录声明为 packed

type 
  Data = packed record  
    ....
  end;

8 字节的差异是由于 Delphi 短字符串具有长度字节前缀。您有 8 个字符串,并且缺少 8 个单字节长度前缀。为 C# 代码中的每个字符串声明它,一切都会好起来的。

所以要明确一点,string[50] 变量的大小都是 51。它们由一个包含长度的字节组成,然后是 50 个 8 位字符元素。与您的 Delphi 相匹配的 C# 如下所示:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct structTiFuRecord
{
    public int intDiscipline;

    public int intNumberOfSets;

    public byte strPlayer1IDlen;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
    public string strPlayer1ID;

    public byte strPlayer2IDlen;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
    public string strPlayer2ID;

    public byte strPlayer3IDlen;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
    public string strPlayer3ID;

    public byte strPlayer4IDlen;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
    public string strPlayer4ID;

    public byte strPlayer1Namelen;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
    public string strPlayer1Name;

    public byte strPlayer2Namelen;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
    public string strPlayer2Name;

    public byte strPlayer3Namelen;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
    public string strPlayer3Name;

    public byte strPlayer4Namelen;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
    public string strPlayer4Name;

    public int intTournamentProgress;

    public int intTableNumber;
}

在更广泛的层面上,您的方法忽略了字节顺序问题,并且非常脆弱,正如您所发现的那样。您使用短字符串会导致您使用 ASCII 或 ANSI 代码页,尽管后者要求两台机器使用相同的代码页。

考虑将此数据序列化为 JSON 对象,例如,以避免此类问题。

【讨论】:

  • 您好,谢谢您的评论!因为我错过了你的帖子,所以我完全修正了你写的东西。无论如何,非常感谢!关于 JSON 仍然是最后一个问题。由于字节序仅与整数有关,这个问题将得到解决——Delphi -> C# 要求我删除或添加(取决于发送方向)空终止,对吧?有没有专业的方法来解决这个问题?删除字符串的最后一个字节/字符或添加它对我来说似乎并不优雅。此外,我还能不会遇到关于编码的问题?通过网络发送字符串时?
  • 我不确定我是否完全理解该评论。我不认为 JSON 和 null 终止是一个问题。马上,您将停止使用短字符串,而只使用母语字符串。编码? UTF-8。至于字节序,它也适用于 16 位和 32 位字符元素,以及浮点类型。
  • 我想通过套接字交换 JSON 字符串。首先,不需要任何网络服务在线,并且客户端不需要在线交换数据。
  • 应该没问题
  • 但我还有一个问题……我现在才意识到。如果我想通过套接字发送 json 字符串,我需要确定大小。由于它可能不同,我需要发送一些标题信息下一个传入数据的大小,对吗?然后我回到(使用标头时)可能带有整数 ==> 字节序问题的标头数据包有什么聪明的主意吗?
猜你喜欢
  • 1970-01-01
  • 2013-03-23
  • 1970-01-01
  • 1970-01-01
  • 2023-03-15
  • 1970-01-01
  • 2010-12-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多