Transmitting complex objects using TCP - c#

I have a client server application in which I need to transmit a user defined object from Client to Server using TCP connection. My object is of the following structure:
class Conversation
{
private string convName, convOwner;
public ArrayList convUsers;
public string getConvName()
{
return this.convName;
}
public string getConvOwner()
{
return this.convOwner;
}
}
Please help me how to transmit this object at from client and again de-serialize it into appropriate object at server side.

As answered, you should make your object serializable. Once you did that with the Serializable attribute, you can use the famous BinaryFormatter to convert your object into a byte array.
You can find many examples out there for using the BinaryFormatter, just use your favorite search engine. Here's a short example:
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
public class SerializationUtils
{
public static byte[] SerializeToByteArray(object request)
{
byte[] result;
BinaryFormatter serializer = new BinaryFormatter();
using (MemoryStream memStream = new MemoryStream())
{
serializer.Serialize(memStream, request);
result = memStream.GetBuffer();
}
return result;
}
public static T DeserializeFromByteArray<T>(byte[] buffer)
{
BinaryFormatter deserializer = new BinaryFormatter();
using (MemoryStream memStream = new MemoryStream(buffer))
{
object newobj = deserializer.Deserialize(memStream);
return (T)newobj;
}
}
}
As for your class, it includes two private fields. I can't see where you set values for them, so I changed your code a bit, so that they can be set in the constructor. In addition, I added the needed Serializable attribute:
using System;
using System.Collections;
[Serializable]
public class Conversation
{
public Conversation(string convName, string convOwner)
{
this.convName = convName;
this.convOwner = convOwner;
}
public Conversation()
{
}
private string convName, convOwner;
public ArrayList convUsers;
public string getConvName()
{
return this.convName;
}
public string getConvOwner()
{
return this.convOwner;
}
}
Now let's put it all together, and see your class serialized and then deserialized, in a Console Application:
using System;
using System.Collections;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace Capishi
{
[Serializable]
public class Conversation
{
public Conversation(string convName, string convOwner)
{
this.convName = convName;
this.convOwner = convOwner;
}
public Conversation()
{
}
private string convName, convOwner;
public ArrayList convUsers;
public string getConvName()
{
return this.convName;
}
public string getConvOwner()
{
return this.convOwner;
}
}
public class SerializationUtils
{
public static byte[] SerializeToByteArray(object request)
{
byte[] result;
BinaryFormatter serializer = new BinaryFormatter();
using (MemoryStream memStream = new MemoryStream())
{
serializer.Serialize(memStream, request);
result = memStream.GetBuffer();
}
return result;
}
public static T DeserializeFromByteArray<T>(byte[] buffer)
{
BinaryFormatter deserializer = new BinaryFormatter();
using (MemoryStream memStream = new MemoryStream(buffer))
{
object newobj = deserializer.Deserialize(memStream);
return (T)newobj;
}
}
}
class Program
{
static void Main(string[] args)
{
// create and initialize a conversation object
var convName = "Capishi";
var convOwner = "Ice Cream";
Conversation myConversation = new Conversation(convName, convOwner);
myConversation.convUsers = new ArrayList();
myConversation.convUsers.Add("Ron Klein");
myConversation.convUsers.Add("Rakesh K");
// serialize to a byte array
byte[] data = SerializationUtils.SerializeToByteArray(myConversation);
// print the resulting byte array if you want
// PrintArray(data);
// deserialize the object (on the other side of the communication
Conversation otherConversation = SerializationUtils.DeserializeFromByteArray<Conversation>(data);
// let's see if all of the members are really there
Console.WriteLine("*** start output ***");
Console.WriteLine("otherConversation.getConvName() = " + otherConversation.getConvName());
Console.WriteLine("otherConversation.getConvOwner() = " + otherConversation.getConvOwner());
Console.WriteLine("otherConversation.convUsers:");
foreach (object item in otherConversation.convUsers)
{
Console.WriteLine(item);
}
Console.WriteLine("*** done output ***");
// wait before close
Console.ReadLine();
}
/// <summary>
/// just a helper function to dump an array to the console's output
/// </summary>
/// <param name="data"></param>
private static void PrintArray(byte[] data)
{
for (int i = 0; i < data.Length; i++)
{
Console.Write("{0:000}", data[i]);
if (i < data.Length - 1)
Console.Write(", ");
}
Console.WriteLine();
}
}
}
The result is:
*** start output ***
otherConversation.getConvName() = Capishi
otherConversation.getConvOwner() = Ice Cream
otherConversation.convUsers:
Ron Klein
Rakesh K
*** done output ***
And a final note:
I'd use the generic List instead of the outdated ArrayList, unless you're bound to .NET 1.*.

