Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Serializing object ready to send over TCPClient Stream

I've got a server and client set up using TcpListener and TcpClient.

I want to send an object to my server application for processing.

I've discovered the using System.Runtime.Serialization and the following documentation, but I didn't want to faff around to find that I'm doing it in long winded way.

The question: What is the best way to process and send an object over the TCP stream?

Sending and receiving.

Here's an example of my object:

// Create a new house to send
house newHouse = new house();

// Set variables
newHouse.street = "Mill Lane";
newHouse.postcode = "LO1 BT5";
newHouse.house_number = 11;
newHouse.house_id = 1;
newHouse.house_town = "London";
like image 509
Luke Avatar asked Oct 21 '11 12:10

Luke


3 Answers

Assuming you have a class House (available on both sides of your connection) looking like this:

[Serializable]
public class House
{
    public string Street { get; set; }
    public string ZipCode { get; set; }
    public int Number { get; set; }
    public int Id { get; set; }
    public string Town { get; set; }
}

You can serialize the class into a MemoryStream. You can then use in your TcpClient connection like this:

// Create a new house to send house and set values.
var newHouse = new House
    {
        Street = "Mill Lane", 
        ZipCode = "LO1 BT5", 
        Number = 11, 
        Id = 1, 
        Town = "London"
    };  

var xmlSerializer = new XmlSerializer(typeof(House));
var networkStream = tcpClient.GetStream();
if (networkStream.CanWrite)
{
    xmlSerializer.Serialize(networkStream, newHouse);
}

Of course you have to do a little more investigation to make the program running without exception. (e.g. Check memoryStream.Length not to be greater than an int, a.s.o.), but I hope I gave you the right suggestions to help you on your way ;-)

like image 179
Fischermaen Avatar answered Sep 19 '22 14:09

Fischermaen


First create a empty ServerApplication and ClientApplication as Console Application to simplify the example.

Then, put the definition for the serializable object into a separate assembly and then add a reference to the shared assembly to each project (server and client). Is necesary share the same object, not just an identical class copy.

To Generate DLL > Right clic in Solution 'ServerApplication' in the Solution Explorer > Add New Project... -> select Class Library (e.g. name this project MySharedHouse) Rename the default Class1 to House and complete it

[Serializable]
public class House
{
    public string Street { get; set; }
    public string ZipCode { get; set; }
    public int Number { get; set; }
    public int Id { get; set; }
    public string Town { get; set; }
}

enter image description here

Right clic in MySharedHouse and Build.

Now the dll is build and we need to add it in Server Project and Client Project. Right clic in ServerApplication > Add Reference > Browse and find the dll, for this example

Projects\ServerApplication\MySharedHouse\bin\Debug\MySharedHouse.dll

Repeat the process in ClientApplication using the same dll (same path).

Now you can use instances of House class in ServerApplication and ClientApplication as a single object, simply adding the sentence "using MySharedHouse" at the top.

SERVER CODE

using System;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;
using MySharedHouse;

namespace ServerApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            MessageServer s = new MessageServer(515);
            s.Start();
        }
    }

    public class MessageServer
    {
        private int _port;
        private TcpListener _tcpListener;
        private bool _running;
        private TcpClient connectedTcpClient;
        private BinaryFormatter _bFormatter;
        private Thread _connectionThread;

        public MessageServer(int port)
        {
            this._port = port;
            this._tcpListener = new TcpListener(IPAddress.Loopback, port);
            this._bFormatter = new BinaryFormatter();
        }

        public void Start()
        {
            if (!_running)
            {
                this._tcpListener.Start();
                Console.WriteLine("Waiting for a connection... ");
                this._running = true;
                this._connectionThread = new Thread
                    (new ThreadStart(ListenForClientConnections));
                this._connectionThread.Start();
            }
        }

        public void Stop()
        {
            if (this._running)
            {
                this._tcpListener.Stop();
                this._running = false;
            }
        }

        private void ListenForClientConnections()
        {
            while (this._running)
            {
                this.connectedTcpClient = this._tcpListener.AcceptTcpClient();
                Console.WriteLine("Connected!");
                House house = new House();
                house.Street = "Evergreen Terrace";
                house.ZipCode = "71474";
                house.Number = 742;
                house.Id = 34527;
                house.Town = "Springfield";
                _bFormatter.Serialize(this.connectedTcpClient.GetStream(), house);
                Console.WriteLine("send House!");
            }
        }
    }
}

CLIENT CODE

using System;
using System.Net.Sockets;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;
using MySharedHouse;

namespace ClientApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            MessageClient client = new MessageClient(515);
            client.StartListening();
        }
    }

    public class MessageClient
    {
        private int _port;
        private TcpClient _tcpClient;
        private BinaryFormatter _bFormatter;
        private Thread _listenThread;
        private bool _running;
        private House house;

        public MessageClient(int port)
        {
            this._port = port;
            this._tcpClient = new TcpClient("127.0.0.1", port);
            this._bFormatter = new BinaryFormatter();
            this._running = false;
        }

        public void StartListening()
        {
            lock (this)
            {
                if (!_running)
                {
                    this._running = true;
                    this._listenThread = new Thread
                        (new ThreadStart(ListenForMessage));
                    this._listenThread.Start();
                }
                else
                {
                    this._running = true;
                    this._listenThread = new Thread
                        (new ThreadStart(ListenForMessage));
                    this._listenThread.Start();
                }
            }
        }

        private void ListenForMessage()
        {
            Console.WriteLine("Reading...");
            try
            {
                while (this._running)
                {
                    this.house = (House)this._bFormatter.Deserialize(this._tcpClient.GetStream());
                    Console.WriteLine(this.house.Street);
                    Console.WriteLine(this.house.ZipCode);
                    Console.WriteLine(this.house.Number);
                    Console.WriteLine(this.house.Id);
                    Console.WriteLine(this.house.Town);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                Console.ReadLine();
            }
        }
    }
}

Wooala! the first house to be sent over TCP/IP

like image 4
Musculaa Avatar answered Sep 17 '22 14:09

Musculaa


You can simply decorate your House class with the [Serializable] attribute. (You do not need to define all the other stuff as posted in the other answer)

You can then send this object on the wire by serializing it using the BinaryFormatter class.

Have you considered setting up a WCF service instead of using TcpListener and TcpClient ? Makes life a lot easier.

For instance you could define a service that returned a house

[ServiceContract]
public interface IService
{
    [OperationContract]
    House GetHouse(int houseId);
}

See this real world example.

like image 2
wal Avatar answered Sep 20 '22 14:09

wal