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
- also add some variables
Now in the constructor, initialize the variables that need to have defined init values
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:
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.
Here the code used to create a new thread
And the method called in the new thread (the UDP listener server thread)
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
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: