Creating a Chat System

Environment: VC6 SP3, VC7, Win98, Win2000, WinXP

Introduction

This article demonstrates the technique of creating a Chat System. The article contains a Chat Server and a Chat Client; each perform different tasks and cannot be used interchangeably. Both client and server use Windows Sockets for the purpose of connecting and sending lines of text between themselves; you can use the Server and Client either on your machine or over a private Lan or network to chat.

Note: I do not have access to either a Lan or network, hence this software was only tested in a “Local Loop” on my machine. Anyone testing the software over a network can please send the the test results through e-mail to barretto_vn@mail.com.

How to Use the Chat Server and Chat Client

Preparing to Use ChatServer and Client

  1. Download the ChatServer and ChatClient Source code.
  2. Unzip ChatServer and ChatClient Source code.
  3. Start Visual Studio.
  4. Build the ChatServer and ChatClient executeables.
  5. Exit Visual Studio.

To use the ChatServer and ChatClient on your machine in what is know as a “Local Loop,” do the following:

Starting ChatServer

  1. Start ChatServer (the executeable is in the ChatServer\Debug folder).
  2. Keep the Port Number as 0 and click OK. (Note: If you change the Port Number, the same number must be used when connecting through ChatClient.)
  3. The Port Number can be between 0 to 1499. (This is useful in cases where the Port Number is in use by another application on the Server System.)

Note: Even though the Port Number input is 0, it is in reality 1500 + x (x stands for the Port Number you input—0 in this case).

Starting ChatClient

  1. Start ChatClient (the executeable is in the ChatClient\Debug folder).
  2. From the menu, select Connection->Connect (or click the Connect button).
  3. A Logon Form will be displayed.
  4. In the Server box, type 127.0.0.1.
  5. In the Nickname box, type any name (should be different for each invocation of ChatClient).
  6. In the Port Number box, type 0 (remember that we had started the Chat Server with 0 as the Port Number).
  7. Click OK.

Repeat Steps 1 thru 7 above to start another ChatClient but with a different Nickname. You can start as many ChatClients as you want, restricted only by available memory. Chat to your heart’s content—with yourself, of course.

To Use ChatServer and ChatClient on a network, do the following; carry out the steps in “Preparing to use ChatServer and ChatClient,” above

  • Copy the ChatServer executeable on a Server system.
  • Copy the ChatClient executeables on machines from where you want users to chat.
  • Start ChatServer on the Server system; refer to the “Starting ChatServer” section above.
  • Start ChatClient on the machines where the ChatClient was copied; refer to the “Starting ChatClient” section above, but remember that in the Server Box on the Logon form you have to type the name or IP Address of the Server system on which the ChatServer is running (not 127.0.0.1) and Port Number, as applicable.

Caution

If you copy the ChatServer and ChatClient executeables on machines that do not have Visual Studio set up, the executeables may not run because MFC is not installed by default on the Windows operating system.

The Installable versions of ChatServer and ChatClient are available for download in the Download section; you can install both on any machine running Windows 9x / Windows 2000 / Windows XP because the Installable versions have MFC included.

I may not upload the Installable versions at the present because of their huge size (1.2Mb each); and, to be very frank, I cannot afford it at the present time. Maybe I will over a period of time, if there is a demand for them.

What’s Missing

  • No Coloured Text for chatting
  • No Variable Fonts for chatting
  • Single Chat Room
  • No Threads Used in the Source

Maybe, in a later version, the above could be incorporated; they are presently under testing.

Note for Developers

The most important coding for both the Server and Client is in the CChatServerDoc and CChatClientDoc classes. You would do well to concentrate on these two classes.

