Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

Multi-client server using C/Windows API

3.75/5 (4 votes)
11 Jan 2013CPOL3 min read 20.5K   1.6K  
This is a simple multiclient server chat on a Local Area Network.

Image 1

Introduction

This article presents a basic multi-client/server chat in C language using Windows APIs. The reason why I am writing this article is when I started learning about sockets, all the tutorials i found on internet were only dealing with console based programs. After getting enough knowledge I decided to code a multi-client/server chat with a graphical user interface in order to make it more attractive and I hope it would help somebody else who has a similar idea but does not know where to start. I'm still a beginner in Networks, Programming ,and even worse, (I am self-educated) since my university did not teach me these things, and i haven't even completed my degree yet. So there might be mistakes and I will really appreciate if you could help to correct those mistakes. Thank you.

The Client

The functions I am using in client code are:

  • init_dll(): initialize ws_32.dll
  • init_connection(): we create a socket (sock) and a variable (sin) of type SOCKADDR_IN that will be used to connect to the server. We retrieve the port number and ip address from ip control and edit control in the user interface. With WSAAsyncselect() I also created a user define message (WM_SOCKET) that I will be using to get notifications of network events such as FD_READ and FD_CLOSE on  sockets.
  • end_connection(): Terminates the use of ws_32.dll
  • reset_controls(): Sets all the controls to their initial state

The connection to the server is made once the user clicks on "connect" button in the user interface. We first call init_dll() then init_connection(). We get the IP address from ip control , and the port number from the edit control (we also check the port number to ensure that it is not a reserved port number). Once the connection is succesfull we can send (by clicking on the send button) and receive a message from the server . When the user clicks on send button, we get the message he has entered from edit control by calling GetDlgItemText(), and we allocate a buffer for storing that message and then we pass that buffer to send() function.

C++
case IDC_SEND :
{
     int n = GetWindowTextLength(GetDlgItem(hWnd, IDC_TO_SERVER));
 
     if(n == 0)
     {
         MessageBox(hWnd , TEXT("You must enter a message") , 
                    TEXT("Hey!") , 0);
         break;
     }
 
     char *buffer = (char*)GlobalAlloc(GPTR, n + 1);
     GetDlgItemText(hWnd , IDC_TO_SERVER , buffer , n+1);
     send(sock , buffer , strlen(buffer) , 0);
     GlobalFree((HANDLE)buffer);
     SendDlgItemMessage(hWnd , IDC_TO_SERVER , WM_SETTEXT , 0 , reinterpret_cast<LPARAM>("")); } break;  

Whenever we get a message from the server, an FD_READ message is sent to our Windows procedure and we retrieve the message using recv() function. FD_CLOSE is sent whenever the server is no longer available.

C#
case FD_READ: {
   int n = recv(sock , buffer1 , sizeof(buffer1) - 1 , 0);

   buffer1[n] = 0;
   strcat(buffer2 , buffer1);
   SendDlgItemMessage(hWnd , IDC_LISTBOX , LB_ADDSTRING , 0 , 
                      reinterpret_cast<LPARAM>(buffer2));
}
break;

case FD_CLOSE :
{
   SendDlgItemMessage(hWnd , IDC_LISTBOX , LB_RESETCONTENT , 0 , 0);
   SendDlgItemMessage(hWnd , IDC_LISTBOX , LB_ADDSTRING , 0 , 
        reinterpret_cast<LPARAM>("\r\nserver closed connection"));
   reset_controls(hWnd);
   end_connection();
}
break;

The Server

The server has similar functions that I have already mentioned in Client part, so I will just quickly mention those functions name :

- init_dll()
- init_conection()
- end_connection()

What we do new in the server part is that, when a client initiates a connection, we retrieve his ip address and the port number he is using then we send a message to the list box to say that a client has connected to server. We also send his name (defined by his IP address and port number)  to the combo box and we attach the socket descriptor to that name in the combobox so that we will be able to retrieve it whenever we want to send a message to that specific client.

C++
int index =  SendDlgItemMessage(hWnd , IDC_SELECT_CLIENT , 
       CB_ADDSTRING , 0 , reinterpret_cast<LPARAM>(buffer)); // Add client to combobox

// Bind socket descriptor to address in combobox
SendDlgItemMessage(hWnd , IDC_SELECT_CLIENT , CB_SETITEMDATA , (WPARAM)index , (LPARAM)s_client);
Button_Enable(GetDlgItem(hWnd , IDC_SELECT_CLIENT) , TRUE);  // eNABLE cLIENT LIST
strcat(buffer , "  is connected");
SendDlgItemMessage(hWnd , IDC_LISTBOX , LB_ADDSTRING , 0 , reinterpret_cast<LPARAM>(buffer)); 

Whenever we get an FD_READ notification we loop through all the socket descriptors that we initially bounded in combo box in order to find out which client sent the message. Once we get that client who sent the message, we store his message in buffer1 , we format it and display it in the listbox.

C++
for(i = 0 ; i < n_client; i++)
{
  s_client = SendDlgItemMessage(hWnd , IDC_SELECT_CLIENT , CB_GETITEMDATA , (WPARAM)i , 0);

  if(recv(s_client , buffer1 , sizeof(buffer1) - 1 , 0) != -1)
  {
     getpeername(s_client , (SOCKADDR*)&clt , &clt_size);        // Get client address
     sprintf(buffer_client_id , "%s",inet_ntoa(clt.sin_addr));  // copy client address into buffer_client

     strcat(buffer_client_id , "@Port:");
     sprintf(buffer2, "%d",clt.sin_port);
     strcat(buffer_client_id,buffer2);

     strcat(buffer_client_id , " : ");
     strcat(buffer_client_id , buffer1);

     SendDlgItemMessage(hWnd , IDC_LISTBOX , LB_ADDSTRING , 0 , reinterpret_cast<LPARAM>(buffer_client_id));
    }
}

Points of Interest

As I said earlier , this is my first post , I might have done few things the wrong way and I would really appreciate if you have a better idea to improve my code. Thank you.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)