summaryrefslogtreecommitdiffstats
path: root/xbmc/network/UdpClient.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/network/UdpClient.cpp')
-rw-r--r--xbmc/network/UdpClient.cpp262
1 files changed, 262 insertions, 0 deletions
diff --git a/xbmc/network/UdpClient.cpp b/xbmc/network/UdpClient.cpp
new file mode 100644
index 0000000..97ac4ed
--- /dev/null
+++ b/xbmc/network/UdpClient.cpp
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "UdpClient.h"
+
+#include <mutex>
+#ifdef TARGET_POSIX
+#include <sys/ioctl.h>
+#endif
+#include "Network.h"
+#include "utils/TimeUtils.h"
+#include "utils/log.h"
+#include "windowing/GraphicContext.h"
+
+#include <chrono>
+
+#include <arpa/inet.h>
+
+using namespace std::chrono_literals;
+
+#define UDPCLIENT_DEBUG_LEVEL LOGDEBUG
+
+CUdpClient::CUdpClient(void) : CThread("UDPClient")
+{}
+
+CUdpClient::~CUdpClient(void) = default;
+
+bool CUdpClient::Create(void)
+{
+ m_bStop = false;
+
+ CLog::Log(UDPCLIENT_DEBUG_LEVEL, "UDPCLIENT: Creating UDP socket...");
+
+ // Create a UDP socket
+ client_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (client_socket == SOCKET_ERROR)
+ {
+ CLog::Log(UDPCLIENT_DEBUG_LEVEL, "UDPCLIENT: Unable to create socket.");
+ return false;
+ }
+
+ CLog::Log(UDPCLIENT_DEBUG_LEVEL, "UDPCLIENT: Setting broadcast socket option...");
+
+ unsigned int value = 1;
+ if ( setsockopt( client_socket, SOL_SOCKET, SO_BROADCAST, (char*) &value, sizeof( unsigned int ) ) == SOCKET_ERROR)
+ {
+ CLog::Log(UDPCLIENT_DEBUG_LEVEL, "UDPCLIENT: Unable to set socket option.");
+ return false;
+ }
+
+ CLog::Log(UDPCLIENT_DEBUG_LEVEL, "UDPCLIENT: Setting non-blocking socket options...");
+
+ unsigned long nonblocking = 1;
+ ioctlsocket(client_socket, FIONBIO, &nonblocking);
+
+ CLog::Log(UDPCLIENT_DEBUG_LEVEL, "UDPCLIENT: Spawning listener thread...");
+ CThread::Create(false);
+
+ CLog::Log(UDPCLIENT_DEBUG_LEVEL, "UDPCLIENT: Ready.");
+
+ return true;
+}
+
+void CUdpClient::Destroy()
+{
+ StopThread();
+ closesocket(client_socket);
+}
+
+void CUdpClient::OnStartup()
+{
+ SetPriority(ThreadPriority::LOWEST);
+}
+
+bool CUdpClient::Broadcast(int aPort, const std::string& aMessage)
+{
+ std::unique_lock<CCriticalSection> lock(critical_section);
+
+ struct sockaddr_in addr;
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(aPort);
+ addr.sin_addr.s_addr = INADDR_BROADCAST;
+ memset(&addr.sin_zero, 0, sizeof(addr.sin_zero));
+
+ UdpCommand broadcast = {addr, aMessage, NULL, 0};
+ commands.push_back(broadcast);
+
+ return true;
+}
+
+
+bool CUdpClient::Send(const std::string& aIpAddress, int aPort, const std::string& aMessage)
+{
+ std::unique_lock<CCriticalSection> lock(critical_section);
+
+ struct sockaddr_in addr;
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(aPort);
+ addr.sin_addr.s_addr = inet_addr(aIpAddress.c_str());
+ memset(&addr.sin_zero, 0, sizeof(addr.sin_zero));
+
+ UdpCommand transmit = {addr, aMessage, NULL, 0};
+ commands.push_back(transmit);
+
+ return true;
+}
+
+bool CUdpClient::Send(struct sockaddr_in aAddress, const std::string& aMessage)
+{
+ std::unique_lock<CCriticalSection> lock(critical_section);
+
+ UdpCommand transmit = {aAddress, aMessage, NULL, 0};
+ commands.push_back(transmit);
+
+ return true;
+}
+
+bool CUdpClient::Send(struct sockaddr_in aAddress, unsigned char* pMessage, DWORD dwSize)
+{
+ std::unique_lock<CCriticalSection> lock(critical_section);
+
+ UdpCommand transmit = {aAddress, "", pMessage, dwSize};
+ commands.push_back(transmit);
+
+ return true;
+}
+
+
+void CUdpClient::Process()
+{
+ CThread::Sleep(2000ms);
+
+ CLog::Log(UDPCLIENT_DEBUG_LEVEL, "UDPCLIENT: Listening.");
+
+ struct sockaddr_in remoteAddress;
+ char messageBuffer[1024];
+ DWORD dataAvailable;
+
+ while ( !m_bStop )
+ {
+ fd_set readset, exceptset;
+ FD_ZERO(&readset); FD_SET(client_socket, &readset);
+ FD_ZERO(&exceptset); FD_SET(client_socket, &exceptset);
+
+ int nfds = (int)(client_socket);
+ timeval tv = { 0, 100000 };
+ if (select(nfds, &readset, NULL, &exceptset, &tv) < 0)
+ {
+ CLog::Log(LOGERROR, "UDPCLIENT: failed to select on socket");
+ break;
+ }
+
+ // is there any data to read
+ dataAvailable = 0;
+ ioctlsocket(client_socket, FIONREAD, &dataAvailable);
+
+ // while there is data to read
+ while (dataAvailable > 0)
+ {
+ // read data
+ int messageLength = sizeof(messageBuffer) - 1 ;
+#ifndef TARGET_POSIX
+ int remoteAddressSize;
+#else
+ socklen_t remoteAddressSize;
+#endif
+ remoteAddressSize = sizeof(remoteAddress);
+
+ int ret = recvfrom(client_socket, messageBuffer, messageLength, 0, (struct sockaddr *) & remoteAddress, &remoteAddressSize);
+ if (ret != SOCKET_ERROR)
+ {
+ // Packet received
+ messageLength = ret;
+ messageBuffer[messageLength] = '\0';
+
+ std::string message = messageBuffer;
+
+ auto now = std::chrono::steady_clock::now();
+ auto timestamp =
+ std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch());
+
+ CLog::Log(UDPCLIENT_DEBUG_LEVEL, "UDPCLIENT RX: {}\t\t<- '{}'", timestamp.count(), message);
+
+ OnMessage(remoteAddress, message, reinterpret_cast<unsigned char*>(messageBuffer), messageLength);
+ }
+ else
+ {
+ CLog::Log(UDPCLIENT_DEBUG_LEVEL, "UDPCLIENT: Socket error {}", WSAGetLastError());
+ }
+
+ // is there any more data to read?
+ dataAvailable = 0;
+ ioctlsocket(client_socket, FIONREAD, &dataAvailable);
+ }
+
+ // dispatch a single command if any pending
+ while(DispatchNextCommand()) {}
+ }
+
+ closesocket(client_socket);
+
+ CLog::Log(UDPCLIENT_DEBUG_LEVEL, "UDPCLIENT: Stopped listening.");
+}
+
+
+bool CUdpClient::DispatchNextCommand()
+{
+ UdpCommand command;
+ {
+ std::unique_lock<CCriticalSection> lock(critical_section);
+
+ if (commands.size() <= 0)
+ return false;
+
+ COMMANDITERATOR it = commands.begin();
+ command = *it;
+ commands.erase(it);
+ }
+
+ int ret;
+ if (command.binarySize > 0)
+ {
+ // only perform the following if logging level at debug
+
+ auto now = std::chrono::steady_clock::now();
+ auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch());
+
+ CLog::Log(UDPCLIENT_DEBUG_LEVEL,
+ "UDPCLIENT TX: {}\t\t-> "
+ "<binary payload {} bytes>",
+ timestamp.count(), command.binarySize);
+
+ do
+ {
+ ret = sendto(client_socket, (const char*) command.binary, command.binarySize, 0, (struct sockaddr *) & command.address, sizeof(command.address));
+ }
+ while (ret == -1);
+
+ delete[] command.binary;
+ }
+ else
+ {
+ // only perform the following if logging level at debug
+ auto now = std::chrono::steady_clock::now();
+ auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch());
+
+ CLog::Log(UDPCLIENT_DEBUG_LEVEL, "UDPCLIENT TX: {}\t\t-> '{}'", timestamp.count(),
+ command.message);
+
+ do
+ {
+ ret = sendto(client_socket, command.message.c_str(), command.message.size(), 0, (struct sockaddr *) & command.address, sizeof(command.address));
+ }
+ while (ret == -1 && !m_bStop);
+ }
+ return true;
+}