One good course of action is to expose this object as a DataContract to a framework like WCF, and use the appropriate transports available in that framework.
For example:
[DataContract]
class Conversation
{
private string convName, convOwner;
public ArrayList convUsers;
[DataMember]
public string ConvName
{
get { return this.convName; }
}
[DataMember]
public string ConvOwner
{
get { return this.convOwner; }
}
}

You need to make your object serializable.

Related

A constructor that takes object from same type

So I want to create constructor for my class EmployeeNodeClass that takes In EmployeeNodeClass object and copies it using a deepclone funtion:
public static T DeepClone<T>(T obj)
{
using (var ms = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(ms, obj);
ms.Position = 0;
return (T)formatter.Deserialize(ms);
}
}
to the new object.
At first i thought that it is as simple as
public EmployeeNodeClass(EmployeeNodeClass EMPND)
{
this = DeepClone(EMPND);
}
but then I got the error that this is readonly.
so how can I do it?
It can be done using NewtonSoft JsonConvert.PopulateObject:
Example:
class Entity
{
public List<int> List { get; set; }
public Entity(List<int> list)
{
List = list;
}
public Entity(Entity original)
{
string originalJson = JsonConvert.SerializeObject(original);
JsonConvert.PopulateObject(originalJson, this);
}
}
And using it:
static void Main(string[] args)
{
var entity1 = new Entity(new List<int> { 1, 2, 3 });
var entity2 = new Entity(entity1);
entity1.List[0] = 5;
Console.WriteLine(entity2.List[0]);
}
Note: since this uses NewtonSoft Json, it will only clone public, writable properties. So internal state in private fields (that are not associated with such properties) will be lost.

C# comfortable way to construct byte array from different objects

I am looking for a way to fast and simple implementation of this paradigm:
MyByteArray mb = new MyByteArray();
mb.Add<byte>(bytevalue);
mb.Add<float>(floatvalue);
mb.Add<string>(str);
mb.Add<MyClass>(object);
And then get byte[] from mb to send it as a byte packet via RPC call (to be decoded on the other side using the same technique).
I've found MemoryStream, but it looks like too overheaded for this simple operation.
Can you help me? Thank you.
What are you looking for is BinaryWritter. But it still needs a Stream to write on for pure logic reason. And the only Stream that fits in your need is MemoryStream.
Are you afraid of performance overhead ? You can create your MemoryStream from an existing byte array ;
byte [] buffer = new byte[1024];
using (var memoryStream = new MemoryStream(buffer))
{
using (var binaryWriter = new BinaryWriter(memoryStream))
{
binaryWriter.Write(1.2F); // float
binaryWriter.Write(1.9D); // double
binaryWriter.Write(1); // integer
binaryWriter.Write("str"); // string
}
}
// buffer is filled with your data now.
A tricky way to achieve this is to use a combination of builtin class in .net
class Program
{
static void Main(string[] args)
{
Program program = new Program();
var listBytes = new List<byte>();
listBytes.Add( program.CastToBytes("test"));
listBytes.Add(program.CastToBytes(5));
}
Note
for a custom object you have to define an implicit operator on how the properties or all the object should be converted
public byte[] CastToBytes<T>(T value)
{
//this will cover most of primitive types
if (typeof(T).IsValueType)
{
return BitConverter.GetBytes((dynamic)value);
}
if (typeof(T) == typeof(string))
{
return Encoding.UTF8.GetBytes((dynamic) value);
}
//for a custom object you have to define the rules
else
{
var formatter = new BinaryFormatter();
var memoryStream = new MemoryStream();
formatter.Serialize(memoryStream, value);
return memoryStream.GetBuffer();
}
}
}
This looks like the case for Protocol Buffers, you could look at at protobuf-net.
First, let's decorate the classes.
[ProtoContract]
class User
{
[ProtoMember(1)]
public int Id { get; set; }
[ProtoMember(2)]
public string Name { get; set; }
}
[ProtoContract]
class Message
{
[ProtoMember(1)]
public byte Type { get; set; }
[ProtoMember(2)]
public float Value { get; set; }
[ProtoMember(3)]
public User Sender { get; set; }
}
Then we create our message.
var msg = new Message
{
Type = 1,
Value = 1.1f,
Sender = new User
{
Id = 8,
Name = "user"
}
};
And now, we can use ProtoBuf's serializer to do all our work.
// memory
using (var mem = new MemoryStream())
{
Serializer.Serialize<Message>(mem, msg);
var bytes = mem.GetBuffer();
}
// file
using (var file = File.Create("message.bin")) Serializer.Serialize<Message>(file, msg);

Persisting MemoryCache Content to File

