One of the things i’ve noticed over the last few months in particular is the lack of decent programming examples in C# for a suitable client/server socket solution that just works! I was working on a contract that needed a robust and stable communications mechanism between multiple PC’s on a closed network for a Railway Simulator and Aircraft Simulator I was working on.

The following Server code is the first part of this two part article. It shows you how to code a solid and reliable server with error and message handling. Part two will explain how to code a client to go along with it.

Now i’m not naive enough to state there are no bugs in this code but it does work and work well. I would love to hear any comments from any developers that can help improve this code. The original code base I took from a couple of articles at the following URL’s, credit to the original authors:

http://www.developerfusion.co.uk/forums/p/36657/126609/#126609

http://www.codeguru.com/Csharp/Csharp/cs_network/sockets/article.php/c8781/

OK here goes, simply copy this code into the relevant files within a new solution. Bear in mind i’m using VS2008 here so you may need to modify the code accordingly for your environment. Should work fine in VS2005 with slight mods, not sure about VS2003

App.Config

<?xml version=1.0 encoding=utf-8 ?>

<configuration>

<appSettings>

<add key=ServerPort value=10001 />

</appSettings>

</configuration>

This defines the port that server will listen on.

Program.cs

using System;

using System.Collections.Generic;

using System.Linq;

using System.Windows.Forms;

using System.Threading;

namespace SocketServer

{

static class Program

{

/// <summary>

/// The main entry point for the application.

/// </summary>

[STAThread]

static void Main()

{

Application.EnableVisualStyles();

Application.SetCompatibleTextRenderingDefault(false);

Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);

Application.Run(new Server());

}

private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)

{

Server.WriteToLogFile(ExceptionHandler.DisplayMessage(e.Exception));

MessageBox.Show(ExceptionHandler.DisplayMessage(e.Exception));

}

}

}

This is the starting point for the application. I’m also making use of the ExceptionHandler class I recently spoke about in my blog.

ExceptionHandler.cs

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Data;

using System.Data.SqlClient;

using System.Windows;

namespace SocketServer

{

///

/// Handles displaying error messages

///

public class ExceptionHandler

{

///

/// Takes the exception message and displays a meaningful message to the user

///

public static string DisplayMessage(Exception ex)

{

return DisplayMessage(ex, “”);

}

///

/// Takes the exception message and displays a meaningful message to the user

///

/// The exception to display.

/// Current User

//[System.Diagnostics.DebuggerStepThrough()]

public static string DisplayMessage(Exception ex, string userName)

{

StringBuilder sb = new StringBuilder();

if (ex is DBConcurrencyException)

sb.Append(“Concurrency Error: One or more people have updated this data since your last request.”);

else if (ex is SqlException)

{

sb.Append(“Database Error: “);

switch (((SqlException)ex).Number)

{

case 547:

sb.Append(“There is a constraint on the items you tried to modify. Please try again.”);

break;

case 2601:

// Unique Index

sb.Append(“Cannot insert duplicate values into the database.”);

break;

case 2627:

// Unique Constraint

sb.Append(“Cannot insert duplicate values into the database.”);

break;

default:

sb.Append(ex.Message);

break;

}

}

else

{

sb.Append(“Exception Handler Unexpected Error: “ + ex.Message);

}

string nl = “\n\n”;

sb.Append(nl + “Exception Information:” + nl);

sb.Append(“Message: “ + ex.Message + nl);

sb.Append(“Source: “ + ex.Source + nl);

sb.Append(“Stack Trace: “ + ex.StackTrace + nl);

if (ex.InnerException != null)

{

sb.Append(nl + “Inner Exception Info:” + nl);

sb.Append(“Message: “ + ex.InnerException.Message + nl);

sb.Append(“Source: “ + ex.InnerException.Source + nl);

sb.Append(“Stack Trace: “ + ex.InnerException.StackTrace + nl);

}

return sb.ToString();

}

}

}

UtilityFunctions.cs

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Net;

using System.Net.Sockets;

