Client Connection Tutorial

From PX Documentation

Jump to: navigation, search

This example is the "Hello World" for NetDog. The client simply connects to the server and then disconnects immediately. The server simply listens for and counts incoming connections.

Client

Include headers.

   #include <stdlib.h>
   #include <signal.h>
   
   #include "ND.h"
   
   #ifdef _MSC_VER
   #pragma comment(lib, "nd.lib")
   #endif
   
   char* serverIP = "127.0.0.1";
   
   int ourOwnerID = -1;
   int ourConnID = -1;

Define an event called "CloseMe" which the client sends to the server to ask to have its connection closed.

   typedef struct _CloseMeEvent {
   } CloseMeEvent;
   
   void closeMe(){
      CloseMeEvent e;
      NDSendEvent(&e, 1, sizeof(e));
   }

Callback which is called by the client when its connection is closed.

   int connClosedCB(NDEventData evtData){
      int connID = NDEventDataGetConnID(evtData);
      if (connID != ourConnID){
         dbg2("%s: close connection error. connID[%d] ourConnID[%d]\n", __FUNCTION__, connID, ourConnID);
         exit(0);
         return -1;
      }
   
      dbg0("%s: The conn closed. Exiting!\n", __FUNCTION__);
      NDShutdown();
      exit(0);
      return 0;
   }
   
   void trap_int(int unused){
      dbg0("Detected SIGINT\n");
      NDConnClose(ourConnID); // Close it directly
      NDEventLoop(); // Need to call the event loop before exiting (not needed if you comment the line below)
      exit(0); // (Un)comment this if you want to
   }

Main function.

   int main(int argc, char* const argv[]) {
   
      if (argc < 2){
         serverIP = "127.0.0.1";
      } else {
         serverIP = argv[1];
      }

Set debug levels.

      NDSetDebugLevel(2);
      NDSetNetDebug(0);
   
      //NDSetConnectTimeout(50); // Uncomment this if you want to test on a low-latency (e.g. local) connection

Connect to the server.

      //dbg0("Creating client conn...\n");
      ourConnID = NDCreateClientConn(serverIP, 56882,
   				  NDConnFlags::assignOwnerIDs |
     				  NDConnFlags::useKeepAlives
   				  //NDConnFlags::useEncryption
   				  );
      if (ourConnID < 0){
         dbg1("Error: Could not establish conn. Exiting.\n");
         return(-1);
      }

Install our "closeMe" event type.

      NDInstallEventType(NDEventType::connClosed, &connClosedCB, NULL, "connClosed");
      NDInstallEventType(1, NULL, NULL, "closeMe");

Wait for the connection to opened.

      // Wait for our conn to be active
      //dbg0("Waiting for session to start...\n");
      while (!NDConnIsStarted(ourConnID)){
         NDEventLoop();
         NDSleepMS(10); // 10ms
      }

Wait for our unique ownerID to be assigned.

      // Wait for our ownerID to be assigned (upon receiving a registerClientACK)
      while (ourOwnerID < 0){
         ourOwnerID = NDConnGetLocalOwnerID(ourConnID);
   
         if (ourOwnerID >= 0){
   	 dbg0("Set ourOwnerID[%d]\n", ourOwnerID);
         }
   
         NDEventLoop();
         NDSleepMS(10); // 10ms
      }

Here we exit immediately by calling NDShutdown() and then exit() but you can comment those if you want to leave the client connected. We also show other ways of closing the client connection, i.e. by closing the connection on the client side using NDConnClose() and by calling closeMe() which sends the "closeMe" event type to the server. The server will then respond to that event by closing the connection on the server side.

      // Here you have three ways to close your own conn. Uncomment the following lines as desired.
      //NDConnClose(ourConnID); // Close it directly
      //closeMe();              // Ask the server to close it using an event
                                // Or just hit CTRL-C to kill the application
   
      NDShutdown();
      exit(0); // (Un)comment this if you want to
   
      signal(SIGINT, trap_int);

Assuming the above exit() is commented out, loop continuously, frequently calling NDEventLoop(), which handles incoming network events.

      //Now we're "playing"
      for (;;){
         NDEventLoop();
         NDSleepMS(10);
      }
   
      return(0);
   }

Server

Include headers.

   #include "ND.h"
   
   #ifdef _MSC_VER
   #pragma comment(lib, "nd.lib")
   #endif
   
   int connsClosed = 0;
   int lastConnsClosed = 0;

Define the "closeMe" event, the same event type used by the client.

   typedef struct _CloseMeEvent {
   } CloseMeEvent;
   
   int CloseMeCB(NDEventData evtData){
      int connID = NDEventDataGetConnID(evtData);
      NDConnClose(connID);
      return 0;
   }

The callback which is called when the client connection is closed. Here we just count the number of closed connections.

   int connCloseCallback(NDEventData data){
      connsClosed++;
      //dbg0("ConnID[%d] close callback\n", NDEventDataGetConnID(data));
      return 0;
   }

Main function.

   int main(){
   

Set debug levels.

      NDSetDebugLevel(2);
      NDSetNetDebug(0);

Create a listening server connection on port 56882. Set connection flags to use keepalives, start the "game" immediately, and assign ownerIDs to clients automatically.

      int connID = NDCreateServerConn(56882,
   				   NDConnFlags::useKeepAlives |
   				   NDConnFlags::startImmediately |
   				   NDConnFlags::assignOwnerIDs
   				   //NDConnFlags::useEncryption
   				   );
   
      if (connID < 0){
         dbg1("Could not establish listening port\n");
         return(0);
      }

Install the "closeMe" event (same event used by the client).

      NDInstallEventType(1, &CloseMeCB, NULL, "closeMe");
      NDInstallEventType(NDEventType::connClosed, &connCloseCallback);
   
      NDConnSetTimeout(connID, 60*60*1000); // 1 hour timeout

Loop continuously, listening for client connections.

      long now = NDTimeMS();
      long lastTime = now;
      long startTime = now;
      for (;;){
   
         now = NDTimeMS();
         // Close all conns after 10 seconds. Disable this if you like.
         if (now-lastTime > 10000){
   	 //dbg0("Closing All Conns!\n");
   	 dbg0("Conns closed: %d (%.2f/s)\n", connsClosed, 1000.0f*(float)(connsClosed-lastConnsClosed)/(float)(now-lastTime));
   	 //NDCloseAllConns(); // Uncomment this if you want to periodically close all connections
   	 lastTime = now;
   	 lastConnsClosed = connsClosed;
         }

NDEventLoop() must be called frequently in order to process incoming network events.

         NDEventLoop();
         NDEventLoop();
         NDSleepMS(10); // 10ms
      }
   
      return(0);
   }