I'm intending to use the powerful caching libraries that are introduced in .Net 4.0 in a Windows Forms application. So far, MemoryCache does everything I need except for persisting its contents to a file. What I'm trying to do is persisting the cache to a file on application exit, and then when the application is opened again, I should be able to write from the file and put its contents in the MemoryCache. I know that I can simply serialize the instance into binary on disk, but then again, I wouldn't know how to convert it back to a *MemoryCache*.
Your help is much appreciated.
I ended up implementing my own Cache project. Hopefully this will help someone somewhere:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
namespace CachingDemo
{
class CachedMemory
{
System.Collections.Specialized.OrderedDictionary cache = null;
private String persistenceFilePath = null;
private int cacheSizeLimit;
public static readonly int CACHE_SIZE_NO_LIMIT = -1;
public CachedMemory(int initialCapacity, int cacheSizeLimit, string persistenceFilePath)
{
this.cache = new System.Collections.Specialized.OrderedDictionary(initialCapacity);
this.persistenceFilePath = persistenceFilePath;
this.cacheSizeLimit = cacheSizeLimit;
}
public int getCacheSize()
{
return this.cache.Count;
}
public CachedMemory(int cacheSizeLimit, string cacheFilePath)
{
initializeCache(cacheFilePath, cacheSizeLimit);
}
private void initializeCache(string cacheFilePath, int cacheSizeLimit)
{
this.cacheSizeLimit = cacheSizeLimit;
using (FileStream fileStream = new FileStream(cacheFilePath, FileMode.Open))
{
IFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
this.cache = (System.Collections.Specialized.OrderedDictionary)bf.Deserialize(fileStream);
fileStream.Close();
}
//In case the deserialized OrderedDictionary had more contents than the limit, we need to shrink it to make its size equal to the limit
if (this.cacheSizeLimit != CACHE_SIZE_NO_LIMIT && this.cache.Keys.Count > this.cacheSizeLimit)
{
int difference = this.cache.Keys.Count - this.cacheSizeLimit;
for (int i = 0; i < difference; i++)
{
cache.RemoveAt(0);
}
}
}
public string get(string key)
{
return cache[key] as string;
}
public string get(int index)
{
return cache[index] as string;
}
public void add(string key, string value)
{
//An ordered dictionary would throw an exception if we try to insert the same key again, so we have to make sure that the newly
//introduced key is not a duplicate.
if (this.cache.Contains(key))
{
this.cache.Remove(key);
}
else
if (this.cacheSizeLimit != CACHE_SIZE_NO_LIMIT && this.cache.Count == this.cacheSizeLimit)
{
this.cache.RemoveAt(0);
}
this.cache.Add(key, value);
}
public void persist()
{
using (FileStream fileStream = new FileStream(persistenceFilePath, FileMode.Create))
{
IFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
bf.Serialize(fileStream, this.cache);
fileStream.Close();
}
}
}
}

Serializing Arrays in C#

Is there a way to Serialize an entire array in C# such as:
[Serializable()]
public class Data
{
public short Some_Short_Data = new short[100,100];
public string Some_String_Data = new string[100,100];
}
Then writting the file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
Data Write_Data = new Data();
XmlSerializer Writer = new XmlSerializer(typeof(Data));
using (FileStream file = File.OpenWrite("Myfile.xml"))
{
Writer.Serialize(file, Write_Data); //Writes data to the file
}
When I try this, it fails on:
XmlSerializer Writer = new XmlSerializer(typeof(Data));
Saying: "There was an error reflecting type 'Data' "
I am particularly new to both Stack and Xml/Xml Serialization so any help would be appreciated.
Multi-dimensional arrays are not supported by XmlSerialzier. But you can make a workaround by using a temp class something like this
public class Array100<T>
{
public T[] Data = new T[100];
}
public class Data
{
public Array100<short>[] Some_Short_Data = new Array100<short>[100];
public Array100<string>[] Some_String_Data = new Array100<string>[100];
}
BTW: No need for Serializable attribute. It is not used by XmlSerialzier
You cannot serialze a int[,] but you can serialize a int[][]. Before serializing your 2D array, just convert it into a jagged array like that :
var my2dArray = new int[2,5];
var myJaggedArray = new int [2][];
for(int i = 0 ; i < my2DArray.GetLength(0) ; i ++)
{
myJaggedArray[i] = new int[my2DArray.GetLength(1)];
for(int j = 0 ; j < my2DArray.GetLength(1) ; j ++)
myJaggedArray[i][j] = my2DArray[i,j];
}
I ran into this issue when attempting to serialize a multi-dimensional array that was one of the values in a dictionary of objects. The approach I took was to wrap all of the arrays in a class that is serializable before serialization and then unwrap them all afterwards.
If you don't care what the data looks like then you can serialize the object using the binary formatter, which can serialize anything marked as serializable, and then persist the binary data as a base 64 encoded string.
The wrapper implementation is below.
[Serializable]
public class UnserializableValueWrapper: IXmlSerializable
{
private static readonly BinaryFormatter Formatter = new BinaryFormatter();
public UnserializableValueWrapper([NotNull] object value)
{
if (value == null)
{
throw new ArgumentNullException("value");
}
this.Value = value;
}
public UnserializableValueWrapper()
{
}
public object Value { get; private set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
reader.ReadStartElement();
var length = int.Parse(reader.GetAttribute("length"));
reader.ReadStartElement("Data");
var buffer = new byte[length];
reader.ReadContentAsBase64(buffer, 0, length);
using (var stream = new MemoryStream(buffer))
{
this.Value = Formatter.Deserialize(stream);
}
reader.ReadEndElement();
reader.ReadEndElement();
}
public void WriteXml(XmlWriter writer)
{
writer.WriteStartElement("Data");
using (var stream = new MemoryStream())
{
Formatter.Serialize(stream, this.Value);
var buffer = stream.ToArray();
writer.WriteAttributeString("length", buffer.Length.ToString());
writer.WriteBase64(buffer, 0, buffer.Length);
}
writer.WriteEndElement();
}
}

