Click here to Skip to main content
15,887,596 members
Articles / Programming Languages / C#
Article

XML Based Communication library

Rate me:
Please Sign up or sign in to vote.
4.69/5 (14 votes)
18 Dec 20032 min read 152.4K   2.6K   65   31
XML based communication library to use command/object based through TCP/IP connections.

Introduction

The TCP/IP communication requires a lot of processing of incoming and outgoing data. The XML communication library uses XML streams to transfer data between the TCP/IP connections. The main idea is to use predefined communication objects. These communication objects can be serialized into XML streams. This XML stream, with a very small header to identify the serialized object and the XML size, can be used to communicate.

Background

XML communication library uses a straight forward idea:

  • Writing
    1. Define objects for each XML message wanted to be transferred.
    2. Serialize created object into XML stream.
    3. Determine the size of the generated XML stream.
    4. Construct communication header. I used a very simple header with the following format:
      CommandID:XML Stream Size:XML Stream String
    5. Write the constructed header/XML data to the network stream.
  • Reading
    1. Analyze incoming data for appropriate header (command ID, and XML data size)
    2. Use the header to read and deserialize into the appropriate object. This can be determined using the command ID.

This mechanism makes the communication more easier and more clear to code.

Using the code

The XML communication library is mainly based on one static class' operations. This class is called CommProtocol. This class defines a private default constructor to prevent any creation of objects for this class. This class has four static functions:

  1. Read: Reads data from network stream.
  2. Write: Writes data to network stream.
  3. ReadHeader: Reads incoming header from network stream. This function is used mainly by the Read function.
  4. ReadCommand: Deserialize the incoming XML stream to its appropriate object using the header information. This function is also mainly used by the Read function.

The following code describes how we can write to network stream using the CommProtocol class:

C#
CommProtocol.Write(netStream,object,object type);

The Write function will take care of object serialization and header building, and writing to the network stream.

The following code describes how we can read from the network stream using the CommProtocol class:

C#
object o = CommProtocol.Read(Networkstream,out cmd);
 switch(cmd) 
{ 
case /*your commands selector*/: 
/*Function to process the incoming object*/ 
break; 
}

The Read function will throw an exception if the network stream is closed or header is corrupted.

The XML serialized objects are defined in the library main file CommLib.cs. For the article code, I defined one class called CommSendText and one communication command called CommandSendText.

I developed the communication library mainly to help myself in the development of a server based chat application. The main issue I considered when I developed this library was to make the communication that simple. For this, I used double handshaking between the server and the clients. This means, each command sent to the server must return a status to the client. This makes the communication more reliable and up to date.

There are many enhancements I want to add to this library, so your feedback is very important to me.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Architect Nebras Technology
Egypt Egypt
With a heavy C++/MFC background started my way to the DotNet and C# world.
Going from device drivers to standard windows applications development using C++/MFC since 1998, I started the switch to .Net technology in 2004.
Currently I am the Technical Development Manager and Software Architect of Nebras Technology (Medical Software Vendor) and one of its executives owners.

Comments and Discussions

 
SuggestionDemofile here! Working Communication. Pin
ayeks13-May-13 22:47
ayeks13-May-13 22:47 
Hey folks,

unfortunately there is no demo provided by the author of the library. I wrote a small communication between a client and a server by myself. The lib is very easy to use!

If you want to send your own object you muss add a CommSend_XYZ_ class in the CommLib.cs.
Also you have to add a case in the CommProtocol.cs at the "ReadCommand()" method.

I marked with: "// ##### add here a case for your own classes" where you should insert your stuff.

For example i added 2 Classes, CommSendJob and CommSendResponse.

You have to start the server first, following by the client. The client sends a Job with Name, Parameter and Priority. The server returns a Result with jobAccepted and an Error Message someting is wrong with the job.

Feel free to ask anything about my sample. Germans can be happy because my additional comments are in german. Nevertheless it should be easy to understand for everyone.

Okay. Make 2 new console projects, insert the CommLib.cs and the CommProtocol.cs.
One project is the server, the other the Client. Here are the project files: http://www57.zippyshare.com/v/45396257/file.html[^] !!You have to change the serverIP to 127.0.0.1 in the SharedComponents.cs!!!

The Code:

CommProtocol.cs
C#
using System;
using System.Net.Sockets;
using System.IO;
using System.Xml.Serialization;

namespace CommLib
{
	/// <summary>
	/// CommProtocol Class, Handle the communication protocol between server/clients
	/// or clients/clients
	/// </summary>
	public sealed class CommProtocol
	{
		/// <summary>
		/// private constructor to prevent object creation since this is a static class
		/// </summary>
		private CommProtocol()
		{
		}

