.. _program_listing_file_src_tap_communication_tcp-server_tcp_server.cpp: Program Listing for File tcp_server.cpp ======================================= |exhale_lsh| :ref:`Return to documentation for file ` (``src/tap/communication/tcp-server/tcp_server.cpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp /* * Copyright (c) 2020-2021 Advanced Robotics at the University of Washington * * This file is part of Taproot. * * Taproot is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Taproot is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Taproot. If not, see . */ #ifdef PLATFORM_HOSTED #include "tcp_server.hpp" #ifdef __linux__ #include #include #include #include #include #include #include #endif // __linux__ #include #include #include #include "json_messages.hpp" using std::cerr; namespace tap { namespace communication { TCPServer::TCPServer(int targetPortNumber) #ifdef __linux__ : socketOpened(false), clientConnected(false), mainClientDescriptor(-1), serverAddress(), portNumber(-1) #endif // __linux__ { #ifdef __linux__ // Do sockety stuff. listenFileDescriptor = socket(AF_INET, SOCK_STREAM, 0); if (listenFileDescriptor < 0) { perror("TCPServer failed to open socket"); throw std::runtime_error("SocketError"); } int yes = 1; if (setsockopt(listenFileDescriptor, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes))) { perror("TCPSever failed to set socket options"); throw std::runtime_error("SocketOptionsError"); } socketOpened = true; serverAddress.sin_family = AF_INET; serverAddress.sin_port = htons(targetPortNumber); serverAddress.sin_addr.s_addr = INADDR_ANY; int n = bind( listenFileDescriptor, reinterpret_cast(&serverAddress), sizeof(serverAddress)); if (n < 0) { perror("TCPServer failed to bind socket"); // Good reference for why this error occurs and what it means: // https://hea-www.harvard.edu/~fine/Tech/addrinuse.html std::cerr << "If you're seeing this error it means that the TCPServer " "has failed to bind to a port too many times. If you're seeing this " "message, something is probably really messed up." << std::endl; throw std::runtime_error("BindError"); } portNumber = targetPortNumber; listen(listenFileDescriptor, LISTEN_QUEUE_SIZE); std::cout << "TCPServer initialized on port: " << targetPortNumber << std::endl; std::cout << "call getConnection() to accept client" << std::endl; #else UNUSED(targetPortNumber); #endif // __linux__ } TCPServer::~TCPServer() { #ifdef __linux__ close(listenFileDescriptor); close(mainClientDescriptor); #endif // __linux__ } TCPServer* TCPServer::MainServer() { #if defined(ENV_UNIT_TESTS) || !defined(__linux__) return nullptr; #else return &mainServer; #endif } void TCPServer::getConnection() { #ifdef __linux__ sockaddr_in clientAddress; socklen_t clientAddressLength = sizeof(clientAddress); mainClientDescriptor = accept( listenFileDescriptor, reinterpret_cast(&clientAddress), &clientAddressLength); cerr << "TCPServer: connection accepted" << std::endl; #endif // __linux__ } void TCPServer::closeConnection() { #ifdef __linux__ close(mainClientDescriptor); mainClientDescriptor = -1; std::cout << "TCPServer: closed connection with client, " "use getConnection() to connect to a new one"; #endif // __linux__ } uint16_t TCPServer::getPortNumber() { #ifdef __linux__ return this->portNumber; #else return 0; #endif // __linux__ } void TCPServer::writeToClient(const char* message, int32_t messageLength) { #ifdef __linux__ if (mainClientDescriptor < 0) { // Not necessarily an error if fileDescriptor still hasn't been opened // so we just don't write to anything and early return. cerr << "TCPServer: mainClientDescriptor not connected yet" << std::endl; return; } try { writeMessage(mainClientDescriptor, message, messageLength); } catch (std::runtime_error& e) { std::cerr << e.what() << std::endl; } #else UNUSED(message); UNUSED(messageLength); #endif // __linux__ } #ifdef __linux__ void readMessage(int16_t fileDescriptor, char* readBuffer, uint16_t messageLength) { readBuffer[messageLength] = '\0'; // Null terminate the message uint16_t bytesRead = read(fileDescriptor, readBuffer, messageLength); while (bytesRead < messageLength) { int32_t n = read(fileDescriptor, readBuffer + bytesRead, messageLength - bytesRead); if (n < 0) { if (errno == EAGAIN or errno == EINTR) { continue; } else { perror("TCPServer failed to read from client"); throw std::runtime_error("ReadingError"); } } bytesRead += n; } } void writeMessage(int16_t fileDescriptor, const char* message, uint16_t bytes) { uint32_t bytesWritten = 0; while (bytesWritten < bytes) { int32_t n = write(fileDescriptor, message + bytesWritten, bytes - bytesWritten); if (n < 0) { if (errno == EAGAIN or errno == EINTR) { continue; } else { perror("TCPServer failed to write"); throw std::runtime_error("WriteError"); } } bytesWritten += n; } } int32_t readInt32(int16_t fileDescriptor) { char buffer[5]; readMessage(fileDescriptor, buffer, 4); int32_t answer = 0; for (size_t i = 0; i < 4; i++) { answer = answer | buffer[i]; answer <<= 8; } return answer; } #endif // __linux__ // Only construct static singleton in actual sim, not in unit tests. #ifndef ENV_UNIT_TESTS // Definition of static variable. mainServer never created otherwise. TCPServer TCPServer::mainServer(2001); #endif } // namespace communication } // namespace tap #endif // PLATFORM_HOSTED