Files
Leo-ElixirLights/components/spit/src/SPITSocket.cpp
T

224 lines
7.8 KiB
C++

/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/*
* Unix implementation of a SPITSocket.
*
* File: UnixSocket.cpp
* Author: Hagi
*
* Created on 12. April 2019, 15:58
*/
#include "SPITSocket.h"
#include "Task.hpp"
#include "esp/uuid.h"
#include "esp_log.h"
#include "esp_netif.h"
#include <string>
#include <unistd.h>
using namespace std;
/**
* Creates socket. If there exist some, they will be closed.
* @return
*/
bool SPITSocket::createSocket_UDP() {
if (m_UDPSocketIP4 != INVALID_SOCKET) {
close(m_UDPSocketIP4);
m_UDPSocketIP4 = INVALID_SOCKET;
}
m_UDPSocketIP4 = socket(AF_INET, SOCK_DGRAM, 0);
int intResult = -1;
bool success = true;
if (m_UDPSocketIP4 == INVALID_SOCKET) {
success = false;
} else {
int intOptionValue4 = 1;
intResult = setsockopt(m_UDPSocketIP4, SOL_SOCKET, SO_REUSEADDR, (void *)&intOptionValue4, sizeof(intOptionValue4));
}
if (intResult < 0) {
success = false;
}
if (success == true) {
struct timeval tv;
tv.tv_sec = 1; // seconds
tv.tv_usec = 0; // microseconds (0.0 sec)
// This sets the receive timeout
setsockopt(m_UDPSocketIP4, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
// set the m_UDPSocketIP4 in NON BLOCKING mode because
// if the UDPSockets are in blocking mode the receiveMessage does not return in every constellation
// int intFlags;
// intFlags = fcntl(m_UDPSocketIP4, F_GETFL);
// fcntl(m_UDPSocketIP4, F_SETFL, intFlags | O_NONBLOCK);
// int intBufferSize = 2046 * 512;
// intResult = setsockopt(m_UDPSocketIP4, SOL_SOCKET, SO_RCVBUF, (void*)&intBufferSize, sizeof(intBufferSize));
// intResult = setsockopt(m_UDPSocketIP4, SOL_SOCKET, SO_SNDBUF, (void*)&intBufferSize, sizeof(intBufferSize));
}
return success;
}
/**
* Binds the socket to the client's ip address and port. The socket option SO_REUSEADDR is set to true.
* @param clientIp ip address of the client
* @param clientPort port of the client.
* if p_Port == "0" any free port will be used
* @return -1 id bind fails, o if bind is ok
*
*/
int SPITSocket::bindSocket(string clientIp, string clientPort) {
string stringClientPort;
if (clientPort.length() <= 0)
stringClientPort = "0";
else
stringClientPort = clientPort;
addrinfo *clientAI;
if (getaddrinfo(clientIp.data(), clientPort.data(), nullptr, &clientAI) != 0) {
return -1;
}
if (m_UDPSocketIP4 == INVALID_SOCKET) {
return -1;
}
auto *socketAdr = (sockaddr_in *)(clientAI->ai_addr);
int intSocklen = sizeof(*socketAdr);
// copy the socketAdr, may be not filled correct by getaddrinfo
sockaddr_in o_IP4SocketAddress;
memset(&o_IP4SocketAddress, 0, intSocklen);
o_IP4SocketAddress.sin_addr = socketAdr->sin_addr;
o_IP4SocketAddress.sin_family = socketAdr->sin_family;
o_IP4SocketAddress.sin_port = socketAdr->sin_port;
int intResult = bind(m_UDPSocketIP4, (const sockaddr *)(socketAdr), (socklen_t)intSocklen);
if (intResult < 0) {
return intResult;
}
if ((intResult == 0) &&
(getsockname(m_UDPSocketIP4, (sockaddr *)socketAdr, (socklen_t *)&intSocklen) != -1)) {
setBoundClientPort(to_string((int)socketAdr->sin_port));
}
freeaddrinfo(clientAI);
setIsBound(intResult != -1);
return intResult;
}
/**
* Set whether the socket are broadcast sockets or not. Set the sockopt SO_BROADCAST to 0 or 1
* @param p_Broadcast true SO_BROADCAST is set
* false = SO_BROADCAST is not set
* @return 0 = ok, -1 = error
*/
int SPITSocket::setBroadcast(bool broadcast) const {
int intOptionValue = broadcast ? 1 : 0;
int intResult = setsockopt(m_UDPSocketIP4, SOL_SOCKET, SO_BROADCAST, &intOptionValue, sizeof(int));
return intResult;
}
/**
* @param ip the ip address of the server
* @param port the port of the server
* @param msg buffer containing the message
* @param msgLength the length of the message
* @return -1 send fails
* 0 send is done
*/
int SPITSocket::sendMessageTo(const string &ip, const string &port, const char *message, int msgLength) {
if (m_UDPSocketIP4 == INVALID_SOCKET) {
return -1;
}
// prepare AddressInfo
int intResult = -1;
addrinfo *ptrAddressInfo;
if (getaddrinfo(ip.data(), port.data(), nullptr, &ptrAddressInfo) != 0) {
return intResult;
}
auto *ptrIP4SocketAddress = (sockaddr_in *)(ptrAddressInfo->ai_addr);
int intSocklen = sizeof(*ptrIP4SocketAddress);
if (m_UDPSocketIP4 != INVALID_SOCKET) {
// copy the ptrIP4SocketAddress, may be not filled correct by getaddrinfo
sockaddr_in o_IP4SocketAddress;
memset(&o_IP4SocketAddress, 0, intSocklen);
o_IP4SocketAddress.sin_addr = ptrIP4SocketAddress->sin_addr;
o_IP4SocketAddress.sin_family = ptrIP4SocketAddress->sin_family;
o_IP4SocketAddress.sin_port = ptrIP4SocketAddress->sin_port;
intResult = sendto(m_UDPSocketIP4, message, msgLength, 0, (const sockaddr *)&o_IP4SocketAddress, (socklen_t)intSocklen);
if (intResult < 0) {
ESP_LOGE("SPIT", "sendMessageTo IP4 sendto %s FAILED %s\n", ip.data(), strerror(errno));
ESP_LOGE("SPIT", "sendMessageTo ret=%d %s / %s / %d\n", intResult, port.data(), message, msgLength);
}
freeaddrinfo(ptrAddressInfo);
}
if (intResult < 0) {
return -1;
}
return 0;
}
/**
* Call this for receiving a message
* This will block until a message is received or the underlying socket is closed
* @param p_Buffer the buffer containing the received message later.
* @param p_BufferLength the size of the buffer
* @param p_ServerIP a pointer to a string, when a messages is received the string will contain the ip address of the sender/server
* if p_ServerIP is NULL - no detection of the server will be done.
* @return
*/
int SPITSocket::receiveMessage(char *p_Buffer, int p_BufferLength, string *serverIp) const {
int intResult = -1;
if (m_UDPSocketIP4 == INVALID_SOCKET) {
return intResult;
}
sockaddr_in o_IP4SocketAddress;
int intSocklen = sizeof(o_IP4SocketAddress);
memset(&o_IP4SocketAddress, 0, intSocklen);
intResult = recvfrom(m_UDPSocketIP4, (void *)p_Buffer, p_BufferLength, 0, (sockaddr *)&o_IP4SocketAddress, (socklen_t *)&intSocklen);
if (intResult >= 0 && serverIp != nullptr) {
char bufferIP4[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(o_IP4SocketAddress.sin_addr), bufferIP4, intSocklen);
*serverIp = bufferIP4;
}
return intResult;
}
SPITSocket::IPAddress SPITSocket::getAdaptersInfo() {
// iterate over active interfaces, and print out IPs
esp_netif_t *netif = nullptr;
if ((netif = esp_netif_next_unsafe(netif)) != nullptr) {
ESP_LOGI("SPIT", "Connected to %s", esp_netif_get_desc(netif));
esp_netif_ip_info_t ip;
ESP_ERROR_CHECK(esp_netif_get_ip_info(netif, &ip));
ESP_LOGI("SPIT", "- IPv4 address: " IPSTR ",", IP2STR(&ip.ip));
ESP_LOGI("SPIT", "- IPv4 netmask: " IPSTR ",", IP2STR(&ip.netmask));
char buffer[64];
sprintf(buffer, IPSTR, IP2STR(&ip.ip));
string stringIP4 = buffer;
sprintf(buffer, IPSTR, IP2STR(&ip.netmask));
string stringSubnet = buffer;
return {stringIP4, stringSubnet};
}
return {"0.0.0.0", "0.0.0.0"};
}
string createGUID() {
uuid_t uu;
uuid_generate(uu);
char uu_str[UUID_STR_LEN];
uuid_unparse(uu, uu_str);
return {uu_str};
}
string getClientHostName() {
const char *hostname;
esp_netif_get_hostname(esp_netif_get_default_netif(), &hostname);
if (hostname != nullptr) {
return {hostname};
}
return "";
}