This post has been imported from the old blog and has not yet been converted to the new syntax yet.
In this article we'll write a small ChatServer.

I'll start by explaining the logic we will follow:

We start a Console Application (ChatServer) which will listen on a specified TCP port. When a client connects we create a new object that will represent the Connection. This object has three events, two of those will Add and Remove the client from the connected list when the connection is created and closed. The last one will simply be fired when a message is received.

Our ChatServer will upon receiving a message broadcast it to all clients currently connected. There is no processing, just forwarding to every connected client. A very basic framework for a client/server architecture.

Let's begin by creating a Console App called ChatServer.

[csharp]
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Collections;

namespace ChatServer {
class ChatServer {
static void Main(string[] args) {
ChatServer chatServer = new ChatServer();
} /* Main */

public ChatServer() { }
} /* ChatServer */
} /* ChatServer */
[/csharp]

We are using the System.Net namespace to be able to listen for connections. And System.Threading because we will launch our Listen method in a separate Thread.

What info do we need? We'll need a serverport to listen on, a TcpListener, a Thread where the listener runs in, and we'll use an ArrayList to contain our connected clients.

[csharp]
private int serverPort;
private Thread listenerThread;
private TcpListener socketListener;
private ArrayList chatClients;
[/csharp]

I made properties of these as well, called ServerPort, ListenThread, ListenSocket and Clients.

In our constructor we will assign the port, start the listener and provide some feedback.

[csharp]
public ChatServer() {
this.ServerPort = 54321;
this.ListenThread = new Thread(new ThreadStart(Listen));
this.ListenThread.Start();
this.Clients = new ArrayList();
this.Output("STATUS", String.Format("ChatServer Started on port {0}.", this.ServerPort));
} /* ChatServer */
[/csharp]

Before we go any further in our ChatServer, we'll create a new class called Connection.

[csharp]
using System;
using System.Net.Sockets;
using System.IO;
using System.Threading;

namespace ChatServer {
public class Connection {
public Connection(TcpClient chatClient, int clientId) {
// TODO
} /* Connection */
} /* Connection */
} /* ChatServer */
[/csharp]

Let's see, what does a Connection really need? We need some way to identify it, so we add a clientNumber. We have a TcpClient where we will get a NetworkStream from to create a StreamWriter and StreamReader. We're also going to launch our listener in a separate Thread.

[csharp]
private int clientNumber;
private Thread listenerThread;
private TcpClient chatConnection;
private NetworkStream chatStream;
private StreamWriter chatWriter;
private StreamReader chatReader;
[/csharp]

These are the following properties: Id, ChatConnection, ChatStream, ChatWriter, ChatReader and ListenThread.

Each Connection will be able to fire of events, so let's create those.

[csharp]
namespace ChatServer {
#region Delegates
public delegate void MessageReceived(string msgText, int chatClient);
public delegate void AddClient(Connection chatConnection);
public delegate void RemoveClient(Connection chatConnection);
#endregion

public class Connection {
#region Events
public event MessageReceived eventReceiving;
public event AddClient eventAdding;
public event RemoveClient eventRemoving;
#endregion
[/csharp]

When we first create a Connection we take a TcpClient and a number assigned to us be the server.

[csharp]
public Connection(TcpClient chatClient, int clientId) {
this.ChatConnection = chatClient;
this.clientNumber = clientId;
} /* Connection */
[/csharp]

After our object is created the server will bind the events and will Connect the client.

[csharp]
public void Connect() {
/* If we implement the listener while loop here,
only one client at a time will be able to send.
Therefore we launch a new Thread that is responsible for one Connection. */

this.ListenThread = new Thread(new ThreadStart(Listen));
this.ListenThread.Start();
if (eventAdding != null) { this.eventAdding(this); }
} /* Connect */
[/csharp]

Let's take a look at the Listen method.

[csharp]
private void Listen() {
this.ChatStream = this.ChatConnection.GetStream();
this.ChatReader = new StreamReader(this.ChatStream);
this.ChatWriter = new StreamWriter(this.ChatStream);
bool connOpen = true;

this.SendMessage(String.Format("Welcome to ChatServer. You are client #{0}.", this.Id));

while (connOpen) {
string chatMsg;
while ((chatMsg = this.ChatReader.ReadLine()) != null) {
if (eventReceiving != null) { this.eventReceiving(chatMsg, this.Id); }
}
this.ChatWriter.Close();
this.ChatReader.Close();
this.ChatConnection.Close();
if (eventRemoving != null) { this.eventRemoving(this); }
connOpen = false;
}
} /* Listen */
[/csharp]

We take our Stream, open a StreamReader and a StreamWriter and we wait for incoming messages, and when the client disconnects we remove ourself from the client list.

We also sent some feedback to the client with a SendMessage method. This is a very simple one, it writes a line to the StreamWriter.

[csharp]
public void SendMessage(string chatMsg) {
/* We check if the Writer already exists, else we get an exception thrown
(which doesn't crash the server thou).
This situation can occur when a client first connects. */

if (this.ChatWriter != null) {
this.ChatWriter.WriteLine(chatMsg);
this.ChatWriter.Flush();
}
} /* SendMessage */
[/csharp]

That's all for our Connection class. Let's go back to our server to set up the Listen method over there.

We want to listen on every address on our pc:

[csharp]
IPAddress ipAddress = Dns.Resolve("0.0.0.0").AddressList[0];
[/csharp]

Then we listen on a specific port and we wait for clients. When a client connects we create an object, assign events and Connect it. And then we wait for another client to connect.

[csharp]
// We listen for connections on all available IPs on port 54321.
this.ListenSocket = new TcpListener(ipAddress, this.ServerPort);
this.ListenSocket.Start();
while(true) {
// For every connection we create a new Connection object.
Connection chatConnection = new Connection(this.ListenSocket.AcceptTcpClient(), this.Clients.Count + 1);
chatConnection.eventReceiving += new MessageReceived(this.MessageReceived);
chatConnection.eventAdding += new AddClient(this.AddClient);
chatConnection.eventRemoving += new RemoveClient(this.RemoveClient);
chatConnection.Connect();
}
[/csharp]

When we receive a message we broadcast it to all other clients.

[csharp]
private void MessageReceived(string chatMessage, int chatClient) {
this.Output("MESSAGE", String.Format("[{0}]: {1}", chatClient, chatMessage));
} /* MessageReceived */

private void Output(string msgStatus, string msgText) {
Console.WriteLine(String.Format("{0}: {1}", msgStatus, msgText));
foreach (Connection chatClient in this.Clients) {
chatClient.SendMessage(msgText);
}
} /* Output */
[/csharp]

Adding and Removing a client is just adding it to an ArrayList and removing it. When we remove it we also set the object to null to get it collected.

That's our class. Now just run the server, telnet to it and chat ;)

Server:


Telnet:


As always I've uploaded the project for you too look at.
 
  • Leave a reply
    Items marked with * are required. (Name, Email, Comment)
    Comment is missing some required fields.
     
     
     
    To make sure you are not a computer, please type in the characters you see.