So it's early and I always have anxiety over things like this, so I was hoping someone could take a quick look and make sure this would actually work.

I've got a socket program that will transmit a Data object over the wire. I just want to make sure that my bytes are in order and nothing obviously wrong is going on. I haven't gotten far enough into it to test it, I just wanted to head off any errors at the pass.

This is the method that converts a object of type Data to a single byte array to be transmitted:

/// <summary>
        /// Prepares a Data object for transmission over the wire.
        /// </summary>
        /// <param name="data">The Data object to prepare</param>
        /// <returns>A byte array to send over the wire</returns>
        public byte[] PrepareForWire(Data data)
        {
            using (MemoryStream DataArray = new MemoryStream())
            {
                // convert the data to byte arrays
                byte[] dIsSecret = BitConverter.GetBytes(data.DIsSecret);
                byte[] dCommand = BitConverter.GetBytes((int)data.DCommand);

                // write the arrays to the memory stream
                //skip the first 4 bytes, this is where the message length goes
                DataArray.Seek(4, 0);
                DataArray.Write(dCommand, 0, 4);
                DataArray.Write(dIsSecret, 0, 1);
                DataArray.Write(data.DMessage, 0, data.DMessage.Length);
                
                // find the length of our message
                int TransmissionLength = DataArray.ToArray().Length;

                // write the length of the message minus 4 bytes to the start of the memory stream
                DataArray.Seek(0, 0);
                DataArray.Write(BitConverter.GetBytes(TransmissionLength - 4), 0, 4);

                return DataArray.ToArray();
            }
        }

And here is a Data object and it's constructors:

/// <summary>
    /// Data object
    /// </summary>
    public class Data
    {
        /// <summary>
        /// The command requested
        /// </summary>
        public Command DCommand;

        /// <summary>
        /// Flag indicating if the message is secret or not
        /// </summary>
        public bool DIsSecret;

        /// <summary>
        /// The message
        /// </summary>
        public byte[] DMessage;

        /// <summary>
        /// Creates a new Data object
        /// </summary>
        public Data()
        {
            DCommand = Command.GoodBye;
            DIsSecret = true;
        }

        /// <summary>
        /// Creates a new Data object
        /// </summary>
        /// <param name="dCommand">Command to perform</param>
        /// <param name="dMessage">The message</param>
        public Data(Command dCommand, byte[] dMessage)
        {
            DCommand = dCommand;
            DIsSecret = true;
            DMessage = dMessage;
        }

        /// <summary>
        /// Creates a new Data object
        /// </summary>
        /// <param name="dCommand">Command to perform</param>
        /// <param name="dIsSecret">Flag indicating if the message is secret or not</param>
        /// <param name="dMessage">The message</param>
        public Data(Command dCommand, bool dIsSecret, byte[] dMessage)
        {
            DCommand = dCommand;
            DIsSecret = dIsSecret;
            DMessage = dMessage;
        }

The array that the method returns should look like this:

| Position  | Length    | Data Type | Description                                                  |
----------------------------------------------------------------------------------------------------
| 0         | 4         | Int32     | The length of the following message                          |
| 4         | 4         | Int32     | The command to perform (See: Command Enumeration)            |
| 8         | 1         | bool      | Flag indicating if the receiver should use his private key   |
|           |           |           |   when reading the message                                   |
| 9         | Varies    | string    | If the IsSecret flag is "False" this is unicode text.        |
|           |           |           |   If the IsSecret flag is "True" this is an encrypted object,|
|           |           |           |   the type of which is determined by the command.            |
----------------------------------------------------------------------------------------------------

Normally I'd see this done with serialization in the C# world, this looks like C code :) The only issue I have is the hard coded lengths in lines 17/18, but you have to have them for something like this.

Momerath,

Actually I guess I don't really HAVE to hard code them. I could use the bit converter for them as well, but I figured it was an unnecessary call. I always figured that when it came to a communication protocol (of any kind, network or between threads etc...) it's better to be "tough" and have a tight leash on things. One extra byte or two could accidentally change the entire meaning of the communication because someone didn't follow the protocol correctly.

Course I guess the same could happen here, but I dunno.

One thing that occurs to me now though is that if I need to modify the protocol for some reason (add something or remove something) or maybe change the type of data like from bool to an integer, I would have to go back and change the lengths I hard-coded by hand. If I used the bit converter and found the byte length I wouldn't have to do that, I would just have to modify the methods that encode and decode the stream.

Hmmm...Actually I would have to do that anyway, regardless of the setup...I think.

Wow, anyway, so no glaring issues? Seems like it would encode in the correct order?

I must say I did actually consider serialization, but I was worried that if I serialized the entire class that the recipient, if it was running a slightly older or newer version might run into major issues and they might not be able to communicate at all.

Is that a valid concern? Or am I just too paranoid? This whole application development gig is going to kill me! I just know it!

Momerath,

Were you talking about something like this:

/// <summary>
        /// Prepares a Data object for transmission over the wire.
        /// </summary>
        /// <param name="data">The Data object to prepare</param>
        /// <returns>A byte array to send over the wire</returns>
        public byte[] PrepareForWire(Data data)
        {
            using (MemoryStream DataArray = new MemoryStream())
            {
                BinaryFormatter DataSerializer = new BinaryFormatter();
                DataSerializer.Serialize(DataArray, data);

                return DataArray.ToArray();
            }
        }

Does that cause the size of the array to become bloated?

Looks like that will work. Instead of using hardcoded values, maybe consider using the sizeof(int/etc) keyword. It will give you the size of a datatype relative to the current system architecture.

This got me to thinking...there must be a way to make a generic class serializer that isn't as bloated as using the built in using System.Runtime.Serialization.Formatters.Binary and flagging a class [Serializable]

After a quick google search I found some interesting little methods in the System.Reflection library. For example:

public static Dictionary<string, object> GetFields(object ob)
        {
            Dictionary<string, object> rData = new Dictionary<string, object>();
            Type objectType = ob.GetType();

            foreach (System.Reflection.FieldInfo mInfo in objectType.GetFields())
                rData.Add(mInfo.Name, mInfo.GetValue(ob));

            return rData;
        }

I think I might keep dablling away at this until I have a useful serialization class. It's kind of reinventing the wheel, but it's a good learning exercise to understand how serialization works.

Edited 5 Years Ago by skatamatic: n/a

This article has been dead for over six months. Start a new discussion instead.