diff --git a/build.gradle b/build.gradle index 3c5597dea3..0a4d1d1639 100644 --- a/build.gradle +++ b/build.gradle @@ -56,11 +56,11 @@ model { x86ExcludeSymbols = [ '_CT??_R0?AV_System_error', '_CT??_R0?AVexception', '_CT??_R0?AVfailure', '_CT??_R0?AVruntime_error', '_CT??_R0?AVsystem_error', '_CTA5?AVfailure', '_TI5?AVfailure', '_CT??_R0?AVout_of_range', '_CTA3?AVout_of_range', - '_TI3?AVout_of_range' ] + '_TI3?AVout_of_range', '_CT??_R0?AVbad_cast' ] x64ExcludeSymbols = [ '_CT??_R0?AV_System_error', '_CT??_R0?AVexception', '_CT??_R0?AVfailure', '_CT??_R0?AVruntime_error', '_CT??_R0?AVsystem_error', '_CTA5?AVfailure', '_TI5?AVfailure', '_CT??_R0?AVout_of_range', '_CTA3?AVout_of_range', - '_TI3?AVout_of_range' ] + '_TI3?AVout_of_range', '_CT??_R0?AVbad_cast' ] } } jniConfigs { diff --git a/src/main/native/cpp/udpsockets/UDPClient.cpp b/src/main/native/cpp/udpsockets/UDPClient.cpp new file mode 100644 index 0000000000..c76d0a0321 --- /dev/null +++ b/src/main/native/cpp/udpsockets/UDPClient.cpp @@ -0,0 +1,175 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2017. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#include "udpsockets/UDPClient.h" + +#ifdef _WIN32 +#include +#include +#pragma comment(lib, "Ws2_32.lib") +#else +#include +#include +#include +#include +#endif + +#include "support/Logger.h" +#include "tcpsockets/SocketError.h" + + +using namespace wpi; + +UDPClient::UDPClient(Logger& logger) + : UDPClient("", logger) {} + +UDPClient::UDPClient(llvm::StringRef address, Logger& logger) + : m_lsd(0), + m_address(address), + m_logger(logger) {} + +UDPClient::UDPClient(UDPClient&& other) + : m_lsd(other.m_lsd), + m_address(std::move(other.m_address)), + m_logger(other.m_logger) { + other.m_lsd = 0; +} + +UDPClient::~UDPClient() { + if (m_lsd > 0) { + shutdown(); + } +} + +UDPClient& UDPClient::operator=(UDPClient&& other) { + if (this == &other) return *this; + shutdown(); + m_logger = other.m_logger; + m_lsd = other.m_lsd; + m_address = std::move(other.m_address); + other.m_lsd = 0; + return *this; +} + +int UDPClient::start() { + if (m_lsd > 0) return 0; + +#ifdef _WIN32 + WSAData wsaData; + WORD wVersionRequested = MAKEWORD(2, 2); + WSAStartup(wVersionRequested, &wsaData); +#endif + + m_lsd = socket(AF_INET, SOCK_DGRAM, 0); + + if (m_lsd < 0) { + WPI_ERROR(m_logger, "could not create socket"); + return -1; + } + + struct sockaddr_in addr; + std::memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + if (m_address.size() > 0) { +#ifdef _WIN32 + llvm::SmallString<128> addr_copy(m_address); + addr_copy.push_back('\0'); + int res = InetPton(PF_INET, addr_copy.data(), &(addr.sin_addr)); +#else + int res = inet_pton(PF_INET, m_address.c_str(), &(addr.sin_addr)); +#endif + if (res != 1) { + WPI_ERROR(m_logger, "could not resolve " << m_address << " address"); + return -1; + } + } else { + addr.sin_addr.s_addr = INADDR_ANY; + } + addr.sin_port = htons(0); + + int result = bind(m_lsd, reinterpret_cast(&addr), sizeof(addr)); + if (result != 0) { + WPI_ERROR(m_logger, "bind() failed: " << SocketStrerror()); + return result; + } + return 0; +} + +void UDPClient::shutdown() { + if (m_lsd > 0) { +#ifdef _WIN32 + ::shutdown(m_lsd, SD_BOTH); + closesocket(m_lsd); + WSACleanup(); +#else + ::shutdown(m_lsd, SHUT_RDWR); + close(m_lsd); +#endif + m_lsd = 0; + } +} + +int UDPClient::send(llvm::ArrayRef data, + llvm::StringRef server, int port) { + // server must be a resolvable IP address + struct sockaddr_in addr; + std::memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + if (server.size() > 0) { + llvm::SmallVector addr_store; + auto remoteAddr = server.c_str(addr_store); +#ifdef _WIN32 + int res = InetPton(AF_INET, remoteAddr, &(addr.sin_addr)); +#else + int res = inet_pton(AF_INET, remoteAddr, &(addr.sin_addr)); +#endif + if (res != 1) { + WPI_ERROR(m_logger, "could not resolve " << server << " address"); + return -1; + } + } else { + WPI_ERROR(m_logger, "server must be passed"); + return -1; + } + addr.sin_port = htons(port); + + // sendto should not block + int result = sendto(m_lsd, reinterpret_cast(data.data()), + data.size(), 0, reinterpret_cast(&addr), + sizeof(addr)); + return result; +} + +int UDPClient::send(llvm::StringRef data, + llvm::StringRef server, int port) { + // server must be a resolvable IP address + struct sockaddr_in addr; + std::memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + if (server.size() > 0) { + llvm::SmallVector addr_store; + auto remoteAddr = server.c_str(addr_store); +#ifdef _WIN32 + int res = InetPton(AF_INET, remoteAddr, &(addr.sin_addr)); +#else + int res = inet_pton(AF_INET, remoteAddr, &(addr.sin_addr)); +#endif + if (res != 1) { + WPI_ERROR(m_logger, "could not resolve " << server << " address"); + return -1; + } + } else { + WPI_ERROR(m_logger, "server must be passed"); + return -1; + } + addr.sin_port = htons(port); + + // sendto should not block + int result = sendto(m_lsd, data.data(), data.size(), 0, + reinterpret_cast(&addr), sizeof(addr)); + return result; +} diff --git a/src/main/native/include/udpsockets/UDPClient.h b/src/main/native/include/udpsockets/UDPClient.h new file mode 100644 index 0000000000..a7e8eca814 --- /dev/null +++ b/src/main/native/include/udpsockets/UDPClient.h @@ -0,0 +1,46 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2017. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#ifndef WPIUTIL_UDPSOCKETS_UDPCLIENT_H_ +#define WPIUTIL_UDPSOCKETS_UDPCLIENT_H_ + +#include + +#include "llvm/ArrayRef.h" +#include "llvm/StringRef.h" +#include "support/mutex.h" + + +namespace wpi { + +class Logger; + +class UDPClient { + int m_lsd; + std::string m_address; + Logger& m_logger; + +public: + explicit UDPClient(Logger& logger); + UDPClient(llvm::StringRef address, Logger& logger); + UDPClient(const UDPClient& other) = delete; + UDPClient(UDPClient&& other); + ~UDPClient(); + + UDPClient& operator=(const UDPClient& other) = delete; + UDPClient& operator=(UDPClient&& other); + + int start(); + void shutdown(); + // The passed in address MUST be a resolved IP address. + int send(llvm::ArrayRef data, llvm::StringRef server, int port); + int send(llvm::StringRef data, llvm::StringRef server, int port); +}; + +} // namespace wpi + +#endif // WPIUTIL_UDPSOCKETS_UDPCLIENT_H_