Click here to Skip to main content
15,881,248 members
Articles / Desktop Programming / Windows Forms
Article

A simple TCP/IP Chat client/server

Rate me:
Please Sign up or sign in to vote.
1.38/5 (18 votes)
3 Nov 20043 min read 187.8K   12.5K   52   14
An article useful to understand simple thread use, TCP/IP networking, and basic use of XML.

Sample Image - shot1.jpg

You can find updates here.

Introduction

This is a simple TCP/IP chat sample. The solution is made up of three different projects. The main project is "Listener", where you can find the core of this program. In Rete.cs, there are the main methods used to make this program work.

Please note that source code comments (//) are written in Italian.

The most important class is Poller, in rete.cs. In Main.cs is created a Poller object, that is used to connect, poll and write the socket.

In Main.cs is started a thread that has to refresh the GUI; the thread executes:

C#
public void StartGUIRefreshThread()
{
    ThreadStart GuiRefreshDelegate=new ThreadStart(this.GUIRefreshThread);
    GUIRefresh= new Thread(GuiRefreshDelegate);
    GUIRefresh.Name="GUIRefresh";
    GUIRefresh.Start();
}

This is a simple example of using threads. Starting a new thread means running different portions of the code at "the same time". While a thread is running a loop, the "main program" which is another thread executes other instructions.

A ThreadStart object contains a reference to a method that has to be run when the thread starts. If this function does not contain a loop, the thread will stop after a while. If the referenced method contains an infinite loop, the thread will be running as long as the application.

GUIRefresh.Start(); executes the "this.GUIRefreshThread()" method, as specified in "new ThreadStart(this.GUIRefreshThread);".

My "StartGUIRefreshThread" method starts a thread which contains an infinite loop:

C#
public void GUIRefreshThread()
{
    int localDelay, totalDelay=1200, newDataPresentIconDelay=300;

    while (true)
    {
        ...
        Thread.Sleep(localDelay);
    }
}

This checks the Poller status, and displays the appropriate output to the user.

The Poller object handles the TCP connection, using a "raw" Socket object, initialized with the "InizializzaTX()" method if the current application is running as a client, or with "InizializzaRX" if the application is running as a server (listener).

Server case:

C#
public bool InizializzaRX()
{
    if (this.IsListen)
        this.FermaListening();

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

    try
    {
        this.ListenSocket.Bind(LIPE);
        this.ListenSocket.Listen(10);
        this.IsListen=true;
        return true;
    }
    
    ...

This code creates a new socket (this.ListenSocket) which is bound to the listening IP address and port (LIPE).

Now the socket is properly configured, but it's not "active". We have to start it to begin listening with the <Socket>.Accept() method, which puts the socket in a "wait state" for client connections.

The propriety <Socket>.Blocking determines whether the execution will stop waiting for client connections or not (true=stop). I use blocking=true. Note that if you use blocking=true in the main thread, your application will hang until a connection is accepted.

C#
private void PollingRX()
{
    string s="Inizio";
    while (s!=MSGTag.ConnectionStop+"\n")
    {
        if (this.UsedSocket==null&&this.ListenSocket!=null)
        {
            this.DebugRefresh("PollingRX: fermo in accept() su"+ this.LIPE.Port);
            this.IsListen=true;
            this.UsedSocket=this.ListenSocket.Accept();
            this.DebugRefresh("PollingRX: Accept() eseguita");
            this.IsConnectedAsRX=true;

            //Comunica il nome al client, come in InizializzaTX() 
            //il client comunica il suo nome al server
            this.PushString(MSGTag.UserName/*+"Server "*/+this.LocalName);
            this.RTM.RemoteIP=this.UsedScocket.RemoteEndPoint.ToString();
            this.RTM.LocalIP=this.LIPE.ToString();
            //Non uso UsedScocket.LocalEndPoint xchè non è definito l'IP locale

            this.ControlClear();
        }

        s=this.CheckAndRetrive();
        int n=0;
        if (s!=null)
            n=s.Length;

        this.DebugRefresh("PollingRX: eseguito ("+n+" bytes)");
        System.Threading.Thread.Sleep(500);
    }
    this.DebugRefresh("PollingRX: ricevuto EXIT");
    this.KillRX();
}

When a client makes a connection, the Accept() returns a new socket, connected to the client, which is used to transfer data, using my CheckAndRetrive() (read from socket) and PushString() (write to socket) methods. See below to read pertinent code.

This loop continues until a "stop string" is received from the client.

Client case:

C#
public void InizializzaTX()
{
    if ((!this.IsConnected)) 
    {
        try
        {
            this.UsedScocket = new Socket(AddressFamily.InterNetwork, 
                               SocketType.Stream, ProtocolType.Tcp);

            this.UsedScocket.Connect(this.RIPE);
            this.IsConnectedAsTX=true;
            this.PushString(MSGTag.UserName+/*"Client "+*/this.LocalName);

            this.ControlClear();
            //E'= a RIPE
            this.RTM.RemoteIP=this.UsedScocket.RemoteEndPoint.ToString();
            this.RTM.LocalIP=this.UsedScocket.LocalEndPoint.ToString();
        }
        ...

This is the dual of RX: a new socket is created, and a connection to a remote IP and port (RIPE) is created.

C#
private void PollingTX()
{
    string s="inizio";
    while (s!=MSGTag.ConnectionStop+"\n")
    {
        if (this.UsedScocket!=null)
        {
            s=this.CheckAndRetrive();
        }
        int n=0;
        if (s!=null)
            n=s.Length;
        this.DebugRefresh("PollingTX: eseguito("+n+" bytes)");
        System.Threading.Thread.Sleep(500);
    }
    this.DebugRefresh("PollingTX: ricevuto EXIT");
    this.KillTX();
}

Very similar to RX... once the socket is connected, communication is "symmetric".

Writing and Reading data

This is how I get a string from the socket (CheckAndRetrive() uses this function to get data from the socket):

C#
public static string GetStringFromSocket(Socket s)
{
    Byte[] recBytes=new Byte[10001];
    int i=0; 
    string st=null;

    if (s.Available>0)
    {
        i=s.Receive(recBytes,0,s.Available,SocketFlags.None);
        char[] ac=Encoding.UTF8.GetChars(recBytes,0,i);
        foreach (char c in ac)
        {
            st+=c.ToString();
        }
    }
    return st;
}

The socket needs to be a connected socket; you have to use a byte array to put in the received data. Then you have to encode it with a encoding mechanism, the same is used to decode the data. I use UTF8 Encoding.

This reads at most a 10001 bytes block from the socket. To read more bytes, a loop is required, something like:

C#
while (s.Available>0){...}

This is how to put a string in a socket:

C#
public static int PutStringInSocket(Socket s, string msg)
{
    Byte[] sendBytes=Encoding.UTF8.GetBytes(Framing.IncapsulaMSG(msg));
    if (s.Connected)
        return s.Send(sendBytes);
    else return 0;
}

Framing.IncapsulaMSG() is simply a method that writes tags in a message to make it simple to interpret it, and returns a string.

Polling Threads

Inside Poller are executed other threads, responsible for receiving/checking and sending data over the TCP connection. Loops inside PollingRX() and PollingTX() run in two different threads.

C#
public bool StartPollingTX()
{
    try
    {
        if (!this.IsRunningPollingTX)
        {
            ThreadStart TXPollingDelegate = new ThreadStart(this.PollingTX);
            TXPolling= new Thread(TXPollingDelegate);
            TXPolling.Name="TXPolling";
            TXPolling.Start();
            this.IsRunningPollingTX=true;
            this.DebugRefresh("PollingTX: (Start) eseguito");
        }
        return true;
    }
    catch
    {
        return false;
    }
}

This is an example that shows how the "Transmission" thread is started. (It runs "this.PollingTX" described above.) The Settings tab is stored in a separate DLL, and so is the config utility. Settings are stored in an XML file.

I know there are many silly things, but this was intended only as an example.

I hope you enjoyed the article.

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
Web Developer
Italy Italy
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionCODICE PER IP STATICO NELLA CHAT, AIUTOO Pin
antonino09116-Aug-11 21:56
antonino09116-Aug-11 21:56 
AnswerRe: CODICE PER IP STATICO NELLA CHAT, AIUTOO Pin
GMachiavelli17-Aug-11 10:20
GMachiavelli17-Aug-11 10:20 
GeneralNice Work Pin
slimtugo11-Jul-09 9:12
slimtugo11-Jul-09 9:12 
GeneralDevelop a simple chat application consisting of a client and a server using tcp Pin
cuzco2314-Mar-07 17:35
cuzco2314-Mar-07 17:35 
GeneralRe: Develop a simple chat application consisting of a client and a server using tcp Pin
GMachiavelli23-Apr-07 8:36
GMachiavelli23-Apr-07 8:36 
Questionare there already comments in English available? Pin
nulkoud1-Nov-05 1:34
nulkoud1-Nov-05 1:34 
AnswerRe: are there already comments in English available? Pin
GMachiavelli1-Nov-05 4:42
GMachiavelli1-Nov-05 4:42 
QuestionCould you comment it in English? Pin
Yulianto.24-Feb-05 17:57
Yulianto.24-Feb-05 17:57 
AnswerRe: Could you comment it in English? Pin
GMachiavelli24-Feb-05 22:37
GMachiavelli24-Feb-05 22:37 
GeneralRe: Could you comment it in English? Pin
nsimeonov28-Apr-05 11:24
nsimeonov28-Apr-05 11:24 
GeneralRe: Could you comment it in English? Pin
Joe248594-Jun-05 23:41
Joe248594-Jun-05 23:41 
GeneralRe: Could you comment it in English? Pin
nsimeonov5-Jun-05 0:01
nsimeonov5-Jun-05 0:01 
GeneralNon male ! Pin
Frank Olorin Rizzi4-Nov-04 4:18
Frank Olorin Rizzi4-Nov-04 4:18 
GeneralRe: Non male ! Pin
GMachiavelli5-Nov-04 3:29
GMachiavelli5-Nov-04 3:29 

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.