From f591d34d97d1b0eb14c35923b630ae77736c7813 Mon Sep 17 00:00:00 2001 From: Pascal Serrarens Date: Sat, 11 Jan 2025 11:35:28 +0100 Subject: [PATCH] Posix support --- Participant.cpp | 13 +++ UdpPosix.cpp | 207 ++++++++++++++++++++++++++++++++++++++++++++++++ UdpPosix.h | 17 ++++ 3 files changed, 237 insertions(+) create mode 100644 UdpPosix.cpp create mode 100644 UdpPosix.h diff --git a/Participant.cpp b/Participant.cpp index dc5d0c8..723bd2f 100644 --- a/Participant.cpp +++ b/Participant.cpp @@ -5,6 +5,7 @@ #define BUF_SIZE 1024 #include "UdpArduino.h" +#include "UdpPosix.h" #include "UdpWindows.h" #if defined(_WIN32) || defined(_WIN64) @@ -47,6 +48,9 @@ void Passer::Control::Participant::SetupUDP(int localPort, #if defined(_WIN32) || defined(_WIN64) UdpWindows *thisWindows = static_cast(this); thisWindows->Setup(localPort, remoteIpAddress, remotePort); +#elif defined(__unix__) || defined(__APPLE__) + UdpPosix *thisPosix = static_cast(this); + thisPosix->Setup(localPort, remoteIpAddress, remotePort); #elif defined(ARDUINO) UdpArduino *thisArduino = static_cast(this); thisArduino->Setup(localPort, remoteIpAddress, remotePort); @@ -70,6 +74,9 @@ void Participant::ReceiveUDP() { #if defined(_WIN32) || defined(_WIN64) UdpWindows *thisWindows = static_cast(this); thisWindows->Receive(); +#elif defined(__unix__) || defined(__APPLE__) + UdpPosix *thisPosix = static_cast(this); + thisPosix->Receive(); #elif defined(ARDUINO) UdpArduino *thisArduino = static_cast(this); thisArduino->Receive(); @@ -125,6 +132,9 @@ bool Participant::Send(IMessage *msg) { #if defined(_WIN32) || defined(_WIN64) UdpWindows *thisWindows = static_cast(this); return thisWindows->Send(msg); +#elif defined(__unix__) || defined(__APPLE__) + UdpPosix *thisPosix = static_cast(this); + return thisPosix->Send(msg); #elif defined(ARDUINO) UdpArduino *thisArduino = static_cast(this); return thisArduino->Send(msg); @@ -135,6 +145,9 @@ bool Participant::Publish(IMessage *msg) { #if defined(_WIN32) || defined(_WIN64) UdpWindows *thisWindows = static_cast(this); return thisWindows->Publish(msg); +#elif defined(__unix__) || defined(__APPLE__) + UdpPosix *thisPosix = static_cast(this); + return thisPosix->Publish(msg); #elif defined(ARDUINO) UdpArduino *thisArduino = static_cast(this); return thisArduino->Publish(msg); diff --git a/UdpPosix.cpp b/UdpPosix.cpp new file mode 100644 index 0000000..3321f54 --- /dev/null +++ b/UdpPosix.cpp @@ -0,0 +1,207 @@ +#include "UdpPosix.h" + +#if defined(__unix__) || defined(__APPLE__) +#include +#include // For fcntl +#include +#include +#include +#endif + +namespace Passer { +namespace Control { + +void UdpPosix::Setup(int localPort, const char *remoteIpAddress, + int remotePort) { +#if defined(__unix__) || defined(__APPLE__) + + // Create a UDP socket +#if defined(_WIN32) || defined(_WIN64) + // Windows-specific Winsock initialization + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { + std::cerr << "WSAStartup failed" << std::endl; + return; + } +#endif + +#if defined(_WIN32) || defined(_WIN64) + this->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); +#elif defined(__unix__) || defined(__APPLE__) + this->sock = socket(AF_INET, SOCK_DGRAM, 0); +#endif + + if (this->sock < 0) { + std::cerr << "Error creating socket" << std::endl; + return; + } + +// Set the socket to non-blocking mode +#if defined(_WIN32) || defined(_WIN64) + u_long mode = 1; // 1 to enable non-blocking socket + ioctlsocket(this->sock, FIONBIO, &mode); +#elif defined(__unix__) || defined(__APPLE__) + int flags = fcntl(this->sock, F_GETFL, 0); + fcntl(this->sock, F_SETFL, flags | O_NONBLOCK); +#endif + + if (remotePort != 0) { + // Set up the address to send to + memset(&remote_addr, 0, sizeof(remote_addr)); + remote_addr.sin_family = AF_INET; + remote_addr.sin_port = htons(remotePort); + if (inet_pton(AF_INET, remoteIpAddress, &remote_addr.sin_addr) <= 0) { + std::cerr << "Invalid address" << std::endl; +#if defined(_WIN32) || defined(_WIN64) + closesocket(sock); + WSACleanup(); +#elif defined(__unix__) || defined(__APPLE__) + close(sock); +#endif + return; + } + } + + // Set up the receiving address + memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(localPort); + if (inet_pton(AF_INET, "0.0.0.0", &server_addr.sin_addr) <= 0) { + std::cerr << "Invalid address" << std::endl; +#if defined(_WIN32) || defined(_WIN64) + closesocket(sock); + WSACleanup(); +#elif defined(__unix__) || defined(__APPLE__) + close(sock); +#endif + return; + } + + // Bind the socket to the specified port + if (bind(this->sock, (const struct sockaddr *)&server_addr, + sizeof(server_addr)) < 0) { + std::cerr << "Bind failed" << std::endl; +#if defined(_WIN32) || defined(_WIN64) + closesocket(sock); + WSACleanup(); +#elif defined(__unix__) || defined(__APPLE__) + close(sock); +#endif + } + +#endif +} + +void UdpPosix::Receive() { +#if defined(__unix__) || defined(__APPLE__) + // char ip_str[INET_ADDRSTRLEN]; + // inet_ntop(AF_INET, &(server_addr.sin_addr), ip_str, INET_ADDRSTRLEN); + // std::cout << this->name << " Receive on " << ip_str << ":" + // << ntohs(server_addr.sin_port) << "\n"; + + sockaddr_in client_addr; +#if defined(_WIN32) || defined(_WIN64) + int len = sizeof(client_addr); +#elif defined(__unix__) || defined(__APPLE__) + socklen_t len = sizeof(client_addr); +#endif + int packetSize = recvfrom(this->sock, buffer, sizeof(buffer), 0, + (struct sockaddr *)&client_addr, &len); + // std::cout << "received data " << packetSize << "\n"; + if (packetSize < 0) { +#if defined(_WIN32) || defined(_WIN64) + int error_code = WSAGetLastError(); // Get the error code on Windows + if (error_code != WSAEWOULDBLOCK) + std::cerr << "recvfrom failed with error: " << error_code << std::endl; +#else + // std::cerr << "recvfrom failed with error: " << packetSize << std::endl; +#endif + } else if (packetSize > 0) { + char sender_ipAddress[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &(client_addr.sin_addr), sender_ipAddress, + INET_ADDRSTRLEN); + int sender_port = ntohs(client_addr.sin_port); + + Participant *remoteParticipant = + this->GetParticipant(sender_ipAddress, sender_port); + if (remoteParticipant == nullptr) { + remoteParticipant = this->AddParticipant(sender_ipAddress, sender_port); + std::cout << "New sender " << sender_ipAddress << ":" << sender_port + << "\n"; + std::cout << "New remote participant " << remoteParticipant->ipAddress + << ":" << remoteParticipant->port << " " + << (int)remoteParticipant->networkId << "\n"; + } + + ReceiveData(packetSize, remoteParticipant); + } +#endif +} + +bool UdpPosix::Send( + IMessage *msg) { // Send the message to the specified address and port +#if defined(__unix__) || defined(__APPLE__) + int bufferSize = msg->Serialize(this->buffer); + // std::cout << "buffer size " << bufferSize << "\n"; + if (bufferSize <= 0) + return true; + + char ip_str[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &(remote_addr.sin_addr), ip_str, INET_ADDRSTRLEN); + std::cout << "Send to " << ip_str << ":" << ntohs(remote_addr.sin_port) + << "\n"; + int sent_bytes = sendto(sock, this->buffer, bufferSize, 0, + (struct sockaddr *)&remote_addr, sizeof(remote_addr)); +#if defined(_WIN32) || defined(_WIN64) + if (sent_bytes <= SOCKET_ERROR) { + int error_code = WSAGetLastError(); // Get the error code on Windows + std::cerr << "sendto failed with error: " << error_code << std::endl; + closesocket(sock); + WSACleanup(); + return false; + } +#elif defined(__unix__) || defined(__APPLE__) + if (sent_bytes < 0) { + std::cerr << "sendto failed with error: " << sent_bytes << std::endl; + close(sock); + return false; + } +#endif +#endif + return true; +} + +bool UdpPosix::Publish(IMessage *msg) { +#if defined(__unix__) || defined(__APPLE__) + int bufferSize = msg->Serialize(this->buffer); + if (bufferSize <= 0) + return true; + + char ip_str[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &(broadcast_addr.sin_addr), ip_str, INET_ADDRSTRLEN); + std::cout << "Publish to " << ip_str << ":" << ntohs(broadcast_addr.sin_port) + << "\n"; + int sent_bytes = + sendto(sock, this->buffer, bufferSize, 0, + (struct sockaddr *)&broadcast_addr, sizeof(broadcast_addr)); +#if defined(_WIN32) || defined(_WIN64) + if (sent_bytes <= SOCKET_ERROR) { + int error_code = WSAGetLastError(); // Get the error code on Windows + std::cerr << "sendto failed with error: " << error_code << std::endl; + closesocket(sock); + WSACleanup(); + return false; + } +#elif defined(__unix__) || defined(__APPLE__) + if (sent_bytes < 0) { + std::cerr << "sendto failed with error: " << sent_bytes << std::endl; + close(sock); + return false; + } +#endif +#endif + return true; +} + +} // namespace Control +} // namespace Passer \ No newline at end of file diff --git a/UdpPosix.h b/UdpPosix.h new file mode 100644 index 0000000..13022cf --- /dev/null +++ b/UdpPosix.h @@ -0,0 +1,17 @@ +#pragma once + +#include "Participant.h" + +namespace Passer { +namespace Control { + +class UdpPosix : public Participant { +public: + void Setup(int localPort, const char *remoteIpAddress, int remotePort); + void Receive(); + bool Send(IMessage *msg); + bool Publish(IMessage *msg); +}; + +} // namespace Control +} // namespace Passer \ No newline at end of file