A Simple C++/SDL_Net Chat Server & Client

Update – Nov 2011: I’ve refactored this code into something significantly easier to work with, modify and extend – so you should probably try this instead: https://r3dux.org/2011/11/a-simple-csdl_net-chat-server-client-rewritten/.

I was due to be teaching some network programming in the new term, so I thought I’d try to find the simplest library for cross-platform socket programming and knock up some examples that I could teach from. The trouble is, there are lots of different socket libraries… Lots and lots. So I did a bit of investigating and came up with the following:

Library Good Points Bad Points
Boost ASIO
  • The real deal – a world-class, well documented socket library
  • Comprehensive – you can do absolutely anything with it
  • More complicated than I’d like for the level I’m teaching at – I’m not convinced they’ll get it
  • You need to include the boost library in each project, which makes each project big (like 60MB+ big)
C++ Sockets Library
  • Lots of documentation
  • Appears to be very solid from all accounts
  • I couldn’t get it to play ball!
  • Pretty small
  • Works without you having to jump through too many hoops
  • Decent level of abstraction without the need to mess about with too much C (as opposed to C++) stuff
  • Does not require SDL – SDL_net can be happily used standalone
  • Good documentation available at:
  • You do still need to transfer strings to char array pointers
  • Resolving hosts and IPs puts the details in Network Byte Order (i.e. Big Endian) numbers, which you then have to jump through hoops to retrieve as human-readable values, so you end up doing stuff like hacking a unsigned 32-bit number into an array of four 8-bit numbers to get the dot-quad IP address etc.
NetLink Socket Library
  • Small!
  • No documentation at all (that I could find), which could be because…
  • …netlink is already the name of a *nix socket mechanism which transfers data between kernel-space and user-space – why the hell would you name your C++ socket library with a name which is already used for a different type of socket communication? Fail.
SimpleSockets (now defunct)
  • ?!?
  • Surprisingly for a library called SimpleSockets, not that simple – lots of memset and memcpy stuff needed

In the end I chose SDL_net as teh winnah, as I’d already done some SDL stuff with the class previously (before switching to GLFW to minimise code-bloat), and I managed to get SDL_net up and communicating pretty easily. So, for the next couple of days I put together some simple client/server examples, culminating in the code you’ll find below, which is a (very) simple IRC-esque chat server.

Have a look – you can tell what it’s doing from the output in the windows:

SDL_net Client-Server Example
The server is in the middle, the clients connect in, and chat commences (click for larger, more legible version)

Although I’ve written this code in Linux, as SDL_net is cross-platform, this code should be cross platform – only there are two tweaks you’ll need to make for it to work in Windows: I’ve used some custom kbhit and getch functions to check for a keypress and read what key was pressed, if one was. These functions come natively with Windows, so you should use the native versions and strip the custom ones out.

With those small changes made this should work fine in Windows – but if it doesn’t please feel free to fix it yourself and send me the changes you made :)

As the code for all this is a couple of hundred lines for each for the client and server I’ve put all the source after the jump. Part of the reason it’s so large is that I’ve commented it to the hilt (no really, I’ve gone to town on it – even by my exceptionally verbose standards) as it was originally meant to be a teaching aid. Also, I’ve also left a lot of commented-out debug code in there, so you can uncomment it if you’d like to see exactly what’s going on behind the scenes.

Anyways, I hope this is of use to someone starting off socket programming with SDL_net – and if you have any issues or such please feel free to sling a comment in the article and I’ll do my best to help out.


