Chat Server Through Socket Programming


Sockets is an endpoint of network communication. Windows sockets is an effective way to communicate between two ports. In this article we will create a chat server and client using functionality provided by CSockets and CAsyncSocket classes.

Create a dialog based application called socket. Check 'Windows Sockets' check box in 'Step 2 of 4' of the AppWizard. Add controls to the dialog template as given below:


The controls, their IDs and variables associated with them are given in the following table.

 

Control

ID

Variable

Type

Client radio button

IDC_RADIO1

 

 

Server radio button

IDC_RADIO2

 

 

Server Name edit box

IDC_SERVER

m_server

Value (CString)

Port edit box

IDC_PORT

m_port

Value (int)

Connect button

IDC_CONNECT

 

 

Message edit box

IDC_MESSAGE

m_message

Value (CString)

Send button

IDC_SEND

 

 

List box

IDC_LIST

m_list

Control (CListBox)

 

Now insert a class by selecting ‘Insert | New Class’. Give the class name as mysocket and select CSocket as the base class. Add a private data member m_pwnd of type CDialog* to the mysocket class.

Add two objects of mysocket class m_listensocket and m_connectsocket to the CSocketDlg class. Modify the CSocketDlg::OnInitDialog( ) function as given below:

BOOL CSocketDlg::OnInitDialog( )

{

    // Code added by AppWizard

    m_server = "user12" ;

    m_port = 100 ;

    UpdateData ( FALSE ) ;

    CheckRadioButton ( IDC_RADIO1, IDC_RADIO2, IDC_RADIO2 ) ;

    m_listensocket.setparent ( this ) ;

    m_connectsocket.setparent ( this ) ;

}

 

Here, we have initialised the variables and displayed the values in the controls by calling UpdateData ( FALSE ). Of the two radio buttons we have set the 'Server' radio button as default selection. Next, we have called mysock::setparent( ) function. Add this member function and add the code as given below:

void mysocket::setparent ( CDialog *pwnd )

{

    m_pwnd = pwnd ;

}

 

In this function, we have stored address of the dialog box object in the pointer declared as a data member of mysocket class since we wish to call member functions of CSocketDlg class using this pointer.

Add a handler OnConnect( ) for ‘Connect’ button and add the code in it as given below:

void CSocketDlg::OnConnect( )

{

    UpdateData ( TRUE ) ;

    int checked = GetCheckedRadioButton ( IDC_RADIO1, IDC_RADIO2 ) ;

    if ( checked == IDC_RADIO1 )

    {

        m_connectsocket.Create( ) ;

        m_connectsocket.Connect ( m_server, m_port ) ;

    }

    else

    {

        m_listensocket.Create ( m_port );

        m_listensocket.Listen( ) ;

    }

    GetDlgItem ( IDC_CONNECT ) -> EnableWindow ( FALSE ) ;

}

 

Here, firstly we have ascertained the type of the socket user has selected. Then we have called mysocket::Create( ) function to create the Windows socket. Generally, client calls Create( ) function with the default parameters, whereas, the server should provide port number to Create( ). For the server socket, we have called mysocket::Listen( ) function to listen for incoming connection requests by client. For the client socket we have called mysocket::Connect( ) function to connect the socket object to a server socket.

To prevent the user from clicking the ‘Connect’ button again, we have disabled it through a call to CWnd::EnableWindow( ).

Add a member function onsettype( ) to the dialog class. The function is given below:

void CSocketDlg::onsettype ( UINT id )

{

    if ( id == IDC_RADIO1 )

        GetDlgItem ( IDC_CONNECT ) -> SetWindowText ( "Connect" ) ;

    else

        GetDlgItem ( IDC_CONNECT ) -> SetWindowText ( "Listen" ) ;

}

Here, we have simply set the title of the button according to the type of socket selected by the user. Add a message map entry for this function as given below:

ON_COMMAND_RANGE ( IDC_RADIO1, IDC_RADIO2, onsettype )

Add a button handler OnSend( ) for the ‘Send’ button. The code of OnSend( ) button is given below:

void CSocketDlg::OnSend( )

{

    int len ;

    int sent ;

    UpdateData(TRUE) ;

    if ( m_message != " " )

    {

        len = m_message.GetLength( ) ;

        sent = m_connectsocket.Send ( LPCTSTR ( m_message ), len ) ;

        if ( sent == SOCKET_ERROR )

        {

            MessageBox ( "Error in connection" ) ;

        }

        else

        {

            m_list.AddString ( m_message ) ;

            UpdateData ( FALSE ) ;

        }

    }

}

 

Here, we have sent the message given by the user on the connected socket using Send( ) function. Calling UpdateData ( TRUE ) will copy the message entered in the edit box to m_message which is then added to the list box by calling CListBox::AddString( ) function.

Add two more member functions to the CSocketDlg class. They are given below:

void CSocketDlg::onaccept( )

{

    m_listensocket.Accept ( m_connectsocket ) ;

}

void CSocketDlg::onreceive( )

{

    char *buff = new char[512] ;

    int size = 512 ;

    int recd ;

    CString recdstr ;

    recd = m_connectsocket.Receive (buff, size ) ;

    if ( recd == SOCKET_ERROR )

    {

        MessageBox("Error in recieve ");

    }

    else

    {

        buff[recd] = NULL ;

        recdstr = buff ;

        m_list.AddString ( recdstr ) ;

        UpdateData ( FALSE ) ;

    }

}

 

In onaccept( ) function we have called CSocket::Accept( ) function which extracts the first connection in the queue of pending connections, creates a new socket with the same properties as this socket, and attaches it to the socket object passed to it. In onreceive( ) function we have called CSocket::Receive( ) function which receives data from the socket. The message received is then displayed in the list box. We will call the functions onaccept( ) and onreceive( ) from the member functions OnAccept( ) and OnReceive( ) of mysocket class. Add these as virtual functions to the mysocket class. For this right click the class name in the ClassView tab and select ‘Add Virtual Function’ from popup menu. Add the code to these functions as given below:

void mysocket::OnAccept ( int nErrorCode )

{

    if ( nErrorCode == 0 )

        ( ( CSocketDlg* ) m_pWnd ) -> onaccept( ) ;
  
CSocket::OnAccept ( nErrorCode ) ;

}

void mysocket::OnReceive ( int nErrorCode )

{

    if ( nErrorCode == 0 )

        ( ( CSocketDlg* ) m_pWnd ) -> onreceive( ) ;

    CSocket::OnReceive(nErrorCode);

}

We have called onaccept( ) and onrecive( ) member functions using m_pwnd pointer which is already initialised in the setparent( ) function. #include ‘socketDlg.h’ file in ‘mysocket.h’ to make available the CSocketDlg class.

How to run the program

Copy ‘socket.exe’ file on two or more machines. On any one machine where all messages should receive select ‘Server’ radio button. Enter the server name and port number (Port number is any integer number). Click the ‘Listen’ button. On all other machines give server name and port and click ‘Connect’ button. All the users should give same server name, but the port number should be different for every client. For example, if three clients A, B and C are attached to the server three instances of the application must be started on the server. Suppose the three instances give port numbers as 80, 90 and 100 then A, B and C should also give the same port numbers. Now sockets are ready to send and receive the data across the machines. Firstly the client must enter message and click ‘Send’ button. When client sends data for the first time server accepts the connection by calling OnAccept( ) function. The OnReceived( ) function is called every time data is received by the sockets.


Download