Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Struct.Pack Equivalent in C#?

Tags:

python

c#

I'm building a C# client that connects to a render application and failing dismally! I narrowed the problem down by dissecting a python client that does work to this line:

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

What would the equivalent be in C#? From my understanding it needs 8 bytes before the message.

like image 597
NigeC Avatar asked Oct 21 '22 23:10

NigeC


1 Answers

struct.pack takes a format followed by a series of values that will be packed according to the format. In your question you call:

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

Which is saying "Pack the length of this message as a little-endian long followed by a zero packed as a little-endian long followed by appending the rest of my message".

When you approach this kind of problem in C# we unfortunately do not have a direct port of struct.pack. Your closest equivalent would be to use either a BitConverter for one-off conversions like:

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

or use a BinaryWriter into a MemoryStream. This brings yet another problem though which is that you cannot control the endian-ness using these tools. They expose "IsLittleEndian" so you know how they are acting but you cannot alter it.

Jon Skeet however is on the case - his MiscUtils library contains a LittleEndianBitConverter (MiscUtil.Conversion.LittleEndianBitConverter) that you can use or an EndianBinaryWriter should you go the Writer/MemoryStream route. So putting it all together, reference the MiscUtil library and use something like:

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;
}

The code is untested but should give you a reasonable starting point. It's assuming that your message is already a byte array and that msg is the array you want to return. We use System.Buffer.BlockCopy as it's the most efficient copy method for primitive types.

* EDIT *

I've taken the example in the question and mocked up a quick script in IDEOne for the Python code and its equivalent in C#. The kicker here is that the Struct.Pack('<l', 0) call ignores the byte and does not add it to the output which may be what's tripping you up. This was causing the output to be 8 bytes too long.

Those scripts should point you in the right direction. If you're still having trouble can you post the code that you've tried.

For reference, the finished code in 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))

And in 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("-", ":"));
    }
}
like image 132
Wolfwyrd Avatar answered Oct 23 '22 18:10

Wolfwyrd