Quote:Original post by NukeCorr
Thanks for the reply rip-off
Is there any example of connecting multiple clients to a server?
I checked the socket set stuff in the link but I can't figure out how you do process them in the main loop.
The message is sent to the server again..once, using 'if', but I think if I use 'while' with multiple connections, it doesn't work...
Yes, the while loop will soak up all your time until the connection dies. It was only a quick solution to show how a real program would work.
Quote:
If the server and client is in the same application/game, the server SDL rendering loop gets stuck because of the while, so is there some alternate way than using while?
Yes, the SocketSet will help you with this.
Quote:
And how does it work to have server and client implemented to same application. So one could host a server, and others connect to it...with the same version of the app?
This is certainly possible. You just have to have all the code paths available in the same application.
Quote:
EDIT: Is there some alternate for std::vector<> since I haven't never used/worked with it?
Yes, but you won't have used these either. Better to learn std::vector<>, it is a vital tool and part of the language, in the standard library.
Here is an example of a non-blocking multi-client server code:
#include <cstdio>#include <cstdlib>#include <cstring>#include <vector> #include "SDL_net.h"TCPsocket initialise(){ if (SDLNet_Init() < 0) { fprintf(stderr, "SDLNet_Init: %s\n", SDLNet_GetError()); exit(1); } IPaddress ip; const int port = 2000; if (SDLNet_ResolveHost(&ip, NULL, port) < 0) { fprintf(stderr, "SDLNet_ResolveHost: %s\n", SDLNet_GetError()); SDLNet_Quit(); exit(1); } if (TCPSocket server = SDLNet_TCP_Open(&ip)) { return server; } else { fprintf(stderr, "SDLNet_TCP_Open: %s\n", SDLNet_GetError()); SDLNet_Quit(); exit(1); }}// Typedef to help readability.typedef std::vector<TCPSocket> Clients;// Helper functions.void handleNewClients(Clients &clients, SDLNet_SocketSet &socketSet);void handleNetworkData(Clients &clients, SDLNet_SocketSet socketSet); int main(int argc, char **argv){ TCPsocket server = initialise(); Clients clients; SDLNet_SocketSet socketSet = 0; bool running = true; while (running) { handleNewClients(clients, socketSet); handleNetworkData(clients, socketSet); // Here we can render and check for input, the usual SDL stuff. } // Clean up all the client sockets here. // Don't forget about socketSet! SDLNet_TCP_Close(server); SDLNet_Quit(); return 0;}void handleNewClients(Clients &clients, SDLNet_SocketSet &socketSet){ // Check if new connections have arrived. // Add them in a loop to match the case of multiple connections arriving at the same time. while(TCPSocket client = SDLNet_TCP_Accept(server))) { if (IPaddress *remoteIP = SDLNet_TCP_GetPeerAddress(client)) { printf("Host connected: %x %d\n", SDLNet_Read32(&remoteIP->host), SDLNet_Read16(&remoteIP->port)); } else { fprintf(stderr, "SDLNet_TCP_GetPeerAddress: %s\n", SDLNet_GetError()); } // Add the new socket to the vector. // The vector is a dynamic array, it will grow in size to ensure it can fit the new item. clients.push_back(client); // If we haven't allocated the socket set, allocate it now. if(!socketSet) { // Use the capacity of the vector as a guide as to how many sockets to allocate. socketSet = SDLNet_AllocSocketSet(clients.capacity()); if(!socketSet) { // error handling } } // Try to add a socket to the set. // If this fails, we probably need to make room for additional sockets in the socketSet. // The socketSet, unlike the vector<>, does not grow to meet demand. if(SDLNet_TCP_AddSocket(socketSet, client) < 0) { // If we are here, an error occured. Let's make the socketset bigger, again guided by the vector capacity. SDL_FreeSocketSet(socketSet); // The capacity() of a vector is >= to its size() socketSet = SDLNet_AllocSocketSet(clients.capacity()); if(!socketSet) { // error handling } // Add all the current clients to the socket set... for(int i = 0 ; i < clients.size() ; ++i) { // If we have a valid client. if(clients) { // We have already ensured room for all the sockets, so if this fails // we fall back on normal error handling. No point making it even bigger. if(SDLNet_TCP_AddSocket(socketSet, clients) < 0) { // error handling } } } } }}void handleNetworkData(Clients &clients, SDLNet_SocketSet socketSet){ // Check for network activity: const int timeout = 0; int active = SDLNet_CheckSockets(socketSet, timeout); // If there is activity. if(active > 0) { // Loop over all connected clients. for(int i = 0 ; i < clients.size() ; ++i) { // If we are on a valid client. if(TCPSocket *client = clients) { // And it's socket has had some activity if(SDLNet_SocketReady(client)) { const int bufferSize = 511; // +1 to ensure there is room for NUL terminator char buffer[bufferSize + 1]; // Read the data. int bytes = SDLNet_TCP_Recv(client, buffer, bufferSize); // Handle all cases. switch(bytes) { case -1: // error handling break; case 0: // Socket was closed by remote host. // Close the socket and null the pointer in the vector. printf("Host %d disconnected\n", i); SDLNet_TCP_DelSocket(socketSet, client); SDLNet_TCP_Close(client); clients = 0; break; default: // We got some data! // Ensure it is NUL terminated and print. buffer[bytes] = '\0'; printf("Host %d says %s\n", i, buffer); break; } } } } }}
Its not perfect code. The two functions could be further broken down into additional functions, and good use of RAII wrapper classes for the SDLNet types would help a lot, but I figured that they would detract from the point, besides writing this is your job, not mine [smile]
Please note that I haven't even compiled it, so there may be a few syntax errors in there.
Now, while I say this is non-blocking there is still an issue of calls to SDL_TCP_Send blocking. Unfortunately SDL_Net doesn't appear to include any way to mark a socket explicitly non-blocking using its API. So you may still end up blocking when sending data. But this shouldn't block on receiving data anyway.