A Simple C++/SDL_Net Chat Server & Client
r3dux | January 14, 2011Update – Nov 2011: I’ve refactored this code into something significantly easier to work with, modify and extend – so you should probably try this instead: http://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 |
|
|
| C++ Sockets Library |
|
|
| SDL_net |
|
|
| NetLink Socket Library |
|
|
| SimpleSockets (now defunct) |
|
|
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:

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.
Cheers!
SDL_net Server:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 | // SDL_net Server | r3dux.org | 14/01/2011 #include <iostream> #include <cstdlib> #include <string> #include <SDL_net.h> using namespace std; const unsigned short PORT = 1234; // The port our server will listen for incoming connecions on const unsigned short BUFFER_SIZE = 512; // Size of our message buffer const unsigned short MAX_SOCKETS = 4; // Max number of sockets 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) // Messages to send back to any connecting client to let them know if we can accept the connection or not const string SERVER_NOT_FULL = "OK"; const string SERVER_FULL = "FULL"; int main(int argc, char **argv) { IPaddress serverIP; // The IP of the server (this will end up being 0.0.0.0 - which means roughly "any IP address") TCPsocket serverSocket; // The server socket that clients will use to connect to us 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) 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) char buffer[BUFFER_SIZE]; // Array of characters used to store the messages we receive 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 int clientCount = 0; // Count of how many clients are currently connected to the server bool shutdownServer = false; // Flag to control when to shut down the server // Initialise SDL_net (Note: We don't initialise or use normal SDL at all - only the SDL_net library!) if (SDLNet_Init() == -1) { cout << "Failed to intialise SDL_net: " << SDLNet_GetError() << endl; exit(-1); // Quit! } // Create the socket set with enough space to store our desired number of connections (i.e. sockets) SDLNet_SocketSet socketSet = SDLNet_AllocSocketSet(MAX_SOCKETS); if (socketSet == NULL) { cout << "Failed to allocate the socket set: " << SDLNet_GetError() << "\n"; exit(-1); // Quit! } else { cout << "Allocated socket set with size: " << MAX_SOCKETS << ", of which " << MAX_CLIENTS << " are availble for use by clients." << endl; } // Initialize all the client sockets (i.e. blank them ready for use!) for (int loop = 0; loop < MAX_CLIENTS; loop++) { clientSocket[loop] = NULL; socketIsFree[loop] = true; // Set all our sockets to be free (i.e. available for use for new client connections) } // 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 // 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 int hostResolved = SDLNet_ResolveHost(&serverIP, NULL, PORT); if (hostResolved == -1) { cout << "Failed to resolve the server host: " << SDLNet_GetError() << endl; } else // If we resolved the host successfully, output the details { // 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... Uint8 * dotQuad = (Uint8*)&serverIP.host; //... and then outputting them cast to integers. Then read the last 16 bits of the serverIP object to get the port number cout << "Successfully resolved server host to IP: " << (unsigned short)dotQuad[0] << "." << (unsigned short)dotQuad[1] << "." << (unsigned short)dotQuad[2] << "." << (unsigned short)dotQuad[3]; cout << " port " << SDLNet_Read16(&serverIP.port) << endl << endl; } // Try to open the server socket serverSocket = SDLNet_TCP_Open(&serverIP); if (!serverSocket) { cout << "Failed to open the server socket: " << SDLNet_GetError() << "\n"; exit(-1); } else { cout << "Sucessfully created server socket." << endl; } // Add our server socket to the socket set SDLNet_TCP_AddSocket(socketSet, serverSocket); cout << "Awaiting clients..." << endl; // Main loop... do { // Check for activity on the entire socket set. The second parameter is the number of milliseconds to wait for. // 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 int numActiveSockets = SDLNet_CheckSockets(socketSet, 0); if (numActiveSockets != 0) { cout << "There are currently " << numActiveSockets << " socket(s) with data to be processed." << endl; } // Check if our server socket has received any data // 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) // 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. int serverSocketActivity = SDLNet_SocketReady(serverSocket); // If there is activity on our server socket (i.e. a client has trasmitted data to us) then... if (serverSocketActivity != 0) { // If we have room for more clients... if (clientCount < MAX_CLIENTS) { // Find the first free socket in our array of client sockets int freeSpot = -99; for (int loop = 0; loop < MAX_CLIENTS; loop++) { if (socketIsFree[loop] == true) { //cout << "Found a free spot at element: " << loop << endl; socketIsFree[loop] = false; // Set the socket to be taken freeSpot = loop; // Keep the location to add our connection at that index in the array of client sockets break; // Break out of the loop straight away } } // ...accept the client connection and then... clientSocket[freeSpot] = SDLNet_TCP_Accept(serverSocket); // ...add the new client socket to the socket set (i.e. the list of sockets we check for activity) SDLNet_TCP_AddSocket(socketSet, clientSocket[freeSpot]); // Increase our client count clientCount++; // Send a message to the client saying "OK" to indicate the incoming connection has been accepted strcpy( buffer, SERVER_NOT_FULL.c_str() ); int msgLength = strlen(buffer) + 1; SDLNet_TCP_Send(clientSocket[freeSpot], (void *)buffer, msgLength); cout << "Client connected. There are now " << clientCount << " client(s) connected." << endl << endl; } else // If we don't have room for new clients... { cout << "*** Maximum client count reached - rejecting client connection ***" << endl; // Accept the client connection to clear it from the incoming connections list TCPsocket tempSock = SDLNet_TCP_Accept(serverSocket); // Send a message to the client saying "FULL" to tell the client to go away strcpy( buffer, SERVER_FULL.c_str() ); int msgLength = strlen(buffer) + 1; SDLNet_TCP_Send(tempSock, (void *)buffer, msgLength); // Shutdown, disconnect, and close the socket to the client SDLNet_TCP_Close(tempSock); } } // End of if server socket is has activity check // Loop to check all possible client sockets for activity for (int clientNumber = 0; clientNumber < MAX_CLIENTS; clientNumber++) { // 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) int clientSocketActivity = SDLNet_SocketReady(clientSocket[clientNumber]); //cout << "Just checked client number " << clientNumber << " and received activity status is: " << clientSocketActivity << endl; // If there is any activity on the client socket... if (clientSocketActivity != 0) { // Check if the client socket has transmitted any data by reading from the socket and placing it in the buffer character array receivedByteCount = SDLNet_TCP_Recv(clientSocket[clientNumber], buffer, BUFFER_SIZE); // If there's activity, but we didn't read anything from the client socket, then the client has disconnected... if (receivedByteCount <= 0) { //...so output a suitable message and then... cout << "Client " << clientNumber << " disconnected." << endl << endl; //... remove the socket from the socket set, then close and reset the socket ready for re-use and finally... SDLNet_TCP_DelSocket(socketSet, clientSocket[clientNumber]); SDLNet_TCP_Close(clientSocket[clientNumber]); clientSocket[clientNumber] = NULL; // ...free up their slot so it can be reused... socketIsFree[clientNumber] = true; // ...and decrement the count of connected clients. clientCount--; cout << "Server is now connected to: " << clientCount << " client(s)." << endl << endl; } else // If we read some data from the client socket... { // Output the message the server received to the screen cout << "Received: >>>> " << buffer << " from client number: " << clientNumber << endl; // Send message to all other connected clients int originatingClient = clientNumber; for (int loop = 0; loop < MAX_CLIENTS; loop++) { // Send a message to the client saying "OK" to indicate the incoming connection has been accepted //strcpy( buffer, SERVER_NOT_FULL.c_str() ); int msgLength = strlen(buffer) + 1; // If the message length is more than 1 (i.e. client pressed enter without entering any other text), then // send the message to all connected clients except the client who originated the message in the first place if (msgLength > 1 && loop != originatingClient && socketIsFree[loop] == false) { cout << "Retransmitting message: " << buffer << " (" << msgLength << " bytes) to client number: " << loop << endl; SDLNet_TCP_Send(clientSocket[loop], (void *)buffer, msgLength); } } // 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 if ( strcmp(buffer, "shutdown") == 0 ) { shutdownServer = true; cout << "Disconnecting all clients and shutting down the server..." << endl << endl; } } } // End of if client socket is active check } // End of server socket check sockets loop } while (shutdownServer == false); // End of main loop // Free our socket set (i.e. all the clients in our socket set) SDLNet_FreeSocketSet(socketSet); // Close our server socket, cleanup SDL_net and finish! SDLNet_TCP_Close(serverSocket); SDLNet_Quit(); return 0; } |
Client:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 | // SDL_net Client | r3dux.org | 14/01/2011 // Includes for non-blocking keyboard input #include <stdlib.h> #include <string.h> #include <stdio.h> #include <termios.h> // If we do not include termios.h the client WILL compile but it WILL NOT WORK! #include <unistd.h> #include <fcntl.h> #include <iostream> #include <string> #include <SDL_net.h> using namespace std; const unsigned short PORT = 1234; // The port we are connecting to const unsigned short BUFFER_SIZE = 512; // Size of our message buffer (i.e. maximum length of characters in a message) struct termios orig_termios; // Function to reset the terminal to blocking mode void reset_terminal_mode() { tcsetattr(0, TCSANOW, &orig_termios); } // Sets the terminal mode to conio mode void set_conio_terminal_mode() { struct termios new_termios; // Take two copies - one for now, one for later tcgetattr(0, &orig_termios); memcpy(&new_termios, &orig_termios, sizeof(new_termios)); // register cleanup handler, and set the new terminal mode //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 cfmakeraw(&new_termios); tcsetattr(0, TCSANOW, &new_termios); } // Fuction to check if a key has been pressed int kbHit() { // How long to wait for input // 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! long waitSeconds = 1L; long waitMicroSeconds = 0L; struct timeval tv = { waitSeconds, waitMicroSeconds }; // Create a file descriptor set fd_set fds; FD_SET(0, &fds); return select(1, &fds, NULL, NULL, &tv); } // Function to read the contents of the keypress int getch() { int r; unsigned char c; if ((r = read(0, &c, sizeof(c))) < 0) { //cout << "About to return a number..." << endl; return r; } else { //cout << "About to return a character..." << endl; return c; } } int main(int argc, char **argv) { const char *host; // Where we store the host name IPaddress serverIP; // The IP we will connect to TCPsocket clientSocket; // The socket to use string serverName; // The server name string userInput = ""; // A string to hold our user input int inputLength = 0; // The length of our string in characters 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. // Initialise SDL_net if (SDLNet_Init() < 0) { cout << "Failed to intialise SDN_net: " << SDLNet_GetError() << "\n"; exit(-1); // Quit! } // 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. 127.0.0.1 etc.) cout << "Server Name: "; //getline(cin, serverName); // Uncomment this and remove the below line to change the server we're connecting to... serverName = "localhost"; // Create the socket set with enough space to store our desired number of connections (i.e. sockets) SDLNet_SocketSet socketSet = SDLNet_AllocSocketSet(1); if (socketSet == NULL) { cout << "Failed to allocate the socket set: " << SDLNet_GetError() << "\n"; exit(-1); // Quit! } else { cout << "Successfully allocated socket set." << endl; } // Try to resolve the host. If successful, this places the connection details in the serverIP object int hostResolved = SDLNet_ResolveHost(&serverIP, serverName.c_str(), PORT); if (hostResolved == -1) { cout << "Failed to resolve the server hostname: " << SDLNet_GetError() << "\nContinuing...\n"; } else // If we successfully resolved the host then output the details { // 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... Uint8 * dotQuad = (Uint8*)&serverIP.host; //... and then outputting them cast to integers. Then read the last 16 bits of the serverIP object to get the port number cout << "Successfully resolved host to IP: " << (unsigned short)dotQuad[0] << "." << (unsigned short)dotQuad[1] << "." << (unsigned short)dotQuad[2] << "." << (unsigned short)dotQuad[3]; cout << " port " << SDLNet_Read16(&serverIP.port) << endl << endl; } // Try to resolve the IP of the server, just for kicks if ((host = SDLNet_ResolveIP(&serverIP)) == NULL) { cout << "Failed to resolve the server IP address: " << SDLNet_GetError() << endl; } else { cout << "Successfully resolved IP to host: " << host << endl; } // 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 bool shutdownClient = true; // Try to open a connection to the server and quit out if we can't connect clientSocket = SDLNet_TCP_Open(&serverIP); if (!clientSocket) { cout << "Failed to open socket to server: " << SDLNet_GetError() << "\n"; exit(-1); } else // If we successfully opened a connection then check for the server response to our connection { cout << "Connection okay, about to read connection status from the server..." << endl; // Add our socket to the socket set for polling SDLNet_TCP_AddSocket(socketSet, clientSocket); // Wait for up to five seconds for a response from the server // 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 int activeSockets = SDLNet_CheckSockets(socketSet, 5000); cout << "There are " << activeSockets << " socket(s) with data on them at the moment." << endl; // Check if we got a response from the server int gotServerResponse = SDLNet_SocketReady(clientSocket); if (gotServerResponse != 0) { cout << "Got a response from the server... " << endl; int serverResponseByteCount = SDLNet_TCP_Recv(clientSocket, buffer, BUFFER_SIZE); cout << "Got the following from server: " << buffer << "(" << serverResponseByteCount << " bytes)" << endl; // We got an okay from the server, so we can join! if ( strcmp(buffer, "OK") == 0 ) { // So set the flag to say we're not quitting out just yet shutdownClient = false; cout << "Joining server now..." << endl << endl; } else { cout << "Server is full... Terminating connection." << endl; } } else { cout << "No response from server..." << endl; } } // End of if we managed to open a connection to the server condition bool wrotePrompt = false; // Whether or not we've already written the prompt bool sendMessage = false; // Whether or not it's time to send the message (flips to true when the user presses return) // While it's not time to shutdown the client... while (shutdownClient == false) { // Write the prompt only once per line of input. This gets reset so that it's displayed again after a message is sent if (wrotePrompt == false) { cout << "Write something:" << endl; wrotePrompt = true; } // If we've detected that the user has pressed a key.. set_conio_terminal_mode(); int status = kbHit(); reset_terminal_mode(); //cout << "status is: " << status << endl; if (status != 0) { //cout << "key was pressed and status is" << status << endl; // Get the keypress set_conio_terminal_mode(); char theChar = getch(); reset_terminal_mode(); // Output the character to stdout cout << theChar; // Flush the character to the screen fflush(stdout); // If the keypressed wasn't return then add the character to our message string if ((int)theChar != 13) { //cout << "Got the character: " << theChar << " (which is number: " << int(theChar) << ")" << endl; // Add the character to our input string userInput += theChar; } else // Otherwise (if the user pressed enter) then send the message { //cout << "user pressed return" << endl; // Copy our user's string into our char array called "buffer" strcpy( buffer, userInput.c_str() ); // 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 inputLength = strlen(buffer) + 1; // Send the message to the server if (SDLNet_TCP_Send(clientSocket, (void *)buffer, inputLength) < inputLength) { cout << "Failed to send message: " << SDLNet_GetError() << endl; exit(-1); } else { //cout << "Message sent successfully." << endl; // If we've asked the server to shutdown or we want out then set the flag appropriately if (sendMessage == true && (userInput == "quit" || userInput == "exit" || userInput == "shutdown")) { shutdownClient = true; } // Reset for the next message cout << endl; wrotePrompt = false; sendMessage = false; userInput = ""; } } // End of message sending section } // End of if the user pressed a key test // Check our socket set for activity. Don't wait if there's nothing on the socket just continue int socketActive = SDLNet_CheckSockets(socketSet, 0); //cout << "Sockets with data on them at the moment: " << activeSockets << endl; if (socketActive != 0) { // Check if we got a response from the server int messageFromServer = SDLNet_SocketReady(clientSocket); if (messageFromServer != 0) { //cout << "Got a response from the server... " << endl; int serverResponseByteCount = SDLNet_TCP_Recv(clientSocket, buffer, BUFFER_SIZE); cout << "Received: " << buffer << endl;// "(" << serverResponseByteCount << " bytes)" << endl; if (strcmp(buffer, "shutdown") == 0) { cout << "Server is going down. Disconnecting..." << endl; shutdownClient = true; } } else { //cout << "No response from server..." << endl; } } // End of if socket has activity check } // End of main while loop // Close our socket, cleanup SDL_net, reset the terminal mode and finish! SDLNet_TCP_Close(clientSocket); SDLNet_Quit(); reset_terminal_mode(); return 0; } |
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: http://r3dux.org/2011/11/a-simple-csdl_net-chat-server-client-rewritten/.