deserialize "long" string with protobuf for c# doesn't work properly for me

I'm obviously doing something basic wrong but I can't figure it out and don't find documentation.
I'm experimenting with proto-buf for .NET by Marc Gravell, and trying to serialize and deserialize objects. Once an object contains a string which is "too long" (didn't try to pinpoint the size threshold but it's few hundred bytes) the string doesn't deserialize properly for me.
This is my code:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using ProtoBuf;
namespace ConsoleApplication1
{
public class Program
{
[ProtoContract]
public class test
{
[ProtoMember(1)]
public int i;
[ProtoMember(2)]
public string s1;
[ProtoMember(3)]
public string s2;
[ProtoMember(4)]
public char[] arrchars;
[ProtoMember(5)]
public Dictionary<int, string> Dict = new Dictionary<int, string>();
}
static void Main(string[] args)
{
test var1 = new test();
var1.i = 10;
var1.s1 = "Hello";
var1.arrchars = new char[] {'A', 'B', 'C'};
var1.Dict.Add(10, "ten");
var1.Dict.Add(5, "five");
var1.s2 = new String('X', 520);
string s = PBSerializer.Serialize(typeof (test), var1);
test var2 = null;
PBSerializer.Deserialize(s, out var2);
}
public static class PBSerializer
{
public static string Serialize(Type objType, object obj)
{
MemoryStream stream = new MemoryStream();
ProtoBuf.Serializer.Serialize(stream, obj);
// ProtoBuf.Serializer.SerializeWithLengthPrefix(stream, obj, PrefixStyle.Fixed32, 1);
stream.Flush();
stream.Position = 0;
StreamReader sr = new StreamReader(stream);
string res = sr.ReadToEnd();
stream.Dispose();
sr.Dispose();
return res;
}
public static void Deserialize(string serializedObj, out test obj)
{
MemoryStream stream = new MemoryStream(Encoding.ASCII.GetBytes(serializedObj));
obj = ProtoBuf.Serializer.Deserialize<test>(stream);
// obj = ProtoBuf.Serializer.DeserializeWithLengthPrefix<test>(stream, PrefixStyle.Fixed32, 1);
stream.Dispose();
}
}
}
}
var2.s2 is not identical to var1.s2 - it has an extra character at the beginning of the string and truncates the majority of the end of the string. If however, I change the length of var1.s2 to a small number (say 52 instead of 520 chars) my problem goes away but I need to be able to serialize long strings.
I assume it has to do with something I do wrong with the setting the PrefixStyle (?) or perhaps I'm not using the right encoding (?). However, trial and error didn't help me sort it out.
I'm using .NET 3.5 and tried it with versions 444 & 450 with the same result.
Thanks.
You're serializing binary data - but then trying to read it as if it were text. It's not - so don't do that.
If you have to turn arbitrary binary data into text, use Convert.ToBase64String and Convert.FromBase64String.
public static class PBSerializer
{
public static string Serialize(Type objType, object obj)
{
using (MemoryStream stream = new MemoryStream())
{
ProtoBuf.Serializer.Serialize(stream, obj);
return Convert.ToBase64String(stream.ToArray());
}
}
// Ideally change this to use a return value instead of an out parameter...
public static void Deserialize(string serializedObj, out test obj)
{
byte[] data = Convert.FromBase64String(serializedObj);
using (MemoryStream stream = new MemoryStream(data))
{
obj = ProtoBuf.Serializer.Deserialize<test>(stream);
}
}

Categories

Resources