Introduction
In this article I will not focus on the basics of socket programming in .net, because there are already plenty of useful articles covering the basics (e.g Introduction to Socket programming, Sockets in C#, etc), but instead I will focus on the part which those useful articles are not covering and that is "How to develop a socket server program which can be able to be connected by as much client as it wish!", to see how that happen read the rest of the article.
Background
Again I emphasize that this is not about basics, you can seek for them elsewhere, so the background to this article would be your googling for "basics of socket programming in .net".
Using the code
The solution contains two project:
- Server and
- Client
Both of them are console application (with .NET framework 2.0 developed with Visual Studio 2010). First I will demonstrate the Server codes and then the Client code since the later is much more simple.
Server code
As I stated in the Introduction sector, the intention of this article is to give you the idea on how to develop Multi-Client capability to Server socket program. To accomplish this goal, you definitely need to know Thread concepts in C#, here again I will not go on what threads are in C# and how to interact with them, I assume you are familiar with them, after all you can Google it and find out how it works. What I would go through is how to handle multi-client capability with threading. As regards to the fact that for each client in our server to be able to connect to server and have a connection with it we should use "AcceptSocket()" method, therefore for having multiple clients to be able to connect with server and have a connection with it, we should have the same number of AcceptSocket() methods for the desired number of clients. To accomplish this, I would receive the number of clients from the server program at the beginning and then by using Thread, I would establish the same number of threads for that number of client.Here is how I do it in main method:
tcpListener.Start();
Console.WriteLine("************This is Server program************");
Console.WriteLine("Hoe many clients are going to connect to this server?:");
int numberOfClientsYouNeedToConnect =int.Parse( Console.ReadLine());
for (int i = 0; i < numberOfClientsYouNeedToConnect; i++)
{
Thread newThread = new Thread(new ThreadStart(Listeners));
newThread.Start();
}
As you can see from above, I first receive the number of clients and then define that number of Threads and handle the rest of the work with Listeners method which is shown below:
like this:
static void Listeners()
{
Socket socketForClient = tcpListener.AcceptSocket();
if (socketForClient.Connected)
{
Console.WriteLine("Client:"+socketForClient.RemoteEndPoint+" now connected to server.");
NetworkStream networkStream = new NetworkStream(socketForClient);
System.IO.StreamWriter streamWriter =
new System.IO.StreamWriter(networkStream);
System.IO.StreamReader streamReader =
new System.IO.StreamReader(networkStream);
while (true)
{
string theString = streamReader.ReadLine();
Console.WriteLine("Message recieved by client:" + theString);
if (theString == "exit")
break;
}
streamReader.Close();
networkStream.Close();
streamWriter.Close();
}
socketForClient.Close();
Console.WriteLine("Press any key to exit from server program");
Console.ReadKey();
}
In above code, there is only 3 parts that are essential to be understood well enough to grasp the idea. I have bold those:
- AcceptSocket()
- RemoteEndPoint.ToString()
- streamReader.ReadLine()
Needless to say what AcceptSocket() does,(refer to basic articles I introduce at the Introduction section). Note that it is really essential to have AcceptSocket() method in place, since this is the one who is playing the role of accepting client requests. What RemoteEndPoint does is basically it defines the unique identity of the client which is connected to the server and the identifiers format is as:
IP address: Port Number.Finally what streamReader.ReadLine() does is that it waits and
blocks the rest of the execution until it receives a message from the connected client. If you pay attention to the code above, you can see that I have done it in a
While(true) and that is because I want the server to be able to receive as many message from client as clients sends till it receives
exit message from client, which emits to the server to finish the connection with that client.As this code is run under different thread than main application's thread, the entire server application will terminate when the last number of client has connected and issued the exit message.To clarify this more, its better to give an example. Assume that you want the server program to handle 2 clients. what you have to do is first execute server application and then say I want 2 clients to be able to connect to server application, then open 2 client program(by clicking client.exe file twice) and start playing with it, finally when you wish to exit from both of those clients just type exit for both of them and then on server side "press any key" twice and you are done with that.simple!
Now lets jump to Client Code.
Client code
As I mentioned before, Client's code is much more simple than server side, all you have to do is connect to specified IP address and Port of server program. Here is how I do it:
static public void Main(string[] Args)
{
TcpClient socketForServer;
try
{
socketForServer = new TcpClient("localHost", 10);
}
catch
{
Console.WriteLine(
"Failed to connect to server at {0}:999", "localhost");
return;
}
NetworkStream networkStream = socketForServer.GetStream();
System.IO.StreamReader streamReader =
new System.IO.StreamReader(networkStream);
System.IO.StreamWriter streamWriter =
new System.IO.StreamWriter(networkStream);
Console.WriteLine("*******This is client program who is connected to localhost on port No:10*****");
try
{
string outputString;
{
Console.WriteLine("type:");
string str = Console.ReadLine();
while (str != "exit")
{
streamWriter.WriteLine(str);
streamWriter.Flush();
Console.WriteLine("type:");
str = Console.ReadLine();
}
if (str == "exit")
{
streamWriter.WriteLine(str);
streamWriter.Flush();
}
}
}
catch
{
Console.WriteLine("Exception reading from Server");
}
networkStream.Close();
Console.WriteLine("Press any key to exit from client program");
Console.ReadKey();
}
Just like Server code, here again there are three major parts which need attention:
- new TcpClient("localhost" , 10)
- streamWriter.WriteLine(str)
- streamWrite.Flush()
First you need to define for client code that where is the server residing, here we have said it is residing on the local computer (localhost or 127.0.0.1) and then you should say at which port number that server is listening for messages: this is done with new TcpClient("localhost", 10). By the way, 10 is the port number.
Second you need to communicate with server, it is done with streamWriter.WriteLine(str) command, please note that the server will receive your message when streamWriter.Flush() is executed.
That's all folks, hope you get the point.
Entire Server and Client code
As I am the fan of those authors who post their entire code, in that respect you can have both client and server codes from below:
Server Code: (program.cs):
using System;
using System.Net.Sockets;
using System.Threading;
public class AsynchIOServer
{
static TcpListener tcpListener = new TcpListener(10);
static void Listeners()
{
Socket socketForClient = tcpListener.AcceptSocket();
if (socketForClient.Connected)
{
Console.WriteLine("Client:"+socketForClient.RemoteEndPoint+" now connected to server.");
NetworkStream networkStream = new NetworkStream(socketForClient);
System.IO.StreamWriter streamWriter =
new System.IO.StreamWriter(networkStream);
System.IO.StreamReader streamReader =
new System.IO.StreamReader(networkStream);
while (true)
{
string theString = streamReader.ReadLine();
Console.WriteLine("Message recieved by client:" + theString);
if (theString == "exit")
break;
}
streamReader.Close();
networkStream.Close();
streamWriter.Close();
}
socketForClient.Close();
Console.WriteLine("Press any key to exit from server program");
Console.ReadKey();
}
public static void Main()
{
tcpListener.Start();
Console.WriteLine("************This is Server program************");
Console.WriteLine("Hoe many clients are going to connect to this server?:");
int numberOfClientsYouNeedToConnect =int.Parse( Console.ReadLine());
for (int i = 0; i < numberOfClientsYouNeedToConnect; i++)
{
Thread newThread = new Thread(new ThreadStart(Listeners));
newThread.Start();
}
}
}
Client Code: (program.cs):
using System;
using System.Net.Sockets;
using System.Threading;
public class Client
{
static public void Main(string[] Args)
{
TcpClient socketForServer;
try
{
socketForServer = new TcpClient("localHost", 10);
}
catch
{
Console.WriteLine(
"Failed to connect to server at {0}:999", "localhost");
return;
}
NetworkStream networkStream = socketForServer.GetStream();
System.IO.StreamReader streamReader =
new System.IO.StreamReader(networkStream);
System.IO.StreamWriter streamWriter =
new System.IO.StreamWriter(networkStream);
Console.WriteLine("*******This is client program who is connected to localhost on port No:10*****");
try
{
string outputString;
{
Console.WriteLine("type:");
string str = Console.ReadLine();
while (str != "exit")
{
streamWriter.WriteLine(str);
streamWriter.Flush();
Console.WriteLine("type:");
str = Console.ReadLine();
}
if (str == "exit")
{
streamWriter.WriteLine(str);
streamWriter.Flush();
}
}
}
catch
{
Console.WriteLine("Exception reading from Server");
}
networkStream.Close();
Console.WriteLine("Press any key to exit from client program");
Console.ReadKey();
}
private static string GetData()
{
return "ack";
}
}
Points of Interest
I would really appreciate any other ideas regarding the topic.