[TUTORIAL] Send UDP trame from 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 some variables
Code:
  // socket variables
  bool environmentAlreadySet;
  int senderSocket; // socket to data
  struct sockaddr_in sadSender;
  char serverHostname[256];
  u_short serverPort;
  char identificationData[22];

Now in the constructor, initialize the variables that need to have defined init values
Code:
ExampleInternalsPlugin() {
   environmentAlreadySet = false;   
   senderSocket = 0;
}

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
        serverPort = (u_short)GetPrivateProfileInt("Server", "Port", 666, str.c_str());
        GetPrivateProfileString("Server", "Address", "localhost", serverHostname, 255, str.c_str());
        
        senderSocket = socket(PF_INET, SOCK_DGRAM, 0);
        if (senderSocket  < 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(serverHostname, NULL, &hints, &pResult);
    //if(errcode != 0)
    //    return ERROR;
        memset(&sadSender, 0, sizeof(sadSender));
        sadSender.sin_family      = AF_INET;
        sadSender.sin_addr.S_un.S_addr = *((ULONG*)&(((sockaddr_in*)pResult->ai_addr)->sin_addr));
        sadSender.sin_port        = htons(serverPort);

        environmentAlreadySet = true;
}

Now to send data throw UDP you can use this kind of code
Code:
        // send a trame containing 12 bytes.
        memcpy(&identificationData[0], &mSession, sizeof(long));
        memcpy(&identificationData[4], &mCurrentET, sizeof(double));
        sendto(senderSocket, identificationData, 12, 0, (struct sockaddr *) &sadSender, sizeof(struct sockaddr));
 
Last edited by a moderator:
Wow, thanks for this tutorial :) Can you give some code how to listen the data send by plugin in a C++/C# application?

Here an example in C# listening UDP trame comming on port 639

Code:
            int listeningPort = 639;
            UdpClient udpServer = new UdpClient(listeningPort);
            // while thread is alive
            while (Thread.CurrentThread.IsAlive)
            {
                IPEndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);
                byte[] data = udpServer.Receive(ref remoteEP);
                
                if (data.Length >= 0)
                {
                             // do something
                }     
          }
 
Back
Top