The fact that this was written to teach network programming makes this code a great resource for anyone learning SLD_net. Thanks!
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!
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.
Thanks.
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.
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.
Assaf.
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
Yup – you’re right. SDL_net requires the SDL library. I must’ve got my head in a twist about it.
Im having trouble understaindg where the types
IPaddress and TCPsocket came from
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):
im trying to compile this file in UBUNTU.. since im beginner. can u teach me how to compile this file.. thanks for helping..
Hi Su,
I’m really short on time at the moment but will try to provide some instructions for you this evening or tomorrow.
Regards,
r3dux
thank u..never mind i will wait.. take your time..
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..
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.
-r3dux
now i understand.so the server can be the client at the same time right.. correct me if im wrong..
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.
i will try as recommended and i will inform u later.. TQ very much
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..
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:
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!).
Cheers!
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..
i can run it .. im so happy.. thanks for ur help.. i really appreciate it..
No worries, man – enjoy! =D
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
and how this application terminate…
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:
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:
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.
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
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:
TO:
THEN:
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/127.0.0.1 as the address of the server.
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..
can i run client in the same computer with server??
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
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:
- PiChat,
- Universal,
- 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.
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
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 – 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:
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.
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
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.
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..
Cheers!
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.
Thanks – and glad you’ve found it useful.
I’d love to take a look sometime if you do anything cool with it! =D
[...] deal with network programming with sockets and stuff, so I duly did my research and put together a simple chat server and client in SDL_net. And then my classes changed and I got moved on to teach other stuff. I wasn’t too [...]
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!
-Ex
You’re very welcome – glad you found it useful! =D
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.
It’s all asynchronous and uses non-blocking input already.
Just a stupid question… How do you link SDL_Net?
In what operating system and IDE?