using System.Collections;

namespace SocketServer

{

class UtilityFunctions

{

public static String GetIP()

{

String strHostName = Dns.GetHostName();

// Find host by name

IPHostEntry iphostentry = Dns.GetHostEntry(strHostName);

// Grab the first IP addresses

String IPStr = “”;

foreach (IPAddress ipaddress in iphostentry.AddressList)

{

IPStr = ipaddress.ToString();

return IPStr;

}

return IPStr;

}

/// <summary>

/// Splits any string using seperators string. This is different from the

/// string.Split method as we ignore delimiters inside double quotes and

/// will *ignore multiple delimiters in a row (i.e. “One,,,,two” will split

/// to two fields if comma is a delimiter).

/// Example:

/// Delims: ” \t,” (space, tab, comma)

/// Input: “one two” three four,five

/// Returns (4 strings):

/// one two

/// three

/// four

/// five

/// </summary>

/// <param name=”text”>The string to split.</param>

/// <param name=”delimiters”>The characters to split on.</param>

/// <returns></returns>

public static string[] SplitQuoted(string text, string delimiters)

{

// Default delimiters are a space and tab (e.g. ” \t”).

// All delimiters not inside quote pair are ignored.

// Default quotes pair is two double quotes ( e.g. ‘”"‘ ).

if (text == null)

throw new ArgumentNullException(“text”, “text is null.”);

if (delimiters == null || delimiters.Length < 1)

delimiters = ” \t”; // Default is a space and tab.

ArrayList res = new ArrayList();

// Build the pattern that searches for both quoted and unquoted elements

// notice that the quoted element is defined by group #2 (g1)

// and the unquoted element is defined by group #3 (g2).

string pattern =

@”"”([^""\\]*[\\.[^""\\]*]*)”"” +

“|” +

@”([^" + delimiters + @"]+)”;

// Search the string.

foreach (System.Text.RegularExpressions.Match m in System.Text.RegularExpressions.Regex.Matches(text, pattern))

{

//string g0 = m.Groups[0].Value;

string g1 = m.Groups[1].Value;

string g2 = m.Groups[2].Value;

if (g2 != null && g2.Length > 0)

{

res.Add(g2);

}

else

{

// get the quoted string, but without the quotes in g1;

res.Add(g1);

}

}

return (string[])res.ToArray(typeof(string));

}

}

}

Just a couple of general routines we can call upon. I tend to add things like this to a separate class as it makes things nice and tidy.

SocketServer.Designer.cs

namespace SocketServer

