Tuesday, August 20, 2013

Threaded multi chat room server source code in c++ with MFC Part - 1

Multi-client chat server is open source as I have created it. In this server multiple user can log in and communicate with one another as multicast user. In reality it can use as a chat room server. Can be modified as peer to peer (p2p) text chat. It can also be used as a backbone of a video conferencing project. It has a beautiful and easy to use user interface in MFC. This project is compatible with Microsoft visual studio 2010. I will describe the use of class and function with source code.




Technology used:

i.   C++
ii.   Microsoft visual Studio 2010
iii.  MFC GUI
iv.  Winshock2 Socket
v.   TCP/UDP
vi.  Windows Thread


This project consist of  3 classes.

1. Server.cpp
2. ServerDlg.cpp
3. ServerManager.cpp


Lets start with core portion -- I mean the network level connection portion.

I have used the Winshock2 library for the purpose of network communication.

You can use this library on windows  with MVS 2010 by including library --

# include <winsock2.h>

And as it is multi user  and have GUI so we need thread. For this I have used Windows thread.
You can use this windows thread just by adding ---

# include <Windows.h>

So,  why we are late. Lets start....


ServerManager.cpp  -- core network connection class. In that class I have also demonstrate how to fetch the remote client IP address from a socket connection.


void ServerManager::StartListening(int iPort)
{
    iCount=0;
printf("\nInitialising Winsock...");
    if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)
    {
        printf("Failed. Error Code : %d",WSAGetLastError());
        return;
    }
    
    printf("Initialised.\n");
    
    //Create a socket
    if((s = socket(AF_INET , SOCK_STREAM , 0 )) == INVALID_SOCKET)
    {
        printf("Could not create socket : %d" , WSAGetLastError());
        m_pDialog->ShowServerInfo("Could not create socket");
    }

    printf("Socket created.\n");
    
    //Prepare the sockaddr_in structure
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons( iPort );
    
    //Bind
    if( bind(s ,(struct sockaddr *)&server , sizeof(server)) == SOCKET_ERROR)
    {
        printf("Bind failed with error code : %d" , WSAGetLastError());
        m_pDialog->ShowServerInfo("Bind failed with error code");
        exit(EXIT_FAILURE);
    }
    
    puts("Bind done");

    //Listen to incoming connections
    listen(s , 100);
     char *message;
     puts("Waiting for incoming connections...");
     m_pDialog->ShowServerInfo("Waiting for incoming connections...\n");
     c = sizeof(struct sockaddr_in);
    
    while( (new_socket = accept(s , (struct sockaddr *)&client, &c)) != INVALID_SOCKET )
    {
        puts("Connection accepted");
      
        socklen_t len;
        struct sockaddr_storage addr;
        char ipstr[INET6_ADDRSTRLEN];
        int port;

        len = sizeof addr;
        getpeername(new_socket, (struct sockaddr*)&addr, &len);

        // deal with IPv4:
        if (addr.ss_family == AF_INET) {
            struct sockaddr_in *s = (struct sockaddr_in *)&addr;
            port = ntohs(s->sin_port);
            inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr);
        }

        printf("Peer IP address: %s\n", ipstr);
        m_pDialog->ShowServerInfo("Connected Peer IP address: "+string(ipstr)+"\n");
        CWinThread *cTh = AfxBeginThread(
        DataThreadFunc,
        (LPVOID)new_socket);
        ++iCount;
        sArray[iCount] = new_socket;
    }
    
    if (new_socket == INVALID_SOCKET)
    {
        printf("accept failed with error code : %d" , WSAGetLastError());
        return;
    }
}
StartListening(int iPort) Initiate a socket and Start listening on a particular port for incoming connections.

This bold letter means it can accept multiple connection( client ). In that function we used a static global variable to count the connection globally and a global static SOCKET array to stored the socket's pointer for future use from thread.

How ?

You can see on that function I have used windows thread. Here I accept the incoming connection and make a thread for every connection.

You can see that with the function --


UINT __cdecl ServerManager::DataThreadFunc(LPVOID pParam)
{
    SOCKET pYourSocket = reinterpret_cast<SOCKET>(pParam);
  
    char *message;
    message = "Welcome to Matrix chat room.\n";
    send(pYourSocket , message , strlen(message) , 0);
    char server_reply[2000];
    int recv_size;

    while((recv_size = recv(pYourSocket , server_reply , 2000 , 0)) != SOCKET_ERROR)
    {
        server_reply[recv_size] = '\0';
         for(int i = 1;i<=iCount; i++)
        {
            if( send(sArray[i] , server_reply, recv_size , 0) < 0)
            {
                puts("Send failed");
             }
        }

    }
    return 0;
}
In DataThreadFunc(LPVOID pParam) I have used the winshock send() API to sending the welcome to the client by client socket.
Here I also have a while loop with recv() API which continue to listening for client messages.
This is a static function which is called for every incoming connection for 1 time.

After receiving a message from a client this thread server now sends this message to every socket it has on its SOCKET array by fetching the socket.

NOTE :  Any smart mind can modified this function and can use for p2p message chat.


COMPLETE CODE


 NEXT



4 comments:

  1. Hello Admin. Please send for me code socket this. Because link download dje. Thank
    My email: nguyenlinh20687@gmail.com

    ReplyDelete
    Replies
    1. There it is -- https://github.com/robelsharma/MultiClientChatRoom

      Delete
  2. Hi Robel, I couldn't find Server.exe, can you send me?

    ReplyDelete
    Replies
    1. Here is the github link for binary --
      https://github.com/robelsharma

      Delete

How to Generate and use the ssh key on Gerrit, github.io, gitlab, and bitbucket.

 Details can be found here -