Passing Data Over Sockets

A

amdrit

I am facing an issue where I need to pass data between a client and server
using sockets. I want to minimize bloat in my objects being passed so I was
attempting to use structs. However, in the struct, there is a byte array
that should be variable in length. According to everything I read I must
use a MarshalAs attribute and set a constant size for the data. In my
scenario I would be passing a portion of a given file based on buffer size
in the byte array.

[StructLayout(LayoutKind.Sequential, Pack=1, CharSet=CharSet.Ansi)]
struct Message
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string BatchID;
public int MessageType;
[MarshalAs( UnmanagedType.ByValArray, SizeConst=?? )]
public byte[] data; //Holds the file fragment
}

[StructLayout(LayoutKind.Sequential, Pack=1, CharSet=CharSet.Ansi)]
struct FileFragment
{
public int PageNumber;
public int Offset;
public int Length;
[MarshalAs( UnmanagedType.ByValArray, SizeConst=?? )]
public byte[] Data; //(Buffer Size - len(Message) - Len(Other Fields in
FileFragment))
}

I plan to allow the buffer size to be configurable for bandwidth throttling
so FileFragment will be variable based on that. Plus if I reach the end of
the file and have fewer bytes than the buffer size, the array would be
smaller as well.

I could convert this to a class and use a binary formatter to bypass my
problem, but then I would have a lot of extra header information in the data
and feel that I would be supplanting my goal. I suppose I could create a
method to manually create a byte array from the structures and then manually
reconstruct the structures on the other side. However, I was under the
impression that I have to pin this data so GC wont attempt to collect the
data.


public byte[] ToByteArray(Message msg)
{
List<byte> data = new List<byte>();
byte[] dataBytes;
data.AddRange(System.Text.ASCIIEncoding.ASCII.GetBytes(msg.BatchID));
data.AddRange(BitConverter.GetBytes(msg.MessageType));
data.AddRange(msg.data);
dataBytes = data.ToArray();
return dataBytes;
}

public byte[] ToByteArray(FileFragment ff)
{
List<byte> data = new List<byte>();
byte[] dataBytes;
data.AddRange(BitConverter.GetBytes(ff.PageNumber));
data.AddRange(BitConverter.GetBytes(ff.Offset));
data.AddRange(BitConverter.GetBytes(ff.Length));
data.AddRange(ff.Data);
dataBytes = data.ToArray();
return dataBytes;
}

public Message FromByteArray(byte[] data)
{
Message msg = new Message();
msg.BatchID = System.Text.ASCIIEncoding.ASCII.GetString(data, 0, 32);
msg.MessageType = BitConverter.ToInt32(data, 32);
data.CopyTo(msg.data, 36);
return msg;
}

public FileFragment FromByteArray(Message msg)
{
FileFragment ff = new FileFragment();
ff.PageNumber = BitConverter.ToInt32(msg.data, 0);
ff.Offset = BitConverter.ToInt32(msg.data, 4);
ff.Length = BitConverter.ToInt32(msg.data, 8);
msg.data.CopyTo(ff.Data, 12);
return ff;
}


Do you have any recommendations about this issue?
 
I

Ignacio Machin ( .NET/ C# MVP )

Do you have any recommendations about this issue?


You do not need to marshall nothing, serialize the struct using the
binary serializer, get the size of the resulting stream and then you
send the size and then the serialized struct.
 
P

Pavel Minaev

I am facing an issue where I need to pass data between a client and server
using sockets.  I want to minimize bloat in my objects being passed so I was
attempting to use structs.  However, in the struct, there is a byte array
that should be variable in length.  According to everything I read I must
use a MarshalAs attribute and set a constant size for the data.  In my
scenario I would be passing a portion of a given file based on buffer size
in the byte array.

First of all, you should realise that MarshalAs is only useful for P/
Invoke and COM interop. It has no magical effect for a struct when
it's on a .NET heap or stack, and doesn't matter when you're manually
transmitting data via streams or sockets, as you seem to be doing. If
you're trying to fill the struct in memory, and then treat it directly
as a plain sequence of bytes for a Send call, then you should consider
byte order issues as well.

On the whole, what you seem to want is done in C# in precisely the
same way it's done in ISO-standard C & C++ - by declaring a struct
with a fixed 1-element array at the end, using unmanaged memory
allocation functions (or stackalloc, depending on what does the job
better) to allocate the struct of the correct actual size, and then
treat that 1-element array as if it had the appropriate number of
elements.

I suppose I could create a
method to manually create a byte array from the structures and then manually
reconstruct the structures on the other side.  However, I was under the
impression that I have to pin this data so GC wont attempt to collect the
data.

That's probably the best approach in this case (it's definitely the
most portable). And you do not need to pin anything here, as you
create a copy of the data, so whether the original data moves should
not concern you.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top