[TUTORIAL] Send UDP trame from 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 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: Jun 24, 2014
  2. formula1996

    formula1996 Registered

    Joined:
    Nov 2, 2012
    Messages:
    42
    Likes Received:
    1
    Wow, thanks for this tutorial :) Can you give some code how to listen the data send by plugin in a C++/C# application?
     
  3. Gerald Jacobson

    Gerald Jacobson Registered

    Joined:
    Jan 26, 2013
    Messages:
    827
    Likes Received:
    18
    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
                    }     
              }
    
     

Share This Page