		/// <summary>
		/// read data from network stream. if connection failed or lost an
		/// InvalidOperationException will be thrown 
		/// </summary>
		/// <param name="stream">network stream </param>
		/// <param name="cmd">command that will be read from the stream</param>
		/// <returns>object associated with command</returns>
		public static object Read(NetworkStream stream,out CommCommand cmd)
		{
            Console.WriteLine("#\tCommProtocol Read()");
			TextReader reader = new StreamReader(stream,System.Text.Encoding.Unicode);
			int xml_size;
			ReadHeader(reader,out xml_size,out cmd);
			if(xml_size < 0 ) 
			{
				throw new InvalidOperationException("user disconnected");
			}
			
			char[] buf = new char[xml_size];
			if(reader.Read(buf,0,xml_size) < xml_size)
			{
				throw new InvalidOperationException("user disconnected");
			}
			
			StringReader xml_reader = new StringReader(new string(buf));
			
			return ReadCommand(xml_reader,cmd);
		}
		
		/// <summary>
		/// write data to the stream. data will be written in the following order
		/// Command type
		/// delimiter ":"
		/// command object size
		/// delimiter ":"
		/// object data in xml format
		/// 
		/// </summary>
		/// <param name="stream">stream to write data into</param>
		/// <param name="o">object to write into the stream, this can be null
		/// for some commands</param>
		/// <param name="cmd">command to write to the stream</param>
		public static void Write(Stream stream, object o,CommCommand cmd)
		{
			StringWriter xml_stream = new StringWriter();
			if(o != null)
			{
				XmlSerializer serializer = new XmlSerializer(o.GetType());
				serializer.Serialize(xml_stream,o);
			}
			int xml_size = xml_stream.GetStringBuilder().Length;
			
			string header = string.Format("${0}:{1}:",xml_size,(short)cmd);
			string tmp_str = header+xml_stream.GetStringBuilder().ToString();
			byte[] buf = System.Text.Encoding.Unicode.GetBytes(tmp_str);
			stream.Write(buf,0,buf.Length);
		}

		/// <summary>
		/// read header from the stream. This header will be used to determine how to
		/// read and desirialize the XML data int Comm object
		/// </summary>
		/// <param name="reader">Text Reader stream</param>
		/// <param name="xml_size">xml object size</param>
		/// <param name="cmd">the command</param>
		private static void ReadHeader(TextReader reader,out int xml_size,out CommCommand cmd)
		{
            Console.WriteLine("#\tCommProtocol ReadHeader()");
			bool done = false;
			char[] one_char = new char[1];
			string xml_size_string = string.Empty;
			string command_string = string.Empty;
			int header_item = 0;
			xml_size = -1;
			cmd = new CommCommand();
			while(!done)
			{
				if(reader.Read(one_char,0,1) < 0) return;
				switch(one_char[0])
				{
					case '$':
						break;
					case ':':
						if(++header_item > 1)//2 header items
							done = true;
						break;
					default:
						if(char.IsNumber(one_char[0]))
						{
							switch(header_item)
							{
								case 0:
									xml_size_string = xml_size_string + one_char[0];
									break;
								case 1:
									command_string = command_string + one_char[0];
									break;
							}
						}
						else
						{
							throw new InvalidOperationException("Protocol Error, Header Corrupted");
						}
						break;
				}
			}
			xml_size = int.Parse(xml_size_string);
			cmd = (CommCommand)int.Parse(command_string);
		}
		
		/// <summary>
		/// read command data from the TextReader stream accordign to the requested command
        ///  ##### add here a case for your own classes
		/// </summary>
		/// <param name="reader">Text reader stream contains the object in XML format</param>
		/// <param name="cmd">the command</param>
		/// <returns>object serialized from the stream</returns>
		private static object ReadCommand(TextReader reader,CommCommand cmd)
		{
            Console.WriteLine("#\tCommProtocol ReadCommand()");
			CommStatus status = new CommStatus();
			try
			{
				XmlSerializer serializer;

				switch(cmd)
				{
					case CommCommand.CommandSendText:
						serializer = new XmlSerializer(typeof(CommSendText));
						CommSendText send_text = serializer.Deserialize(reader) as CommSendText;
						return send_text ;

                    //  ##### add here a case for your own classes
                    case CommCommand.CommandSendJob:
                        serializer = new XmlSerializer(typeof(CommSendJob));
                        CommSendJob send_job = serializer.Deserialize(reader) as CommSendJob;
                        return send_job;

                    case CommCommand.CommandSendResponse:
                        serializer = new XmlSerializer(typeof(CommSendResponse));
                        CommSendResponse send_Response = serializer.Deserialize(reader) as CommSendResponse;
                        return send_Response;
				

					case CommCommand.CommandStatus:
						serializer = new XmlSerializer(typeof(CommStatus));
						status = serializer.Deserialize(reader) as CommStatus;
						return status;
					
					default:
						status.OpStatus = OperationStatus.CommNetworkError;
						return status;
				}
			}
			catch(Exception ex)
			{
				//do nothing since the delegate doen't exist
				//just inform the user
				Console.WriteLine("Exception wile processing command:{0}",ex.Message);
				status.OpStatus = OperationStatus.CommError;
				return status;
			}
		}
	}
}