{

partial class Server

{

/// <summary>

/// Required designer variable.

/// </summary>

private System.ComponentModel.IContainer components = null;

/// <summary>

/// Clean up any resources being used.

/// </summary>

/// <param name=”disposing”>true if managed resources should be disposed; otherwise, false.</param>

protected override void Dispose(bool disposing)

{

if (disposing && (components != null))

{

components.Dispose();

}

base.Dispose(disposing);

}

#region Windows Form Designer generated code

/// <summary>

/// Required method for Designer support – do not modify

/// the contents of this method with the code editor.

/// </summary>

private void InitializeComponent()

{

this.label1 = new System.Windows.Forms.Label();

this.groupBox1 = new System.Windows.Forms.GroupBox();

this.richTextBoxSendMsg = new System.Windows.Forms.RichTextBox();

this.btnClear = new System.Windows.Forms.Button();

this.richTextBoxReceivedMsg = new System.Windows.Forms.RichTextBox();

this.label8 = new System.Windows.Forms.Label();

this.buttonSendMsg = new System.Windows.Forms.Button();

this.groupBox1.SuspendLayout();

this.SuspendLayout();

//

// label1

//

this.label1.AutoSize = true;

this.label1.Font = new System.Drawing.Font(“Microsoft Sans Serif”, 11F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));

this.label1.Location = new System.Drawing.Point(360, 9);

this.label1.Name = “label1″;

this.label1.Size = new System.Drawing.Size(115, 18);

this.label1.TabIndex = 7;

this.label1.Text = “Socket Server”;

//

// groupBox1

//

this.groupBox1.Controls.Add(this.richTextBoxSendMsg);

this.groupBox1.Controls.Add(this.label1);

this.groupBox1.Controls.Add(this.btnClear);

this.groupBox1.Controls.Add(this.richTextBoxReceivedMsg);

this.groupBox1.Controls.Add(this.label8);

this.groupBox1.Controls.Add(this.buttonSendMsg);

this.groupBox1.Dock = System.Windows.Forms.DockStyle.Fill;

this.groupBox1.Location = new System.Drawing.Point(0, 0);

this.groupBox1.Name = “groupBox1″;

this.groupBox1.Size = new System.Drawing.Size(477, 298);

this.groupBox1.TabIndex = 22;

this.groupBox1.TabStop = false;

//

// richTextBoxSendMsg

//

this.richTextBoxSendMsg.Location = new System.Drawing.Point(6, 27);

this.richTextBoxSendMsg.Name = “richTextBoxSendMsg”;

this.richTextBoxSendMsg.Size = new System.Drawing.Size(183, 21);

this.richTextBoxSendMsg.TabIndex = 45;

this.richTextBoxSendMsg.Text = “”;

//

// btnClear

//

this.btnClear.Location = new System.Drawing.Point(287, 27);

this.btnClear.Name = “btnClear”;

this.btnClear.Size = new System.Drawing.Size(88, 24);

this.btnClear.TabIndex = 43;

this.btnClear.Text = “Clear”;

this.btnClear.Click += new System.EventHandler(this.btnClear_Click);

//

// richTextBoxReceivedMsg

//

this.richTextBoxReceivedMsg.BackColor = System.Drawing.SystemColors.InactiveCaptionText;

this.richTextBoxReceivedMsg.Dock = System.Windows.Forms.DockStyle.Bottom;

this.richTextBoxReceivedMsg.Font = new System.Drawing.Font(“Microsoft Sans Serif”, 7F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));

this.richTextBoxReceivedMsg.Location = new System.Drawing.Point(3, 57);

this.richTextBoxReceivedMsg.Name = “richTextBoxReceivedMsg”;

this.richTextBoxReceivedMsg.ReadOnly = true;

this.richTextBoxReceivedMsg.Size = new System.Drawing.Size(471, 238);

this.richTextBoxReceivedMsg.TabIndex = 35;

this.richTextBoxReceivedMsg.Text = “”;

//

// label8

//

this.label8.Location = new System.Drawing.Point(3, 15);

this.label8.Name = “label8″;

this.label8.Size = new System.Drawing.Size(158, 20);

this.label8.TabIndex = 34;

this.label8.Text = “Broadcast Message To Clients”;

//

// buttonSendMsg

//

this.buttonSendMsg.Location = new System.Drawing.Point(192, 27);

this.buttonSendMsg.Name = “buttonSendMsg”;

this.buttonSendMsg.Size = new System.Drawing.Size(89, 24);

this.buttonSendMsg.TabIndex = 33;

this.buttonSendMsg.Text = “Send Message”;

this.buttonSendMsg.Click += new System.EventHandler(this.buttonSendMsg_Click);

//

// Server

//

this.AcceptButton = this.btnClear;

this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);

this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;

this.ClientSize = new System.Drawing.Size(477, 298);

this.Controls.Add(this.groupBox1);

this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D;

this.Name = “Server”;

this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;

this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;

this.Load += new System.EventHandler(this.LaunchForm_Load);

this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.Server_FormClosed);

this.groupBox1.ResumeLayout(false);

this.groupBox1.PerformLayout();

this.ResumeLayout(false);

}

#endregion

private System.Windows.Forms.Label label1;

private System.Windows.Forms.GroupBox groupBox1;

private System.Windows.Forms.Button btnClear;

private System.Windows.Forms.RichTextBox richTextBoxReceivedMsg;

private System.Windows.Forms.Label label8;

