[TUTORIAL] Listen UDP trame inside rF2 plugin

Gerald Jacobson

Registered
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:
Back
Top