Monday, August 4, 2008

Module-Oriented development


One of the most important aspects of the development process is the flexibility. Most developers think, that flexibility is also affects to the core of the process, but that also affects the UI of the application.
There are situations, which in some parts (modules) of the application UI stop responding, and all the other UI elements stop responding because of that.
This is just an example, when we need module-oriented development as the main idea for whole the development process.
Let's start from a simple example. So we have a UI application. First of all we can define virtual blocks of UI, each of which should represent a module. For example, let's suppose we should have a search part and toolbar part on the GUI. But our application should not contain all that stuff in it. Each part should be represented by a module. So we should have a Search Module, and a Toolbar Module. Each module should be represented by a separete assembly.
And our application should have a configuration file (xml), where it should keep paths for all installed modules. When it should start, it should just go through the records in the config file. For each module it should give a GUI container, where each module muust be drawn.
During the initialization the application should initialize all the modules first and then start. Using this approach should give us ability to connect different parts of our GUI to different assemlyies. So if for exampl we have not some assemly, it should just not load. This also gives the main application ability to control all the modules work, by giving them appropriate security rules.
Also we should be able to unload the modules, which work bad, during runtime and be error - free.
To gain this, we must define an interface, which should be implemented by each Module. The application should work only with the interface for modules.

The module-oriented development applies not only to GUI applications, but to any type of applications, which cann be represented as multi-module structure.

Simple Socket Server


The idea of creating a good socket-server depends on several factors, which should surely be calculated in accordance to the requirements of the given task.
The most simple socket server exampl is the "One-Thread" server, which does all the job in its main thread. But this is not good enought for system, which should give a lot of time to each connected client to process.
Here come the threads. In this case, each client has its own (separete) thread, which in it works, and does its tasks as long as it need. Here is the main idea of most client-server applications.
The example project represents a simple Socket-Server, which keeps track of all connected clietns. Each client is represented by a ConnectionUser class instnace on the server. That class has a single "Run" method, which starts the processor of that client in a separete thread.
The project is a VS.Net 2005 Solution, but I have also included the source files in the post.


##########################################################################
############################SimpleServer.cs###############################

using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;

namespace SimpleSocketServer
{
public class SimpleServer
{
///
/// The port, to be listened for connection requests
///

private int portToListen = -1;

///
/// The listener object, which through the clients should connect
///

private TcpListener listener;

///
/// Keeps the state of the server
///

private bool isStarted = false;

///
/// The list of connected users
///

private List users = new List();

///
/// Creates an instance of a SimpleServer class
///

public SimpleServer()
{

}

///
/// Creates an instance of a SimpleServer class configured for listening the given port
///

/// The port to be listened
public SimpleServer(int argPortToListen)
{
this.PortToListen = argPortToListen;
}

///
/// Starts the server on the pre-specified port.
/// If the port was not specified, throws an Exception
///

public void Start()
{
if (portToListen == -1)
{
throw new Exception("No port specified for the listener");
}

isStarted = true;
listener = new TcpListener(this.portToListen);
try
{
listener.Start();
while (isStarted)
{
try
{
//Get the TcpClient instance for the connection
TcpClient tmpNewClient = listener.AcceptTcpClient();

//Pass the retrieved object to the new processing unit
//that unit in a new threads
ConnectionUser tmpNewUser = new ConnectionUser(tmpNewClient);

tmpNewUser.ConnectionClosed += new EventHandler(ConnectionUser_ConnectionClosed);

lock (this.users)
{
//add the new user to the track list
this.users.Add(tmpNewUser);
}

//Start processing the client in a new thread
tmpNewUser.Run();
}
catch (Exception ex)
{

}
}
}
catch (Exception e)
{
//Handler code here
}
}

///
/// Handles the ConnectionClosed event of each connected user
///

/// The sender of the event
/// The event arguments
protected void ConnectionUser_ConnectionClosed(object sender, EventArgs e)
{
lock (this.users)
{
ConnectionUser tmpUser = sender as ConnectionUser;
tmpUser.ConnectionClosed -= new EventHandler(ConnectionUser_ConnectionClosed);
if (this.users.Remove(sender as ConnectionUser))
{
Console.WriteLine("Client disconnected");
}
}
}

///
/// Stops the server instance
///

public void Stop()
{
this.isStarted = false;
this.listener.Stop();

foreach (ConnectionUser tmpUser in users)
{
tmpUser.Stop();
}
}

///
/// Gets or sets the port
///

public int PortToListen
{
get
{
return this.portToListen;
}

set
{
if (value <= 0)
{
throw new ArgumentOutOfRangeException();
}

this.portToListen = value;
}
}
}
}


##########################################################################
###########################ConnectionUser.cs##############################
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.IO;

namespace SimpleSocketServer
{
///
/// The class, which uses a single TcpClient
/// connection for communication with remote host
///

public class ConnectionUser
{
public event EventHandler ConnectionClosed;

protected TcpClient connection;
private bool isStarted = false;

public ConnectionUser(TcpClient argClient)
{
this.connection = argClient;
}

///
/// Starts a new Thread and runs current instance in that thread
///

public void Run()
{
Thread tmpNewThread = new Thread(new ThreadStart(StartProcessing));
tmpNewThread.Start();
}


///
/// The processor method, which keeps whole logic of the processor unit
/// Must be overriden in all descendant classes.
///

protected virtual void StartProcessing()
{
isStarted = true;
}

///
/// Gets the Network stream of the underlying connection
///

protected Stream NetworkStream
{
get
{
return connection.GetStream();
}
}


///
/// Stops the new-thread processor
///

public void Stop()
{
this.isStarted = false;
this.connection.Close();
}

///
/// Handles the connection close events
///

private void OnConnectionClose()
{
if (this.ConnectionClosed != null)
{
this.ConnectionClosed(this, null);
}
}
}
}