private System.Windows.Forms.Button buttonSendMsg;

private System.Windows.Forms.RichTextBox richTextBoxSendMsg;

}

}

SocketServer.cs

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Linq;

using System.Text;

using System.Windows.Forms;

using System.Diagnostics;

using System.Threading;

using System.IO;

using System.Net;

using System.Net.Sockets;

using System.Collections;

using System.Reflection;

using System.Configuration;

namespace SocketServer

{

public partial class Server : Form

{

#region socket stuff

// sample code and basis of client/server app taken from the following url’s

// http://www.developerfusion.co.uk/forums/p/36657/126609/#126609

// http://www.codeguru.com/Csharp/Csharp/cs_network/sockets/article.php/c8781/

public delegate void UpdateRichEditCallback(string text);

public delegate void UpdateClientListCallback();

public AsyncCallback pfnWorkerCallBack;

private Socket m_mainSocket;

// An ArrayList is used to keep track of worker sockets that are designed

// to communicate with each connected client. Make it a synchronized ArrayList

// For thread safety

private System.Collections.ArrayList m_workerSocketList = ArrayList.Synchronized(new System.Collections.ArrayList());

// This delegate enables asynchronous calls for setting

// the text property on a TextBox control.

delegate void AppendTextCallback(string text);

// The following variable will keep track of the cumulative

// total number of clients connected at any time. Since multiple threads

// can access this variable, modifying this variable should be done

// in a thread safe manner

private int m_clientCount = 0;

Dictionary<string, int> SocketMachineID = new Dictionary<string, int>(); // int = socketid, string = machineid

#endregion socket stuff

#region private members

Int16 _Port = 10001; // default port we listen on

bool isClosing = false;

byte[] m_dataBuffer = new byte[10];

public AsyncCallback m_pfnCallBack;

public Socket m_clientSocket;

#endregion

#region constructor

public Server()

{

InitializeComponent();

_Port = Convert.ToInt16(ConfigurationManager.AppSettings["ServerPort"]);

}

#endregion constructor

#region private methods

/// <summary>

/// Search our dictionary to get the index of our socket to send the correct data down

/// </summary>

/// <param name=”Mode”>Client mode, we can identify our individual clients here, I will use Client for this example</param>

/// <returns>Integer contaning the socket id to pass into SendMsgToClient method</returns>

private int GetModeFromDictionary(string Mode)

{

int value = -1;

SocketMachineID.TryGetValue(Mode, out value);

return value;

}

#endregion private methods

#region event handlers

/// <summary>

/// Action to be performed by RetryOpen

/// </summary>

/// <returns></returns>

public delegate T RetryOpenDelegate<T>();

/// <summary>

/// Perform an action until succeeds without throwing IOException

/// </summary>

/// <typeparam name=”T”>object returned</typeparam>

/// <param name=”action”>action performed</param>

/// <returns></returns>

public static T RetryOpen<T>(RetryOpenDelegate<T> action)

{

while (true)

{

try

{

return action();

}

catch (IOException)

{

System.Threading.Thread.Sleep(50);

}

}

}

public static void WriteToLogFile(string msg)

{

// create our daily directory

string dir = Application.StartupPath + “\\logs\\” + DateTime.Now.ToString(“ddMMyyyy”);

if (!Directory.Exists(dir))

Directory.CreateDirectory(dir);

string LogFile = dir + “\\” + DateTime.Now.ToString(“ddMMyyyy”) + “_Server_log.txt”;

TextWriter tw = null;

try

{

// create a writer and open the file

tw = RetryOpen<StreamWriter>(delegate()

{

return new StreamWriter(LogFile, true);

});

// write a line of text to the file

tw.WriteLine(DateTime.Now + “: ‘” + Environment.MachineName + “‘ – “ + msg);

}

catch { }

finally

{

// close the stream

if (tw != null)

{

tw.Close();

tw.Dispose();

}

}

}

private void buttonSendMsg_Click(object sender, EventArgs e)

{

AppendToRichEditControl(“User Sending Message :” + richTextBoxSendMsg.Text);

SendMsgToAllClients(richTextBoxSendMsg.Text);

}

private void btnClear_Click(object sender, EventArgs e)

{

richTextBoxReceivedMsg.Clear();

}

void ExitApplication()

{

isClosing = true;

SendMsgToClient(“Shutdown”, -1); // broadcast message to all clients

Close();

}

private void Server_FormClosed(object sender, FormClosedEventArgs e)

{

if (!isClosing)

CloseSockets();

}

private void LaunchForm_Load(object sender, EventArgs e)

{

StartServerListening(); //Ensure our clients can find us by starting the server running

}

#endregion event handlers

#region Socket Methods

private void StartServerListening()

{

try

{

// Create the listening socket…

m_mainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

IPEndPoint ipLocal = new IPEndPoint(IPAddress.Any, _Port);

// Bind to local IP Address…

m_mainSocket.Bind(ipLocal);

// Start listening – allow 5 simultaneous connections…

m_mainSocket.Listen(5);

// Create the call back for any client connections…

m_mainSocket.BeginAccept(new AsyncCallback(OnClientConnect), null);

}

catch (SocketException se)

{

AppendToRichEditControl(se.Message);

}

}

/// <summary>

/// This is the call back function, which will be invoked when a client is connected

/// </summary>

/// <param name=”asyn”></param>

public void OnClientConnect(IAsyncResult asyn)

{

try

{

if (m_mainSocket != null)

{

// Here we complete/end the BeginAccept() asynchronous call

// by calling EndAccept() – which returns the reference to a new Socket object

Socket workerSocket = m_mainSocket.EndAccept(asyn);

// Now increment the client count for this client in a thread safe manner

Interlocked.Increment(ref m_clientCount);

// Add the workerSocket reference to our ArrayList

m_workerSocketList.Add(workerSocket);

// Send a welcome message to client with their socket id so they can communicate properly with us

string msg = “hello,” + m_clientCount;

SendMsgToClient(msg, m_clientCount);

// Let the worker Socket do the further processing for the just connected client

WaitForData(workerSocket, m_clientCount);

// Since the main Socket is now free, it can go back and wait for

// other clients who are attempting to connect

m_mainSocket.BeginAccept(new AsyncCallback(OnClientConnect), null);

}

}

catch (ObjectDisposedException)

{

System.Diagnostics.Debugger.Log(0, “1″, “\n OnClientConnection: Socket has been closed\n”);

}

catch (SocketException se)

{

AppendToRichEditControl(se.Message);

}

}

/// <summary>

/// Start waiting for data from the client

/// </summary>

/// <param name=”soc”></param>

/// <param name=”clientNumber”></param>

public void WaitForData(System.Net.Sockets.Socket soc, int clientNumber)

{

try

{

if (pfnWorkerCallBack == null)

{

// Specify the call back function which is to be

// invoked when there is any write activity by the connected client

pfnWorkerCallBack = new AsyncCallback(OnDataReceived);

}

SocketPacket theSocPkt = new SocketPacket(soc, clientNumber);

soc.BeginReceive(theSocPkt.dataBuffer, 0, theSocPkt.dataBuffer.Length, SocketFlags.None, pfnWorkerCallBack, theSocPkt);

AppendToRichEditControl(“Connected to Client “ + soc.RemoteEndPoint);

}

catch (SocketException se)

{

AppendToRichEditControl(se.Message);

}

}

/// <summary>

/// This the call back function which will be invoked when the socket

/// detects any client writing of data on the stream

/// </summary>

/// <param name=”asyn”></param>

public void OnDataReceived(IAsyncResult asyn)

{

SocketPacket socketData = (SocketPacket)asyn.AsyncState;

try

{

// Complete the BeginReceive() asynchronous call by EndReceive() method

// which will return the number of characters written to the stream by the client

int iRx = socketData.m_currentSocket.EndReceive(asyn);

char[] chars = new char[iRx + 1];

// Extract the characters as a buffer

System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();

int charLen = d.GetChars(socketData.dataBuffer, 0, iRx, chars, 0);

System.String szData = new System.String(chars);

string msg = “” + socketData.m_clientNumber + “:”;

Processcommand(DecodeCommandString(szData), socketData);

AppendToRichEditControl(msg + szData);

msg = “command=” + Dns.GetHostName() + “,ok,” + szData;

SendMsgToClient(msg, socketData); // send our response back to the calling client

// Continue the waiting for data on the Socket

WaitForData(socketData.m_currentSocket, socketData.m_clientNumber);

}

catch (ObjectDisposedException)

{

System.Diagnostics.Debugger.Log(0, “1″, “\nOnDataReceived: Socket has been closed\n”);

}

catch (SocketException se)

{

if (se.ErrorCode == 10054) // Error code for Connection reset by peer

{

string msg = “Client “ + socketData.m_clientNumber + ” Disconnected” + “\n”;

AppendToRichEditControl(msg);

// Remove the reference to the worker socket of the closed client

// so that this object will get garbage collected

m_workerSocketList[socketData.m_clientNumber - 1] = null;

}

else

{

MessageBox.Show(“SocketException Error in OnDataReceived: “ + se.Message + “\r” + se.StackTrace);

}

}

catch (Exception ex)

{

MessageBox.Show(“General Error in OnDataReceived: “ + ex.Message + “\r” + ex.StackTrace);

}

}

private void Processcommand(string[] ClientCommand, SocketPacket socket)

{

// get our command string, parse it, pass through case statement

// and perform the relevant update i.e. label status

int count = ClientCommand.Length; // how many elements in our array

for (int i = 0; i < count; i++)

{

switch (ClientCommand[0].ToLower())

{

case “hello”: // hello from client

{

int SocketID = Convert.ToInt16(ClientCommand[1]); // get our socket

string Ident = “”;

if (ClientCommand[2].ToLower() == “id”)

Ident = ClientCommand[3];

// check if key already exists

if (!SocketMachineID.ContainsKey(Ident))

{

try

{

// Now add to dictionary

SocketMachineID.Add(Ident, SocketID);

}

catch (ArgumentException)

{

SocketMachineID.Remove(Ident); // remove the invalid entry

SocketMachineID.Add(Ident, SocketID); // re-add it

AppendToRichEditControl(“Updated key in dictionary”);

}

}

}

break;

case “id”: // machine identifier

{

if (ClientCommand[1].ToLower() == “client”)

{

bool avail = ClientCommand[2].ToLower() == “available”;

}

}

break;

case “showmessage”:

{

MessageBox.Show(ClientCommand[1]);

}

break;

default:

break;

}

}

}

/// <summary>

/// Here we decode the string sent from the client

/// </summary>

/// <param name=”szData”></param>

private string[] DecodeCommandString(string szData)

{

string[] command = UtilityFunctions.SplitQuoted(szData, “=,\r\n\0″);

return command;

}

/// <summary>

/// This method could be called by either the main thread or any of the worker threads

/// </summary>

/// <param name=”msg”>String to append</param>

private void AppendToRichEditControl(string msg)

{

// Check to see if this method is called from a thread other than the one created the control

if (InvokeRequired)

{

// We cannot update the GUI on this thread.

// All GUI controls are to be updated by the main (GUI) thread.

// Hence we will use the invoke method on the control which will

// be called when the Main thread is free

// Do UI update on UI thread

object[] pList = { msg };

richTextBoxReceivedMsg.BeginInvoke(new UpdateRichEditCallback(OnUpdateRichEdit), pList);

WriteToLogFile(msg);

}

else

{

// This is the main thread which created this control, hence update it directly

OnUpdateRichEdit(msg);

WriteToLogFile(msg);

}

}

/// <summary>

/// This UpdateRichEdit will be run back on the UI thread

/// (using System.EventHandler signature so we don’t need to define a new delegate type here)

/// </summary>

/// <param name=”msg”></param>

private void OnUpdateRichEdit(string msg)

{

richTextBoxReceivedMsg.AppendText(msg + “\r”);

}

void CloseSockets()

{

if (m_mainSocket != null)

{

m_mainSocket.Close();

m_mainSocket = null;

}

try

{

Socket workerSocket = null;

for (int i = 0; i < m_workerSocketList.Count; i++)

{

workerSocket = (Socket)m_workerSocketList[i];

if (workerSocket != null)

{

m_workerSocketList.Remove(workerSocket);

workerSocket.Close();

workerSocket = null;

}

}

}

catch

{ }

}

/// <summary>

/// Allows us to send a specific message to a specific client

/// </summary>

/// <param name=”msg”></param>

/// <param name=”clientNumber”></param>

void SendMsgToClient(string msg, int clientNumber)

{

// Convert the reply to byte array

byte[] byData = System.Text.Encoding.ASCII.GetBytes(msg);

if (clientNumber == -1) // send to all available servers

{

for (int i = 0; i < m_workerSocketList.Count; i++)

{

Socket workerSocket = (Socket)m_workerSocketList[i];

if (workerSocket != null)

workerSocket.Send(byData);

}

}

else

{

if (clientNumber != 0)

{

Socket workerSocket = (Socket)m_workerSocketList[clientNumber - 1];

if (workerSocket != null)

workerSocket.Send(byData);

}

}

}

/// <summary>

/// Send back a reply to the client using a socket descriptor

/// </summary>

/// <param name=”socketData”>Socket to send the data down</param>

/// <param name=”szData”>Data to send</param>

private static void SendMsgToClient(string szData, SocketPacket socketData)

{

try

{

string replyMsg = “Server Reply,” + szData.ToUpper();

// Convert the reply to byte array

byte[] byData = System.Text.Encoding.ASCII.GetBytes(replyMsg);

Socket workerSocket = (Socket)socketData.m_currentSocket;

workerSocket.Send(byData);

}

catch //(Exception ex)

{

// MessageBox.Show(“Error in SendMsgToClient method: ” + ex.Message + “\r” + ex.StackTrace);

}

}

/// <summary>

/// Allows us to send a broadcast message to all our clients

/// </summary>

/// <param name=”msg”>Message to send</param>

private void SendMsgToAllClients(string msg)

{

try

{

msg = “servermsg:” + msg + “\n”;

byte[] byData = System.Text.Encoding.ASCII.GetBytes(msg);

Socket workerSocket = null;

for (int i = 0; i < m_workerSocketList.Count; i++)

{

workerSocket = (Socket)m_workerSocketList[i];

if (workerSocket != null)

{

if (workerSocket.Connected)

{

workerSocket.Send(byData);

}

}

}

}

catch //(SocketException se)

{

//MessageBox.Show(se.Message);

}

}

#endregion Socket Methods

}

#region SocketPacket Class

public class SocketPacket

{

// Constructor which takes a Socket and a client number

public SocketPacket(System.Net.Sockets.Socket socket, int clientNumber)

{

m_currentSocket = socket;

m_clientNumber = clientNumber;

}

public System.Net.Sockets.Socket m_currentSocket;

public int m_clientNumber;

// Buffer to store the data sent by the client

public byte[] dataBuffer = new byte[1024];

}

#endregion SocketPacket Class

}

Now simply compile and run. You will now have a server application which is capable of receiving any clients that connect to it on port 10001. Try it with a telnet client i.e. telnet 127.0.0.1 10001

You should see the client connect in the server window and the telnet client should get a response back saying hello with the socket number it’s connected on. Whilst this session is open, try another telnet session and you should see the socket number increase.

In the server app, add some text to the text box and click Send Message. The telnet clients will display the server sent message

That’s all there is to it. In the next part I will knock up a client application that you can use to talk more effectively to the server and have it send commands that it acts upon. For a hint, check out the ProcessCommand method in the server

Good luck!

Si

Speak to Simon Steed about any of the topics discussed this site.