A CObject class named CMsg is used in both the Server and Client to send and receive messages back and forth; the messages are then unassembled by the receiver (Server or Client). The CMsg class has the following members:

  • Code (to indicate the type of message)
  • m_strText (message text)
  • m_bClose (sent when the Client is disconnecting or shutting down

Following is a list of codes sent by both Server and Client (you can have your own codes as the need arises):

#define JOINING_CHAT          1
#define LEAVING_CHAT          2
#define SENDING_CHATTERS_LIST 3
#define SENDING_NICKNAME      4
#define NORMAL_MESSAGE        5
#define DUPLICATE_NICKNAME    6

How It Works

After you start the Server by inputting the Port Number and clicking OK, a Socket is created for listening for incoming connect requests using the following code in the member function OnNewDocument():

m_pSocket = new CListeningSocket(this);
if (m_pSocket->Create(Dialog.m_nPort+1500))
{
    if (m_pSocket->Listen())
        return TRUE;
}

When a new connect request is received from a client, the member function OnAccept in the CListeningSocket class is notified by the system. OnAccept then calls the ProcessPendingAccept function in the CChatServerDoc class, as show below:

void CListeningSocket::OnAccept(int nErrorCode)
{
    CSocket::OnAccept(nErrorCode);
    m_pDoc->ProcessPendingAccept();
}

In the ProcessPendingAccept function, a new Socket (CClientSocket) is created for the new client and the created Socket is added to the Pointer List CPtrList, which is a kind of array. We use the pointer list all through the program to retreive the Socket for sending or receving messages. The system then reads the incoming message in the member function OnReceive in the CClientSocket, which in turn calls ProcessPendingRead, sending to it the Client Socket which sent the message, as shown below.

void CClientSocket::OnReceive(int nErrorCode)
{
     CSocket::OnReceive(nErrorCode);
     m_pDoc->ProcessPendingRead(this);
}

Inside ProcessPendingRead, the CMsg object sent by the client is unassembled and, based on the code of the message, action is taken as appropriate.

When a Client Disconnects or Exits by selecting File->Exit, a message is sent to the Server with m_bClose set to TRUE. The Server receives the message and closes the Client Socket as show below:

void CChatServerDoc::CloseSocket(CClientSocket* pSocket)
{
  // Close the Client Socket
  pSocket->Close();
  POSITION pos,temp;
  for(pos = m_connectionList.GetHeadPosition(); pos != NULL;)
  {
    temp = pos;
    // Search for the Socket in the Pointer List
    CClientSocket* pSock = (CClientSocket*)m_connectionList.
                                           GetNext(pos);
    if (pSock == pSocket)
    {
      // Found it, so remove it from the Pointer list
      // so that no more messages are sent to that Client Socket
      m_connectionList.RemoveAt(temp);
      break;
    }
  }
  UpdateConnections();
  delete pSocket;
  if(m_connectionList.GetCount() == 0)
    m_msgList.RemoveAll();
}

If you Exit the server by selecting File->Exit, and if there are Clients connected, all the Clients are notified by the Server that it has shut down and all the CClientSocket(s) are closed. This also prevents the Clients from crashing. Following is the code that carries out the task of closing the Client Sockets:

void CChatServerDoc::DeleteContents()
{
     // You must initilize m_pSocket to NULL in the Constructor of
     // CChatServerDoc, else the server will crash at the start
     if(m_pSocket == (CListeningSocket*)NULL)
        return;

    // Delete the Listening Socket
    delete m_pSocket;
    m_pSocket = NULL;

    CString temp;
    if (temp.LoadString(IDS_SERVERSHUTDOWN))
        m_msgList.AddTail(temp);

    while(!m_connectionList.IsEmpty())
    {
     // Get a Pointer to the Client Socket
      CClientSocket* pSocket = (CClientSocket*)m_connectionList.
                                               RemoveHead();
      CMsg* pMsg = AssembleMsg(pSocket , NORMAL_MESSAGE);
      //Set Code for Closing
      pMsg->m_bClose = TRUE;
      // Send Server ShutDowm message to the Client
      SendMsg(pSocket, pMsg);

      if (!pSocket->IsAborted())
      {
         // Shut down the Client
           pSocket->ShutDown();
         BYTE Buffer[50];
         while (pSocket->Receive(Buffer,50) > 0);
         // Delete the Client Socket
           delete pSocket;
      }
   }

   m_msgList.RemoveAll();

   if (!m_viewList.IsEmpty())
      Message("");

   CDocument::DeleteContents();
}

Have fun, Chat to your heart’s content, but don’t blame me. I developed this by modifying MSDN Sample Chatter and Chatsvr for fun’s sake.

Downloads


Download source – 85 Kb

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read