在C#中是否有类似于Struct.Pack的函数?

7

我正在构建一个连接到渲染应用程序的C#客户端,但进展非常缓慢!通过分析一个能够正常工作的Python客户端,我将问题缩小到了这一行代码:

def Startclient_Click(self, sender, e):
     try:
         s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         s.connect((host, int(port)))
         message =  b'message "Render"'
         msg = struct.pack('<l',len(message))+struct.pack('<l',0)+message
         #print(msg)
         s.sendall(msg)
         data = s.recv(1024)

         data.decode("utf-8")
         self.datatxt.Text ="data: " +str(data)
         s.close()

         return
     except:
         self.datatxt.Text ="No Server Connection"
         return

在C#中等价的是什么? 据我了解,在消息之前需要8个字节。


第一个参数告诉它如何打包,这可能是您最常见的问题...我认为('<l')表示最低有效位在最左边...但我很容易错。 - Joran Beasley
附加参考 - https://dev59.com/fUrSa4cB1Zd3GeqPXZGh - Wolfwyrd
1个回答

4

struct.pack函数需要传入一个格式以及一系列值,这些值将按照格式进行打包。在你的问题中,你调用了:

struct.pack('<l', len(message))+struct.pack('<l',0)+message

这段话的意思是:“将消息的长度打包为一个小端字节序的长整型,后跟一个打包为小端字节序的长整型的零,然后再附加我的消息的其余部分。”

在C#中解决这种问题时,我们不幸地没有直接移植struct.pack。最接近的等效方法是使用BitConverter进行一次性转换,例如:

BitConverter.GetBytes((long)message.length) + BitConverter.GetBytes(0l) + message

或者使用BinaryWriter写入MemoryStream,但这会带来另一个问题,即您无法使用这些工具控制字节序。它们公开了“IsLittleEndian”,因此您知道它们的操作方式,但无法更改它。然而,Jon Skeet正在处理此问题-他的MiscUtils库包含一个LittleEndianBitConverter(MiscUtil.Conversion.LittleEndianBitConverter),您可以使用它或一个EndianBinaryWriter,如果您选择Writer/MemoryStream路线。因此,将所有内容组合在一起,引用MiscUtil库并使用以下内容:
var bytes = new List<byte[]>(new[]
    {
        LittleEndianBitConverter.GetBytes(message.LongLength), 
        LittleEndianBitConverter.GetBytes(0l), 
        message
    });

var msg = new byte[bytes.Sum(barray => barray.LongLength)];
int offset = 0;
foreach (var bArray in bytes)
{
    System.Buffer.BlockCopy(bArray, 0, msg, offset, bArray.Length);
    offset = bArray.Length;
}

这段代码未经测试,但应该给你一个合理的起点。它假设您的消息已经是一个字节数组,msg 是您想要返回的数组。我们使用 System.Buffer.BlockCopy 作为最有效的原始类型复制方法。

* 编辑 *

我拿出问题中的示例,在 IDEOne 中模拟了一个快速脚本,分别展现了 Python 代码 和其在 C# 的等效代码 C#。这里的关键是 Struct.Pack('<l', 0) 调用忽略了字节并且没有将其添加到输出中,这可能会使您感到困扰。这导致输出长了8个字节。

这些脚本应该指引您朝着正确的方向前进。如果您仍有困难,请贴出您所尝试的代码。

Python 中的最终代码供参考:

import struct
message =  b'message "Render"'
msg = struct.pack('<l',len(message)) + struct.pack('<l',0) + message
print(":".join("{0:x}".format(ord(c)) for c in msg))

在C#中:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MiscUtil.Conversion;

public class Test
{
    public static void Main()
    {
        var message = Encoding.ASCII.GetBytes("message \"Render\"");

            var lenc = new LittleEndianBitConverter();

            var bytes = new List<byte[]>(new[]
            {
                lenc.GetBytes(message.LongLength),
                message
            });

            var msg = new byte[bytes.Sum(barray => barray.LongLength)];
            int offset = 0;
            foreach (var bArray in bytes)
            {
                Buffer.BlockCopy(bArray, 0, msg, offset, bArray.Length);
                offset = bArray.Length;
            }

            Console.WriteLine(BitConverter.ToString(msg).Replace("-", ":"));
    }
}

谢谢回复,我一有机会就尽快试试看 :) - NigeC
很遗憾,我仍然遇到问题。我已经更新了我的原始帖子,并提供了可工作的WPF IronPython版本。说实话,我认为这可能需要有人使用Thea渲染器测试C#版本,但我不指望任何人下载一个他们永远不会使用的100兆应用程序。至少我有Python版本可以用来进行一些原型设计,直到我找到答案 :) - NigeC
1
@NigeC,在C#中,(origsize, cmpsize, ) = unpack('<LL', catrec)的等效写法是什么? - Furqan Safdar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接