SDL_net Server:

  1. // SDL_net Server | r3dux.org | 14/01/2011
  3. #include <iostream>
  4. #include <cstdlib>
  5. #include <string>
  7. #include <SDL_net.h>
  9. using namespace std;
  11. const unsigned short PORT        = 1234;            // The port our server will listen for incoming connecions on
  12. const unsigned short BUFFER_SIZE = 512;             // Size of our message buffer
  13. const unsigned short MAX_SOCKETS = 4;               // Max number of sockets
  14. const unsigned short MAX_CLIENTS = MAX_SOCKETS - 1; // Max number of clients in our socket set (-1 because server's listening socket takes the 1st socket in the set)
  16. // Messages to send back to any connecting client to let them know if we can accept the connection or not
  17. const string SERVER_NOT_FULL = "OK";
  18. const string SERVER_FULL     = "FULL";
  20. int main(int argc, char **argv)
  21. {
  22.     IPaddress serverIP;                  // The IP of the server (this will end up being - which means roughly "any IP address")
  23.     TCPsocket serverSocket;              // The server socket that clients will use to connect to us
  24.     TCPsocket clientSocket[MAX_CLIENTS]; // An array of sockets for the clients, we don't include the server socket (it's specified separately in the line above)
  25.     bool      socketIsFree[MAX_CLIENTS]; // An array of flags to keep track of which client sockets are free (so we know whether we can use the socket for a new client connection or not)
  27.     char buffer[BUFFER_SIZE];            // Array of characters used to store the messages we receive
  28.     int receivedByteCount = 0;           // A variable to keep track of how many bytes (i.e. characters) we need to read for any given incoming message i.e. the size of the incoming data
  30.     int clientCount = 0;                 // Count of how many clients are currently connected to the server
  32.     bool shutdownServer = false;         // Flag to control when to shut down the server
  34.     // Initialise SDL_net (Note: We don't initialise or use normal SDL at all - only the SDL_net library!)
  35.     if (SDLNet_Init() == -1)
  36.     {
  37.         cout << "Failed to intialise SDL_net: " << SDLNet_GetError() << endl;
  38.         exit(-1); // Quit!
  39.     }
  41.     // Create the socket set with enough space to store our desired number of connections (i.e. sockets)
  42.     SDLNet_SocketSet socketSet = SDLNet_AllocSocketSet(MAX_SOCKETS);
  43.     if (socketSet == NULL)
  44.     {
  45.         cout << "Failed to allocate the socket set: " << SDLNet_GetError() << "\n";
  46.         exit(-1); // Quit!
  47.     }
  48.     else
  49.     {
  50.         cout << "Allocated socket set with size:  " << MAX_SOCKETS << ", of which " << MAX_CLIENTS << " are availble for use by clients." <<  endl;
  51.     }
  53.     // Initialize all the client sockets (i.e. blank them ready for use!)
  54.     for (int loop = 0; loop < MAX_CLIENTS; loop++)
  55.     {
  56.         clientSocket[loop] = NULL;
  57.         socketIsFree[loop] = true; // Set all our sockets to be free (i.e. available for use for new client connections)
  58.     }
  60.     // Try to resolve the provided server hostname. If successful, this places the connection details in the serverIP object and creates a listening port on the provided port number
  61.     // Note: Passing the second parameter as "NULL" means "make a listening port". SDLNet_ResolveHost returns one of two values: -1 if resolving failed, and 0 if resolving was successful
  62.     int hostResolved = SDLNet_ResolveHost(&serverIP, NULL, PORT);
  64.     if (hostResolved == -1)
  65.     {
  66.         cout << "Failed to resolve the server host: " << SDLNet_GetError() << endl;
  67.     }
  68.     else // If we resolved the host successfully, output the details
  69.     {
  70.         // Get our IP address in proper dot-quad format by breaking up the 32-bit unsigned host address and splitting it into an array of four 8-bit unsigned numbers...
  71.         Uint8 * dotQuad = (Uint8*)&serverIP.host;
  73.         //... and then outputting them cast to integers. Then read the last 16 bits of the serverIP object to get the port number
  74.         cout << "Successfully resolved server host to IP: " << (unsigned short)dotQuad[0] << "." << (unsigned short)dotQuad[1] << "." << (unsigned short)dotQuad[2] << "." << (unsigned short)dotQuad[3];
  75.         cout << " port " << SDLNet_Read16(&serverIP.port) << endl << endl;
  76.     }
  78.     // Try to open the server socket
  79.     serverSocket = SDLNet_TCP_Open(&serverIP);
  81.     if (!serverSocket)
  82.     {
  83.         cout << "Failed to open the server socket: " << SDLNet_GetError() << "\n";
  84.         exit(-1);
  85.     }
  86.     else
  87.     {
  88.         cout << "Sucessfully created server socket." << endl;
  89.     }
  91.     // Add our server socket to the socket set
  92.     SDLNet_TCP_AddSocket(socketSet, serverSocket);
  94.     cout << "Awaiting clients..." << endl;
  96.     // Main loop...
  97.     do
  98.     {
  99.         // Check for activity on the entire socket set. The second parameter is the number of milliseconds to wait for.
  100.         // For the wait-time, 0 means do not wait (high CPU!), -1 means wait for up to 49 days (no, really), and any other number is a number of milliseconds, i.e. 5000 means wait for 5 seconds
  101.         int numActiveSockets = SDLNet_CheckSockets(socketSet, 0);
  103.         if (numActiveSockets != 0)
  104.         {
  105.             cout << "There are currently " << numActiveSockets << " socket(s) with data to be processed." << endl;
  106.         }
  108.         // Check if our server socket has received any data
  109.         // Note: SocketReady can only be called on a socket which is part of a set and that has CheckSockets called on it (the set, that is)
  110.         // SDLNet_SocketRead returns non-zero for activity, and zero is returned for no activity. Which is a bit bass-ackwards IMHO, but there you go.
  111.         int serverSocketActivity = SDLNet_SocketReady(serverSocket);
  113.         // If there is activity on our server socket (i.e. a client has trasmitted data to us) then...
  114.         if (serverSocketActivity != 0)
  115.         {
  116.             // If we have room for more clients...
  117.             if (clientCount < MAX_CLIENTS)
  118.             {
  120.                 // Find the first free socket in our array of client sockets
  121.                 int freeSpot = -99;
  122.                 for (int loop = 0; loop < MAX_CLIENTS; loop++)
  123.                 {
  124.                     if (socketIsFree[loop] == true)
  125.                     {
  126.                         //cout << "Found a free spot at element: " << loop << endl;
  127.                         socketIsFree[loop] = false; // Set the socket to be taken
  128.                         freeSpot = loop;            // Keep the location to add our connection at that index in the array of client sockets
  129.                         break;                      // Break out of the loop straight away
  130.                     }
  131.                 }
  133.                 // ...accept the client connection and then...
  134.                 clientSocket[freeSpot] = SDLNet_TCP_Accept(serverSocket);
  136.                 // ...add the new client socket to the socket set (i.e. the list of sockets we check for activity)
  137.                 SDLNet_TCP_AddSocket(socketSet, clientSocket[freeSpot]);
  139.                 // Increase our client count
  140.                 clientCount++;
  142.                 // Send a message to the client saying "OK" to indicate the incoming connection has been accepted
  143.                 strcpy( buffer, SERVER_NOT_FULL.c_str() );
  144.                 int msgLength = strlen(buffer) + 1;
  145.                 SDLNet_TCP_Send(clientSocket[freeSpot], (void *)buffer, msgLength);
  147.                 cout << "Client connected. There are now " << clientCount << " client(s) connected." << endl << endl;
  148.             }
  149.             else // If we don't have room for new clients...
  150.             {
  151.                 cout << "*** Maximum client count reached - rejecting client connection ***" << endl;
  153.                 // Accept the client connection to clear it from the incoming connections list
  154.                 TCPsocket tempSock = SDLNet_TCP_Accept(serverSocket);
  156.                 // Send a message to the client saying "FULL" to tell the client to go away
  157.                 strcpy( buffer, SERVER_FULL.c_str() );
  158.                 int msgLength = strlen(buffer) + 1;
  159.                 SDLNet_TCP_Send(tempSock, (void *)buffer, msgLength);
  161.                 // Shutdown, disconnect, and close the socket to the client
  162.                 SDLNet_TCP_Close(tempSock);
  163.             }
  165.         } // End of if server socket is has activity check
  167.         // Loop to check all possible client sockets for activity
  168.         for (int clientNumber = 0; clientNumber < MAX_CLIENTS; clientNumber++)
  169.         {
  170.             // If the socket is ready (i.e. it has data we can read)... (SDLNet_SocketReady returns non-zero if there is activity on the socket, and zero if there is no activity)
  171.             int clientSocketActivity = SDLNet_SocketReady(clientSocket[clientNumber]);
  173.             //cout << "Just checked client number " << clientNumber << " and received activity status is: " << clientSocketActivity << endl;
  175.             // If there is any activity on the client socket...
  176.             if (clientSocketActivity != 0)
  177.             {
  178.                 // Check if the client socket has transmitted any data by reading from the socket and placing it in the buffer character array
  179.                 receivedByteCount = SDLNet_TCP_Recv(clientSocket[clientNumber], buffer, BUFFER_SIZE);
  181.                 // If there's activity, but we didn't read anything from the client socket, then the client has disconnected...
  182.                 if (receivedByteCount <= 0)
  183.                 {
  184.                     //...so output a suitable message and then...
  185.                     cout << "Client " << clientNumber << " disconnected." << endl << endl;
  187.                     //... remove the socket from the socket set, then close and reset the socket ready for re-use and finally...
  188.                     SDLNet_TCP_DelSocket(socketSet, clientSocket[clientNumber]);
  189.                     SDLNet_TCP_Close(clientSocket[clientNumber]);
  190.                     clientSocket[clientNumber] = NULL;
  192.                     // ...free up their slot so it can be reused...
  193.                     socketIsFree[clientNumber] = true;
  195.                     // ...and decrement the count of connected clients.
  196.                     clientCount--;
  198.                     cout << "Server is now connected to: " << clientCount << " client(s)." << endl << endl;
  199.                 }
  200.                 else // If we read some data from the client socket...
  201.                 {
  202.                     // Output the message the server received to the screen
  203.                     cout << "Received: >>>> " << buffer << " from client number: " << clientNumber << endl;
  205.                     // Send message to all other connected clients
  206.                     int originatingClient = clientNumber;
  208.                     for (int loop = 0; loop < MAX_CLIENTS; loop++)
  209.                     {
  210.                         // Send a message to the client saying "OK" to indicate the incoming connection has been accepted
  211.                         //strcpy( buffer, SERVER_NOT_FULL.c_str() );
  212.                         int msgLength = strlen(buffer) + 1;
  214.                         // If the message length is more than 1 (i.e. client pressed enter without entering any other text), then
  215.                         // send the message to all connected clients except the client who originated the message in the first place
  216.                         if (msgLength > 1 && loop != originatingClient && socketIsFree[loop] == false)
  217.                         {
  218.                             cout << "Retransmitting message: " << buffer << " (" << msgLength << " bytes) to client number: " << loop << endl;
  219.                             SDLNet_TCP_Send(clientSocket[loop], (void *)buffer, msgLength);
  220.                         }
  222.                     }
  224.                     // If the client told us to shut down the server, then set the flag to get us out of the main loop and shut down
  225.                     if ( strcmp(buffer, "shutdown") == 0 )
  226.                     {
  227.                         shutdownServer = true;
  229.                         cout << "Disconnecting all clients and shutting down the server..." << endl << endl;
  230.                     }
  232.                 }
  234.             } // End of if client socket is active check
  236.         } // End of server socket check sockets loop
  238.     }
  239.     while (shutdownServer == false); // End of main loop
  241.     // Free our socket set (i.e. all the clients in our socket set)
  242.     SDLNet_FreeSocketSet(socketSet);
  244.     // Close our server socket, cleanup SDL_net and finish!
  245.     SDLNet_TCP_Close(serverSocket);
  247.     SDLNet_Quit();
  249.     return 0;
  250. }


  1. // SDL_net Client | r3dux.org | 14/01/2011
  3. // Includes for non-blocking keyboard input
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <stdio.h>
  7. #include <termios.h> // If we do not include termios.h the client WILL compile but it WILL NOT WORK!
  8. #include <unistd.h>
  9. #include <fcntl.h>
  11. #include <iostream>
  12. #include <string>
  14. #include <SDL_net.h>
  16. using namespace std;
  18. const unsigned short PORT        = 1234; // The port we are connecting to
  19. const unsigned short BUFFER_SIZE = 512;  // Size of our message buffer (i.e. maximum length of characters in a message)
  21. struct termios orig_termios;
  23. // Function to reset the terminal to blocking mode
  24. void reset_terminal_mode()
  25. {
  26.     tcsetattr(0, TCSANOW, &orig_termios);
  27. }
  29. // Sets the terminal mode to conio mode
  30. void set_conio_terminal_mode()
  31. {
  32.     struct termios new_termios;
  34.     // Take two copies - one for now, one for later
  35.     tcgetattr(0, &orig_termios);
  36.     memcpy(&new_termios, &orig_termios, sizeof(new_termios));
  38.     // register cleanup handler, and set the new terminal mode
  39.     //atexit(reset_terminal_mode); // Commented out because I switch and swap terminal modes a lot - just remember to call reset_terminal_mode() when we finish up
  40.     cfmakeraw(&new_termios);
  41.     tcsetattr(0, TCSANOW, &new_termios);
  42. }
  44. // Fuction to check if a key has been pressed
  45. int kbHit()
  46. {
  47.     // How long to wait for input
  48.     // Note: As soon as we get input the wait is immediately over - so it's not like our typing rate is limited in any way!
  49.     long waitSeconds      = 1L;
  50.     long waitMicroSeconds = 0L;
  51.     struct timeval tv = { waitSeconds, waitMicroSeconds };
  53.     // Create a file descriptor set
  54.     fd_set fds;
  56.     FD_SET(0, &fds);
  57.     return select(1, &fds, NULL, NULL, &tv);
  58. }
  60. // Function to read the contents of the keypress
  61. int getch()
  62. {
  63.     int r;
  64.     unsigned char c;
  65.     if ((r = read(0, &c, sizeof(c))) < 0)
  66.     {
  67.         //cout << "About to return a number..." << endl;
  68.         return r;
  69.     }
  70.     else
  71.     {
  72.         //cout << "About to return a character..." << endl;
  73.         return c;
  74.     }
  75. }
  77. int main(int argc, char **argv)
  78. {
  79.     const char *host;         // Where we store the host name
  81.     IPaddress serverIP;       // The IP we will connect to
  82.     TCPsocket clientSocket;   // The socket to use
  83.     string    serverName;     // The server name
  85.     string userInput = "";    // A string to hold our user input
  86.     int inputLength  = 0;     // The length of our string in characters
  87.     char buffer[BUFFER_SIZE]; // Array of character's we'll use to transmit our message. We get input into the userInput string for ease of use, then just copy it to this character array and send it.
  89.     // Initialise SDL_net
  90.     if (SDLNet_Init() < 0)
  91.     {
  92.         cout << "Failed to intialise SDN_net: " << SDLNet_GetError() << "\n";
  93.         exit(-1); // Quit!
  94.     }
  96.     // Ask the user for a server to connect to - can be entered as a hostname (i.e. localhost etc.) or an IP address (i.e. etc.)
  97.     cout << "Server Name: ";
  98.     //getline(cin, serverName); // Uncomment this and remove the below line to change the server we're connecting to...
  99.     serverName = "localhost";
  101.     // Create the socket set with enough space to store our desired number of connections (i.e. sockets)
  102.     SDLNet_SocketSet socketSet = SDLNet_AllocSocketSet(1);
  103.     if (socketSet == NULL)
  104.     {
  105.         cout << "Failed to allocate the socket set: " << SDLNet_GetError() << "\n";
  106.         exit(-1); // Quit!
  107.     }
  108.     else
  109.     {
  110.         cout << "Successfully allocated socket set." << endl;
  111.     }
  113.     // Try to resolve the host. If successful, this places the connection details in the serverIP object
  114.     int hostResolved = SDLNet_ResolveHost(&serverIP, serverName.c_str(), PORT);
  116.     if (hostResolved == -1)
  117.     {
  118.         cout << "Failed to resolve the server hostname: " << SDLNet_GetError() << "\nContinuing...\n";
  119.     }
  120.     else // If we successfully resolved the host then output the details
  121.     {
  122.         // Get our IP address in proper dot-quad format by breaking up the 32-bit unsigned host address and splitting it into an array of four 8-bit unsigned numbers...
  123.         Uint8 * dotQuad = (Uint8*)&serverIP.host;
  125.         //... and then outputting them cast to integers. Then read the last 16 bits of the serverIP object to get the port number
  126.         cout << "Successfully resolved host to IP: " << (unsigned short)dotQuad[0] << "." << (unsigned short)dotQuad[1] << "." << (unsigned short)dotQuad[2] << "." << (unsigned short)dotQuad[3];
  127.         cout << " port " << SDLNet_Read16(&serverIP.port) << endl << endl;
  128.     }
  130.     // Try to resolve the IP of the server, just for kicks
  131.     if ((host = SDLNet_ResolveIP(&serverIP)) == NULL)
  132.     {
  133.         cout << "Failed to resolve the server IP address: " << SDLNet_GetError() << endl;
  134.     }
  135.     else
  136.     {
  137.         cout << "Successfully resolved IP to host: " << host << endl;
  138.     }
  140.     // Flag to keep track of when to disconnect and finish up. We initially set it so that we CANNOT connect, and only change this to false when we got an "OK" response from the server
  141.     bool shutdownClient = true;
  143.     // Try to open a connection to the server and quit out if we can't connect
  144.     clientSocket = SDLNet_TCP_Open(&serverIP);
  145.     if (!clientSocket)
  146.     {
  147.         cout << "Failed to open socket to server: " << SDLNet_GetError() << "\n";
  148.         exit(-1);
  149.     }
  150.     else // If we successfully opened a connection then check for the server response to our connection
  151.     {
  152.         cout << "Connection okay, about to read connection status from the server..." << endl;
  154.         // Add our socket to the socket set for polling
  155.         SDLNet_TCP_AddSocket(socketSet, clientSocket);
  157.         // Wait for up to five seconds for a response from the server
  158.         // Note: If we don't check the socket set and WAIT for the response, we'll be checking before the server can respond, and it'll look as if the server sent us nothing back
  159.         int activeSockets = SDLNet_CheckSockets(socketSet, 5000);
  161.         cout << "There are " << activeSockets << " socket(s) with data on them at the moment." << endl;
  163.         // Check if we got a response from the server
  164.         int gotServerResponse = SDLNet_SocketReady(clientSocket);
  166.         if (gotServerResponse != 0)
  167.         {
  168.             cout << "Got a response from the server... " << endl;
  169.             int serverResponseByteCount = SDLNet_TCP_Recv(clientSocket, buffer, BUFFER_SIZE);
  171.             cout << "Got the following from server: " << buffer << "(" << serverResponseByteCount << " bytes)" << endl;
  173.             // We got an okay from the server, so we can join!
  174.             if ( strcmp(buffer, "OK") == 0 )
  175.             {
  176.                 // So set the flag to say we're not quitting out just yet
  177.                 shutdownClient = false;
  179.                 cout << "Joining server now..." << endl << endl;
  180.             }
  181.             else
  182.             {
  183.                 cout << "Server is full... Terminating connection." << endl;
  184.             }
  185.         }
  186.         else
  187.         {
  188.             cout << "No response from server..." << endl;
  189.         }
  191.     } // End of if we managed to open a connection to the server condition
  193.     bool wrotePrompt = false; // Whether or not we've already written the prompt
  194.     bool sendMessage = false; // Whether or not it's time to send the message (flips to true when the user presses return)
  196.     // While it's not time to shutdown the client...
  197.     while (shutdownClient == false)
  198.     {
  199.         // Write the prompt only once per line of input. This gets reset so that it's displayed again after a message is sent
  200.         if (wrotePrompt == false)
  201.         {
  202.             cout << "Write something:" << endl;
  203.             wrotePrompt = true;
  204.         }
  206.         // If we've detected that the user has pressed a key..
  207.         set_conio_terminal_mode();
  208.         int status = kbHit();
  209.         reset_terminal_mode();
  211.         //cout << "status is: " << status << endl;
  213.         if (status != 0)
  214.         {
  215.             //cout << "key was pressed and status is" << status << endl;
  217.             // Get the keypress
  218.             set_conio_terminal_mode();
  219.             char theChar = getch();
  220.             reset_terminal_mode();
  222.             // Output the character to stdout
  223.             cout << theChar;
  225.             // Flush the character to the screen
  226.             fflush(stdout);
  228.             // If the keypressed wasn't return then add the character to our message string
  229.             if ((int)theChar != 13)
  230.             {
  231.                 //cout << "Got the character: " << theChar << " (which is number: " << int(theChar) << ")" << endl;
  233.                 // Add the character to our input string
  234.                 userInput += theChar;
  235.             }
  236.             else // Otherwise (if the user pressed enter) then send the message
  237.             {
  238.                 //cout << "user pressed return" << endl;
  240.                 // Copy our user's string into our char array called "buffer"
  241.                 strcpy( buffer, userInput.c_str() );
  243.                 // Calculate the length of our input and then add 1 (for the terminating character) to get the total number of characters we need to send
  244.                 inputLength = strlen(buffer) + 1;
  246.                 // Send the message to the server
  247.                 if (SDLNet_TCP_Send(clientSocket, (void *)buffer, inputLength) < inputLength)
  248.                 {
  249.                     cout << "Failed to send message: " << SDLNet_GetError() << endl;
  250.                     exit(-1);
  251.                 }
  252.                 else
  253.                 {
  254.                     //cout << "Message sent successfully." << endl;
  256.                     // If we've asked the server to shutdown or we want out then set the flag appropriately
  257.                     if (sendMessage == true && (userInput == "quit" || userInput == "exit" || userInput == "shutdown"))
  258.                     {
  259.                         shutdownClient = true;
  260.                     }
  262.                     // Reset for the next message
  263.                     cout << endl;
  264.                     wrotePrompt = false;
  265.                     sendMessage = false;
  266.                     userInput = "";
  267.                 }
  269.             } // End of message sending section
  271.         } // End of if the user pressed a key test
  273.         // Check our socket set for activity. Don't wait if there's nothing on the socket just continue
  274.         int socketActive = SDLNet_CheckSockets(socketSet, 0);
  276.         //cout << "Sockets with data on them at the moment: " << activeSockets << endl;
  278.         if (socketActive != 0)
  279.         {
  280.             // Check if we got a response from the server
  281.             int messageFromServer = SDLNet_SocketReady(clientSocket);
  283.             if (messageFromServer != 0)
  284.             {
  285.                 //cout << "Got a response from the server... " << endl;
  286.                 int serverResponseByteCount = SDLNet_TCP_Recv(clientSocket, buffer, BUFFER_SIZE);
  288.                 cout << "Received: " << buffer << endl;// "(" << serverResponseByteCount << " bytes)" << endl;
  290.                 if (strcmp(buffer, "shutdown") == 0)
  291.                 {
  292.                     cout << "Server is going down. Disconnecting..." << endl;
  293.                     shutdownClient = true;
  294.                 }
  295.             }
  296.             else
  297.             {
  298.                 //cout << "No response from server..." << endl;
  299.             }
  301.         } // End of if socket has activity check
  303.     } // End of main while loop
  305.     // Close our socket, cleanup SDL_net, reset the terminal mode and finish!
  306.     SDLNet_TCP_Close(clientSocket);
  308.     SDLNet_Quit();
  310.     reset_terminal_mode();
  312.     return 0;
  313. }