CommLib.cs
C#
using System;
using System.Collections;

namespace CommLib
{
	/// <summary>
	/// Operation Status enumerator
	/// </summary>
	public enum OperationStatus
	{
		CommSuccess,
		CommNetworkError,
		CommError
	}
	
	/// <summary>
	/// Communication command
	/// this is user for client/server and client/client communication
	/// </summary>
    public enum CommCommand
    {
        CommandNOP,
        CommandSendText,
        CommandStatus,
        CommandSendJob,
        CommandSendResponse
    }


    /// <summary>
    /// Send text command data class
    /// </summary>
    public class CommSendText
    {
        /// <remarks/>
        public string ChatText;
    }

    //  ##### add here a case for your own classes

    /// <summary>
    /// Send Job Status Class
    /// </summary>
    public class CommSendJob
    {
        public string Name;
        public string Para;
        public int Prio;
    }

    /// <summary>
    /// Send Response Status Class
    /// </summary>
    public class CommSendResponse
    {
        public bool JobAccepted;
        public string ErrMessage;
    }

    /// <summary>
    /// the communicatio result status data class
    /// </summary>
    public class CommStatus
    {
        public OperationStatus OpStatus;
        public CommCommand Cmd;
    }

	
}

SharedComponents.cs
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;

namespace SharedComponents
{
    public class ConInfo
    {
        public const string serverIP = "10.106.64.222";
        public const int port = 10000;
    }

}

Server:
Program.cs
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CommLib;
using SharedComponents;
using System.Net;
using System.Net.Sockets;
using System.IO;

namespace ca_xmlotcp_server
{
    class Program
    {
        static void Main(string[] args)
        {

            // Erstellen eines neuen Listeners an Anschluss 10000
            TcpListener listener = new TcpListener(IPAddress.Any,
            ConInfo.port);

            Console.WriteLine("Initialisiere Anschluss.");
            listener.Start();
            Console.WriteLine("Warte auf eingehende Verbindungsanforderungen...");



            try
            {
                // Auf eingehende Verbindungsanforderung warten 
                // und einen für die Kommunikation initalisierten TcpClient zurückgeben. 
                TcpClient client = listener.AcceptTcpClient();
                Console.WriteLine("Connection accepted.");

                // Den Netzwerk-Stream abrufen.
                NetworkStream stream = client.GetStream();
                CommCommand cmd = new CommCommand();

                // Stream einlesen
                object outputString = CommProtocol.Read(stream, out cmd);
                Console.WriteLine("ObjectString: "+outputString+" Command: "+cmd);

                // hier case auf cmd:
                Console.WriteLine(((CommSendJob)outputString).Name);
                Console.WriteLine(((CommSendJob)outputString).Para);
                Console.WriteLine(((CommSendJob)outputString).Prio);
                Console.WriteLine();

                // Response von Server an Client
                // Response erstellen
                CommSendResponse resToSend = new CommSendResponse();
                resToSend.JobAccepted = false;
                resToSend.ErrMessage = "keine Routine hier vorhanden";
                // Response senden
                CommProtocol.Write(stream, resToSend, CommCommand.CommandSendResponse);


                // Den Verbindungs-Socket schließen.
                client.Close();
                Console.WriteLine("Connection closed.");

                // Den zugrundeliegenden Socket schließen (das Abhören neuer Anforderungen stoppen).
                listener.Stop();
                Console.WriteLine("Listener gestoppt.");


            }
            catch (Exception err)
            {
                Console.WriteLine(err.ToString());

            }

            Console.Read();
        }
    }
    
}

Client:
Program.cs
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CommLib;
using System.Net.Sockets;
using System.Net;
using SharedComponents;

