C# 字节数组中的封装包

C# 字节数组中的封装包,c#,arrays,sockets,tcp,C#,Arrays,Sockets,Tcp,我正在为需要使用CIP协议通过TCP/IP进行通信的应用程序构建一个C#库。(这是一个工业协议,由PLC使用)。我的库基于一个开源VB.net项目 我在微软开发者网络上找到了很多信息来设置套接字。下一步是注册会话,因此我需要使用Socket.receive方法来接收信息。同样,还有很多关于开发者网络的有用信息 我正在构建标题,如下所示: private string Build_Header(byte[] Command, int Length) //Build the encapsulate

我正在为需要使用CIP协议通过TCP/IP进行通信的应用程序构建一个C#库。(这是一个工业协议,由PLC使用)。我的库基于一个开源VB.net项目

我在微软开发者网络上找到了很多信息来设置套接字。下一步是注册会话,因此我需要使用Socket.receive方法来接收信息。同样,还有很多关于开发者网络的有用信息

我正在构建标题,如下所示:

private string Build_Header(byte[] Command, int Length) //Build the encapsulate message header. The header is 24 bytes fixed length and includes the command and the length of the optional data portion
    {
        string Header;
        byte[] HeaderStatus = { 0x0, 0x0, 0x0, 0x0 };
        byte[] HeaderOption = { 0x0, 0x0, 0x0, 0x0 };

        try
        {
            Header = Encoding.Unicode.GetString(Command);
            Header += Encoding.Unicode.GetString(BitConverter.GetBytes(Length));
            Header += Encoding.Unicode.GetString(BitConverter.GetBytes(SessionID));
            Header += Encoding.Unicode.GetString(HeaderStatus);
            Header += Encoding.Unicode.GetString(BitConverter.GetBytes(Context));
            Header += Encoding.Unicode.GetString(HeaderOption);

            return Header;
        }
        catch (Exception ex)
        {
            Debug.WriteLine("Failed to create header: {0}", ex);
            return "FAILED";
        }
    }
Witch是VB函数的副本:

Private Function _build_header(Command As Byte(), length As Short)
    'Build the encapsulate message header
    'The header is 24 bytes fixed length, and includes the command and the length of the optional data portion.
    Dim header As String
    Try
        header = Encoding.Unicode.GetString(Command)                             '# Command UINT
        header += Encoding.Unicode.GetString(BitConverter.GetBytes(length))      '# Length UINT
        header += Encoding.Unicode.GetString(BitConverter.GetBytes(_session))    '# Session Handle UDINT
        header += Encoding.Unicode.GetString({&H0, &H0, &H0, &H0})               '# Status UDINT
        header += Encoding.Unicode.GetString(BitConverter.GetBytes(_Context))    '# Sender Context 8 bytes
        header += Encoding.Unicode.GetString({&H0, &H0, &H0, &H0})               '# Option UDINT
        'If Not DisableLogFile Then Console.WriteLine("Header created: " + Bytes_To_String(Encoding.Unicode.GetBytes(header)) + "  , length: " _
        '                    + header.Length.ToString + "   (must be equal to 24 bytes)")
        Return header
    Catch ex As Exception
        Console.WriteLine("Failed to create header: " + ex.Message)
        Return ""
    End Try
End Function
但我收到一条错误消息,状态为“Unsupported encapsulation protocol revision”(不受支持的封装协议修订版)。因此我开始一行接一行地调试,并发现以下差异:

原件(VB):

副本(C#):


我不确定这是否是主要问题,但我至少应该是我猜的相同格式;从哪里开始。K:坦率地说,你复制的VB参考资料是垃圾。这是100%的错误,现在一切都是偶然的。首先,该数据不是文本,而是二进制帧。因此,不能返回
字符串
。返回一个
字节[]
是合理的。因此,最重要的问题可能不是“我应该如何获得这个字符串”,而是“一旦我拥有这个字符串,我将如何将它写入套接字”?您的问题中没有显示该代码,但我很高兴地打赌,将字符串写入套接字的方式是两个版本之间的关键区别

但是:首先你不应该处理字符串。假设该方法返回一个
字节[]
。然后我们有:

byte[] header = BuildHeader(command, length);
现在只有一种方法可以将
字节[]
写入流(除非我们非常直观):

(或涉及
流的类似变体

现在;
BuildHeader
应该做的是:

byte[] header = new byte[24];
// .. fill in
return header;
至于这些部分应该是什么:这取决于经纬度;看起来,
UINT
是2个字节,
UDINT
是4个字节。因此,以写入
长度
为例(第二个字段,2个字节,偏移量2个字节),即:

不幸的是,
BitConverter.GetBytes
不能帮助您打包缓冲区,因为a:它不允许您传入缓冲区和偏移量,b:endianness由您的CPU定义(我们需要它是一个特定的endianness)。如果VB代码正常工作,它可能是little endian(除非您有安腾),因此第一个版本(little end first)应该是正确的


对所有字段重复该操作,您将获得一个有效的标题。

这是一个骗局;两个字符串包含相同的数据。它的显示完全不同。如果您收到协议错误,则您的客户端和服务器在发送和接收内容上不一致。是否有方法可以获取相同格式的两个字符串,以便检查内容是否完全相同?@Belekz使用即时窗口,只需输入
标题
,它将显示实际的(未扫描)value@Belekz他错了;我已经找到了规范,无论是谁编写了VB代码,都完全误解了二进制编码协议。事实上,它现在可以正常工作完全是偶然的-他们很幸运编码器没有损坏数据。他们做得非常错误。这不是很好的代码复制,即使它可以工作。我是作为一个有着丰富实施二进制网络协议经验的人说这番话的。让我们100%清楚:此方法不应返回
string
。此数据不是文本。协议要求的是:字节数组。它有一个固定的长度,所以应该不会太难。啊,更新:我在规范中发现了这一点:“封装消息中的多字节整数字段应以小端字节顺序传输。”-所以,是的,我是对的-小端字节是什么意思
(byte)(length>>8)
?@Belekz“right shift”;我们试图将16位数据打包成2个字节;
(字节)长度
取最低的8位,只删除其余的(假设您没有启用
选中的
模式)-因此我们假设
长度
814
;(大端)二进制,即
00000011 00101110
(字节)长度为
00101110
(46为十进制),我们将其插入第一个字节
>>8
表示“将所有位向右移动8位”(根据需要删除任何位)-留给我们
00000011
(3为十进制)。814的小端编码是(小数):46,3@Belekz值得使用short而不是int来表示“length”,因为它在您的协议中有2个字节(因此int范围比您需要的要大得多)。@Belekz有三个选项:1:您分配一个您想要的大小的第二个缓冲区,只需两次调用即可发送/写入;2:您在开始时分配一个适合两者的缓冲区,并将其传递到两种方法中,以便它们添加位;3:你的缓冲池变得非常复杂,所以你从不分配任何东西——这是最核心的:)
byte[] header = new byte[24];
// .. fill in
return header;
header[2] = (byte)length;
header[3] = (byte)(length >> 8);
header[2] = (byte)(length >> 8);
header[3] = (byte)length;