The Kicker: Soon after doing all this research and spending a few days (of my own time, unpaid) writing client/server code I found out that I’m not teaching any programming this year, and have instead been assigned Systems Analysis & Design and MS/Cisco Networking. Pah! I’m not going to call this time wasted though – I learnt a lot, and I hope that you might find this code of use too.. =D

Update – Nov 2011: I’ve refactored this code into something significantly easier to work with, modify and extend – so you should probably try this instead: https://r3dux.org/2011/11/a-simple-csdl_net-chat-server-client-rewritten/.

50 thoughts on “A Simple C++/SDL_Net Chat Server & Client”

  1. The fact that this was written to teach network programming makes this code a great resource for anyone learning SLD_net. Thanks!

  2. This is absolutely terrific and helpful. I’ve been looking for exactly that kind of messenger server example and this is by far the simplest.
    Thanks alot!

  3. Just one thing which bothers me and would be great if you can help with: you state that SDL_net doesn’t require SDL, but it does so at both Compile (“Where’s SDL.h?”) & Link (“Where’s SDL.dll?) time.
    They also mention it at . So what did I not get here? Am I using a different version or what? My version of SDL_Net is 1.2.7.


    1. Hi Assaf,

      I’m not sure your comment came through the filters correctly (“they also mention it at .”??) – but at no point am I ever including SDL.h or linking to SDL.dll. SDL_Net is completely standalone from SDL as far as I’m aware. Where are you getting these “Where’s SDL.blah” statements from?

      I have no idea what version of SDL_Net I used – whichever was current at the time I wrote this post or a week or so earlier.

  4. Hi Al,

    Thanks for your reply.

    Sorry about the mess. Okay, “they also mention it at ” had the download page’s link after it, same as the one you gave in the libraries table above.
    In the 1.2.7 version (that’s where it’s currently pointing), SDL_net.h includes SDL.h.

    So, SDL_net is indeed coupled with SDL (in this version at least) , but only to utilize the SDL_Get/SetError() functions. The author also mentions that he wishes to disentangle the libraries in the future.

    So all one needs to do is to find a different solution for getting/setting errors and he can be SDL-free.


    1. Ah, I get ya. I’m not using SDL_Get/SetError functions, so that’s probably why I’m not seeing the dependency crop up.

      Cheers for the clarification :D

      Yup – you’re right. SDL_net requires the SDL library. I must’ve got my head in a twist about it.

    1. IPaddress and TCPsocket are built in types provided by the SDL_net package.

      To compile the code you’ll need to link in the SDL and SDL_net libs, as well as include SDL_net.h – which is where these new types are defined.

      For example, inside SDL_net.h, you’ll find the following code (amongst other things):

      typedef struct {
      	Uint32 host;			/* 32-bit IPv4 host address */
      	Uint16 port;			/* 16-bit protocol port */
      } IPaddress;
  5. if u dont mind i have another question to ask.. how can this program be multiple client chat, do we need to define the port address on client’s computer. what about the server… need to do some change or not.. hehe.. sorry. so many question to ask..i really want to know how this program work..

    1. The program comes in two parts:
      – A chat server which can link multiple clients so they can chat, and
      – A client application which can connect to the server.

      The chat flow goes like this:
      1 – Run the server, which sits there waiting for incoming connections,
      2 – Run a client, which connects to the server. The client can send messages to the server, and the server then sends those messages to all connected clients.

      So, if you first run the server and leave it running, and then run one instance of the client, then the client connects to the server and can talk to it – but there’s no-one to talk to as there are no other clients!

      If you then run ANOTHER copy of the client (while the first client is still running), then there are now two clients connected to the server, and any message entered into any client will be broadcast (by the server) to all other clients.

      You can configure the number of clients that can connect to the server before the server will refuse any more connections, I think I set that to be a low number like 4 clients connected at once, but you can change it to be any number you like.

      When the server starts it opens up port 1234 to listen on, and it can listen to multiple connections on that port. When a client starts, it connects to port 1234 on (if I remember correctly) any available, valid socket i.e. the client picks a random port to listen/transmit on – which is connected to the server port 1234, and the server keeps track of all the ports of the connected clients.

      Will post up build instructions soon – hope this helps for now.


    1. Not quite…

      The server just sits in the background and listens for incoming connections. When it gets an incoming connection from a client, it adds the client to the list of active clients. The server then takes any messages that it receives from any client, and sends the message on to all the other connected clients in the list (if there are any).

      A client just connects to the server, and allows you to type some text which gets sent to the server. When the server receives that text, as mentioned above, it sends on the message to any other connected clients.

      That’s it! =D

      Also, I might as well give you the build instructions now I have a moment. To compile both the client and the server projects you need to link in the following libraries:

      – libSDL (on my system this means the file libSDL.a, which is in the package libsdl1.2-dev)

      – libSDL_net (on my system this means the file libSDL_net.a, which is in the package libsdl1.2-net-dev)

      – pthread (on my system this means the file libpthread.so.0 which should exist as a core part of your Ubuntu system – I’d imagine it’s in /usr/lib/ but I don’t run Ubuntu anymore so can’t guarantee it).

      With that done, the project should compile without issue.

      Let me know how you get on.

  6. i already install the SDL and SDL_net lib in my UBUNTU but i dont know how to compile it.. i have try compile using compilation script on the internet but got error..

    1. You’re making life difficult for yourself by compiling from the command line – it’s far easier to set libraries and paths in an IDE like Code::Blocks or such…

      Anyways, I looked into it and (assuming you have the server and clients called SDL-Net-Server.cpp and SDL-Net-Client.cpp), you can build them with the following commands:

      g++ -Wall `sdl-config --cflags --libs` -lpthread -lSDL -lSDL_net -o SDL-Net-Server ./SDL-Net-Server.cpp
      g++ -Wall `sdl-config --cflags --libs` -lpthread -lSDL -lSDL_net -o SDL-Net-Client ./SDL-Net-Client.cpp

      Where the generated executables will be called SDL-Net-Server and SDL-Net-Client respectively. Using the -l switch uses the shared object (.so) versions of the libraries, so the final executables won’t be standalone i.e. the system running them will need to have those same libraries available on the system to execute successfully, but this is good enough for our purposes.

      I’ve even put the files together into a zip for you with “build-server.sh” and “build-client.sh” scripts to do the commands for you – all you should have to do is make the build*.sh scripts executable with chmod +x build-server.sh and chmod +x build-client.sh – or, of course, you could just copy/paste/run the above commands one at a time (making sure that the .cpp files are in the same directory where you’re running the commands!).

      You can find the archive here

      I reckon you’ll be able to do it this time!

      Final note – when compiling the client it’ll moan about an unused variable: don’t worry about it, it’s just a warning (as opposed to an error), and I think the variable it’s complaining about IS used in the program when some of the debug comments are uncommented!).


  7. thank u very much… i able to execute the file and compile.. now i have 3 file in the folder client and folder sever.. which 2 file i got from u and another 1 file exist after i follow your instruction.. is this correct..?? :) im not sure… so how to run this file..

  8. thanks again.. i don’t know how to say thank to u.. :).. can i ask something.. if i want to increase the no of client.. then how.. do i need to change the code only or need to change something in build*.sh also

    1. To increase the number of clients that can connect at once, you have to change the number of sockets available in the server at line 13:

      const unsigned short MAX_SOCKETS = 4;               // Max number of sockets

      And then you obviously have to recompile the server.

      The server itself takes up 1 socket, so if MAX_SOCKETS is 4, you can connect 3 clients – if MAX_SOCKETS is 10, you can connect 9 clients, etc.

      You can see this at the line 14 where it says:

      const unsigned short MAX_CLIENTS = MAX_SOCKETS - 1; // Max number of clients in our socket set...

      To shutdown the server, you have to enter “shutdown” into one of the clients connected to the server – on receiving the “shutdown” message, the server will… shut down.

      The shutdown flag is set on line 224 to 230, finally exiting the main loop at line 239.

      It’s all there in the source code – you just need to look.

  9. then on client computer, do we need to compile the file again or we need to copy the application that already execute in my computer

    1. Any changes you make to ANY code have to be compiled/recompiled to produce the executable file (incorporating the changes) that you ‘run’.

      So if you change the server code, you have to recompile and run the new server. And if you change the client code, then you need to recompile and run the new client.

      If I understand you correctly, you just want to allow the server to accept more clients. To do this, you simply have to change the number of clients the server will accept on line 13 of the server code (as I’ve already shown you), and then recompile the server code to produce a new server executable which you can run, and then allow clients to connect to it.

      So you don’t compile the client again, but you will have to compile the server again.

      That is, UNLESS you want to run the server on one computer, and the clients on other (different) computers, in which case you’ll need to change the client source code so that lines 96 to 99 change FROM:

      // Ask the user for a server to connect to - can be entered as a hostname (i.e. localhost etc.) or an IP address (i.e. etc.)
      cout << "Server Name: ";
      //getline(cin, serverName); // Uncomment this and remove the below line to change the server we're connecting to...
      serverName = "localhost";


      // Ask the user for a server to connect to - can be entered as a hostname (i.e. localhost etc.) or an IP address (i.e. etc.)
      cout << "Server Name: ";
      getline(cin, serverName);

      Recompile the client with those changes made.

      At which point, when you run a client, you’ll be asked for the server address (i.e. IP address) of the computer which is running the server software, instead of the client assuming localhost/ as the address of the server.

  10. correct.. that was what i want to do with your program.. brilliant. ..so now let me try first.. later i will ask u again if i have some problem. i need to find my friend to compile this program in other computer..

  11. i run the client prog on other computer but got this error
    Failed to open socket to server: Couldn’t connect to remote host
    i compile this file in my comp.. so is that problem

    1. Is that after changing the client so that you have to specify the server address, and then specifying the correct IP address of server?

      There are many issues that could be going on here:
      – If the other computer and the server are not on the same network it won’t work by providing internal IPs, you’ll have to provide external IPs of the gateways to the server, and then port-forward the server port (1234) to the correct internal IP from the router…
      – Firewalls and proxies could be in the way,
      – You might not even have recompiled the client to allow for specifying an IP address for all I know..
      – Many, many other factors.

      I’m sorry, but you don’t have the technical skills to do this. I’m not even sure WHY you want to use this client/server code across networks – it’s not a fully functional chat client, it’s just an example of how to do the most basic possible client/server socket communication which I put together to teach teenagers some basic socket code.

      If you really want to run a peer-to-peer chat client (i.e. a chat client that doesn’t use a centralised server, as chat services like MS Live, YahooChat, ICQ, IRC etc DO use centralised servers), then might I suggest searching for “p2p chat client” – it’ll come up with things like:
      JabberD14 jabber (protocol) server with any jabber client of your choice.

      If you’re going to persist with using the client/server code provided, then you’ll have to find someone who can see your network and IP setup and advise from the ground – I cannot do that from here.

      Also, as mentioned, this is just example code I put together to teach from, and NOT an official, supported chat system.

      I’ve done as much as I can, and I think I’ve been very patient and answered all of your questions to the best of my ability – but now you’re on your own.

      Please do not contact me with any further questions regarding the source code – I am not a limitless fountain of free technical support. If you want to learn programming and networking, then go and learn programming and networking instead of relying on other people to solve each and every issue that you have.

      Best of luck.

  12. :) . orite.. thanks for ur reply.. i have learn a lot from u.. i will try my best to fix this thing.. thank u very much.. may god bless u..

  13. can i have your name to put in my reference together with your website address. if u don’t want to reveal your name here may be u can email me..anis152003@yahoo.com. thanks a lot ..sir/madam. actually i’m doing this for my Laboratory.. I’m university Student. I take electric telecommunication and this is my final year. Since i have only 3 week to complete this and i know nothing about socket programming and UBUNTU, so i just take your example. Hope u don’t mind

  14. I’m quite interesting to the server- multiple client program..may I know if all client simultaneously click the Enter after type the message then the server cant detect which client sent, correct??

    May I know isn’t need to allocate different port to different client?? I’m new to ubuntu and chat programming..may I know which part of the above program need to modify in order using different port for each client??

    Thanks a lot for the help…
    Just wonder if using different port the program can work @@

    1. 1 – The server CAN tell which client sent which message – it needs to know so that it can broadcast the message to all the other clients. You can even see in the server output that it says “Received [some message] from client [some client number]”.

      2 – Each client already has its own (unique) randomly assigned port. To see which ports the client(s) are using, try using the command:

      netstat --tcp | grep 1234

      This will find all tcp sockets which are currently open on the machine, and the “1234” bit is the default port number of the server.

      If you want to assign specific ports to clients, then you’ll have to look up how to do that in the SDL_net documentation.

      Best of luck.

  15. Sir, i have try this program using LAN and the program work great.. We can communicate with each other. but i have something to adjust. for this program, at client side we wouldn’t know the message is sent by which client. If we have multiple client, we are going to have a little bit problem to detect which client sent the message.Only the server know who is sending the message. But never mind. I will configure it out and will let you know the progress. Thanks a lot Sir/Teacher

    1. Easiest way to do this is to modify the client to ask for a username (i.e. who is using the client) and then just prepend that username to each message sent from the client.

  16. hi Sir, after looking at some example of C++ and socket programming, i already successful modify the client code to ask for user name and broadcast it together with the message.. The received message will be like this

    Received: My message :User Name /Client Name

    That is the best that i can do.. :)

    Thanks for the clue.. i really appreciate it..

  17. this code was so well designed that i added sdl to it and made it a regular, non terminal code with minimal effort. I hope you do get teaching gigs cuz you are a good example.

  18. Thank you so much for this tutorial! I am in the process of creating a multiplayer game. This tutorial was exactly what I needed to get used to SDL_net and get chat working. Once again, thanks!


  19. This is a synchronous socket system right?
    Is there any way to create an asynchronous socket with SDL_net?
    Or at least a non-blocking send and receive function?

    Nice tut by the way.

  20. Thanks a lot! Very useful tutorial! You are gr8 m8 :D
    You just saved me a lot of pain i had since few days! Now everything is clear – i just lacked this “int serverSocketActivity = SDLNet_SocketReady(serverSocket);”
    this one line of code gave me what I needed ^_^ – I thought i read the documentation thoroughly but somehow missed this function :)

    1. Yeah, I reckon so… the SDL_net send and receive methods accept void pointers instead of specifically requiring char arrays, so I don’t see why not.

      If you do the refactoring and it all works, it’d be great if you posted the code and I’ll post it here and credit you. You can, of course, do anything with the code, so you don’t have to pass on any refactoring work you do if you don’t want to. Also, you might like to work from the re-factored version of the code here rather than this version.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.