namespace ca_xmlotcp_client
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Started!");

            // Job erstellen
            CommSendJob jobToSend = new CommSendJob();
            jobToSend.Name = "Jobname";
            jobToSend.Para = "cpu = 4";
            jobToSend.Prio = 1;
            
            // TCP Verbindung aufbauen
            TcpClient client = new TcpClient();
            Console.WriteLine("Verbindungsversuch mit Server " +
                    "über Anschluss 10000.");

            // Zu Server connecten
            client.Connect(IPAddress.Parse(ConInfo.serverIP), ConInfo.port);
            Console.WriteLine("Verbindung hergestellt.");
                
             // Den Netzwerk-Stream aufbauen              
             NetworkStream stream = client.GetStream();
             Console.WriteLine("Stream initialisiert!");

             // Job senden
             CommProtocol.Write(stream, jobToSend, CommCommand.CommandSendJob);

             // Response empfangen
             CommCommand cmd = new CommCommand();
             object outputString = CommProtocol.Read(stream, out cmd);
             Console.WriteLine(((CommSendResponse)outputString).JobAccepted);
             Console.WriteLine(((CommSendResponse)outputString).ErrMessage);

             Console.WriteLine("Trenne Verbindung...");
             // Den Verbindungs-Socket schließen.
             client.Close();
             Console.WriteLine("Anschluss geschlossen.");
             Console.Read();

        }
    }

}


Btw, i know that the post is 10 years old. But this is the best library i could find.
GeneralDemo Pin
acheoacheo19-Oct-10 3:39
acheoacheo19-Oct-10 3:39 
GeneralHelp [modified] Pin
nintondo30-Nov-08 22:24
nintondo30-Nov-08 22:24 
GeneralRe: Help Pin
fabianse22-Apr-10 7:35
professionalfabianse22-Apr-10 7:35 
GeneralHELP!!!! Pin
tmn11418-Sep-06 11:29
tmn11418-Sep-06 11:29 
AnswerRe: HELP!!!! Pin
Hesham Desouky18-Sep-06 12:11
Hesham Desouky18-Sep-06 12:11 
QuestionReading more than one command at a time? Pin
Ikram Shaikh17-Nov-05 23:30
Ikram Shaikh17-Nov-05 23:30 
AnswerRe: Reading more than one command at a time? Pin
Ikram Shaikh18-Nov-05 18:58
Ikram Shaikh18-Nov-05 18:58 
GeneralProtocol Error, Header Corrupted Pin
EclecticFrog13-Sep-04 9:45
EclecticFrog13-Sep-04 9:45 
GeneralRe: Protocol Error, Header Corrupted Pin
Hesham Desouky17-Sep-04 6:49
Hesham Desouky17-Sep-04 6:49 
GeneralRe: Protocol Error, Header Corrupted Pin
EclecticFrog25-Oct-04 15:55
EclecticFrog25-Oct-04 15:55 
GeneralNew at C# with a couple questions... Pin
James Bengard13-Jul-04 9:06
sussJames Bengard13-Jul-04 9:06 
GeneralRe: New at C# with a couple questions... Pin
Hesham Desouky14-Jul-04 10:24
Hesham Desouky14-Jul-04 10:24 
GeneralRe: New at C# with a couple questions... Pin
Jonathan Merriweather19-Aug-04 7:40
Jonathan Merriweather19-Aug-04 7:40 
GeneralRe: New at C# with a couple questions... Pin
FocusedWolf30-Apr-05 14:04
FocusedWolf30-Apr-05 14:04 
GeneralRe: New at C# with a couple questions... Pin
Hesham Desouky6-Jun-05 21:09
Hesham Desouky6-Jun-05 21:09 
GeneralRe: New at C# with a couple questions... Pin
acheoacheo19-Oct-10 4:11
acheoacheo19-Oct-10 4:11 
General&quot;There is an error in XML document&quot; Pin
Chimerique17-May-04 14:13
Chimerique17-May-04 14:13 
GeneralRe: &quot;There is an error in XML document&quot; Pin
Anonymous17-May-04 20:34
Anonymous17-May-04 20:34 
GeneralRe: &quot;There is an error in XML document&quot; Pin
Chimerique17-May-04 22:55
Chimerique17-May-04 22:55 
GeneralRe: &quot;There is an error in XML document&quot; Pin
Chimerique17-May-04 23:06
Chimerique17-May-04 23:06 
GeneralRe: &quot;There is an error in XML document&quot; Pin
Hesham Desouky5-Jun-04 21:30
Hesham Desouky5-Jun-04 21:30 
GeneralProblem in Using DLL in Visual C++ 6.0 Pin
BeeAsif11-Nov-09 1:06
BeeAsif11-Nov-09 1:06 
GeneralRe: &quot;There is an error in XML document&quot; Pin
Hesham Desouky5-Jun-04 21:33
Hesham Desouky5-Jun-04 21:33 
GeneralRe: &quot;There is an error in XML document&quot; Pin
pinartekin17-Apr-07 4:11
pinartekin17-Apr-07 4:11 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.