通过Socket将Delphi记录发送到C#

时间:2015-08-11 10:36:51

标签: c# sockets delphi marshalling

作为主题提到我尝试通过套接字将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#struct:

[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#struct。包装参数,布局等。

支持表示赞赏。非常感谢和最好, Mäfinho

1 个答案:

答案 0 :(得分: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对象,以避免此类问题。