[TUTORIAL] Listen UDP trame inside rF2 plugin

Discussion in 'Plugins' started by Gerald Jacobson, Jun 24, 2014.

  1. Gerald Jacobson

    Gerald Jacobson Registered

    Joined:
    Jan 26, 2013
    Messages:
    827
    Likes Received:
    18
    Here an example of code used to listen for UDP trame. As C language is not my programming language my code can be far from best.
    This code works in 32bit and 64bit (Use VC++ 2012 express - much easier to build DLL for 32bits and 64bits than VC++ 2010)

    Notes:
    - This example take the ISI example plugin as code base
    - In C when a variable is declared, it is not initialized with classical values (so if you do not explicitly set a variable default value in the constructor, a new created variable will have a random value) -> Main source of issue for JAVA, C#, ... developpers
    - This example use Naming Resolution (to covnert a DNS name into an IP address), and as "gethostbyname" is obsolete, i need to use "getaddrinfo" instead. This method needs to include <WinSock2.h>. But as this version contains structure also declared in the <windows.h> include, you need to specify #include <WinSock2.h> before #include <windows.h>
    - This example also use WINAPI to read ini files.

    Here we go:
    - in the Example.hpp add the following method decalration
    Code:
            void ThreadListenerUPD(); // will be listening method running in a separate thread
            static void thread_func_(void* p);   // method used to create a new thread and call ThreadListenerUPD
    
    - also add some variables
    Code:
      // socket variables
      bool environmentAlreadySet;
      bool stopListening;
      int receiverSocket; // socket to receive data from
      struct sockaddr_in sadReceiver;
      u_short port;
    
    Now in the constructor, initialize the variables that need to have defined init values
    Code:
    ExampleInternalsPlugin() {
       environmentAlreadySet = false;   
       receiverSocket = 0;
       stopListening = false;
    }
    
    Now in the Example.cpp, add the includes for socket. The #include <WinSock2.h> must be inserted before the #include "Example.hpp"
    The include part looks like:
    Code:
    // socket WinSock2.h must be included before <windows.h>
    #include <WinSock2.h>
    #include "Example.hpp"          // corresponding header file
    #include <math.h>               // for atan2, sqrt
    #include <stdio.h>              // for sample output
    #include <limits>
    #include <string>         // std::string
    // socket
    #include <ws2tcpip.h>
    // thread
    #include <process.h>
    #pragma comment(lib, "Ws2_32.lib")
    
    As i use INI file to configure the plugin and i want the ini file are stored in the <rF2Data>/UserData/<profile> directory and not in the <rF2Code>/plugins directory. To achieve that from inside the plugin, i use the rF2 SetEnvironment API method to retrieve profile path.
    Code:
    void ExampleInternalsPlugin::SetEnvironment( const EnvironmentInfoV01 &info )       {
    
            // as the SetEnvironment can be called several times at launch                  
            if (environmentAlreadySet) {
                    return;
            }
    
            // retrieve the ini filename (full path)
            std::string str = info.mPath[1];
            unsigned found = str.find_last_of("/\\");
            str = str.substr(0,found);
            str.append("\\ExampleInternalsPlugin.ini");
    
            // read ini
            port = (u_short)GetPrivateProfileInt("Plugin", "Port", 12345, str.c_str());
            
            
    
            environmentAlreadySet = true;
    
            _beginthread(thread_func_, 0, this );
    }
    
    Here the code used to create a new thread
    Code:
     void ExampleInternalsPlugin::thread_func_(void* p) {
            ExampleInternalsPlugin* this_ = reinterpret_cast<ExampleInternalsPlugin*>(p);
            this_->ThreadListenerUPD();
     }
    
    And the method called in the new thread (the UDP listener server thread)
    Code:
    void ExampleInternalsPlugin::ThreadListenerUPD() { 
            // buffer used to receive incomming UDP trame (must be greater or equals to the maximum size of yours awaiting trame)
            char buf[1024];
    
            struct sockaddr_in from;
            int fromlen;
            int n;
    
            receiverSocket = socket(PF_INET, SOCK_DGRAM, 0);
            if (receiverSocket  < 0) {
                    return;
            }
            
            //gethostbyname by getaddrinfo replacement
        ADDRINFO hints = {sizeof(addrinfo)};
        hints.ai_flags = AI_ALL;
        hints.ai_family = PF_INET;
        hints.ai_protocol = IPPROTO_IPV4;
        ADDRINFO* pResult = NULL;
        int errcode = getaddrinfo("localhost", NULL, &hints, &pResult);
        //if(errcode != 0)
        //    return ERROR;
        memset(&sadReceiver, 0, sizeof(sadReceiver));
        sadReceiver.sin_family      = AF_INET;
        sadReceiver.sin_addr.S_un.S_addr = *((ULONG*)&(((sockaddr_in*)pResult->ai_addr)->sin_addr));
            sadReceiver.sin_port        = htons(port);
    
     
            if (bind(receiverSocket,(struct sockaddr *)&sadReceiver, sizeof(sadReceiver)) < 0) {
                    return;
            }
            fromlen = sizeof(struct sockaddr_in);
            
            while(!stopListening) {
                    Sleep(10);
                    n = recvfrom(receiverSocket, buf, 1024, 0, (struct sockaddr *)&from, &fromlen);
                    if (n > 0) {
                            if (memcmp("RF2WSe", buf, 6) == 0) {
                                 // the received UDP trame start with 6 characters equals to "RF2WSe"
                                    // do what you need (but very quickly :))
                            } else if (memcmp("RF2WSw", buf, 6) == 0) { // new weather received
                                 // the received UDP trame start with 6 characters equals to "RF2WSw"
                                    // do what you need (but very quickly :))
                            }
                    }
            }
    
            if (receiverSocket > 0) {
                    closesocket(receiverSocket);
                    receiverSocket = 0;
            }
    }
    
     
    Last edited by a moderator: Jun 24, 2014
  2. MaD_King

    MaD_King Registered

    Joined:
    Oct 5, 2010
    Messages:
    1,827
    Likes Received:
    611
    ISI Admin, please sticky this post, very useful.
    Thank you Gerald.
     
  3. Noel Hibbard

    Noel Hibbard Registered

    Joined:
    Oct 5, 2010
    Messages:
    2,744
    Likes Received:
    40
    Great info. Thanks for sharing!
     

Share This Page