.. _program_listing_file_gui_Network_Network.cpp: Program Listing for File Network.cpp ==================================== |exhale_lsh| :ref:`Return to documentation for file ` (``gui/Network/Network.cpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #include "Network.hpp" #include "logging/Logger.hpp" #include #include #include constexpr int FD_PIPE_EXIT = 0; constexpr int FD_SERVER_IN = 1; constexpr int FD_PIPE_NETWORK = 2; constexpr int FD_EXIT_IN = 0; constexpr int FD_EXIT_OUT = 1; constexpr int FD_SERVER_OUT = 0; Network::Network(int port, std::string hostname, std::shared_ptr &data) : _port(port), _hostname(std::move(hostname)), _api(data) { // Create server socket _fdServer = socket(AF_INET, SOCK_STREAM, 0); if (_fdServer == -1) throw std:: runtime_error("Error: socket, Function: Network, File: Network.cpp"); // Set socket options memset(&serverAddr, 0, sizeof(serverAddr)); serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(_port); socklen_t sockLen = sizeof(serverAddr); // Convert hostname to IP address if (inet_pton(AF_INET, _hostname.c_str(), &serverAddr.sin_addr) != 1) throw std::runtime_error( "Error: inet_pton failed. Function: RunNetwork, File: Network.cpp"); // Try to connect to the server in a loop until successful or unrecoverable // error while (true) { if (connect(_fdServer, (const sockaddr *)(&serverAddr), sockLen) == 0) break; // Connected successfully // Check for recoverable errors if (errno == EINTR || errno == ECONNREFUSED || errno == ETIMEDOUT) { std::this_thread::sleep_for(std::chrono::milliseconds(500)); continue; } // Unrecoverable error throw std::runtime_error( std::string( "Error: connect failed. Function: Network, File: " "Network.cpp: (") + strerror(errno) + ")."); } // Create pipe for thread exit if (pipe(_pipeFdExit.data()) == -1) throw std::runtime_error( "Error: pipe failed. Function: Network, File: Network.cpp"); // Initialize poll structures _pollInFd[FD_PIPE_EXIT].fd = _pipeFdExit[FD_EXIT_IN]; _pollInFd[FD_SERVER_IN].fd = _fdServer; _pollInFd[FD_PIPE_NETWORK].fd = _api->GetInFd(); for (pollfd &pollFd: _pollInFd) { pollFd.events = POLLIN | POLLHUP; // monitor for input and hangup pollFd.revents = 0; } // Initialize pollOutFd for sending messages _pollOutFd[FD_SERVER_OUT].events = POLLOUT; _pollOutFd[FD_SERVER_OUT].fd = _fdServer; _pollOutFd[FD_SERVER_OUT].revents = 0; } Network::~Network() { _networkThread.join(); // wait for the network thread to finish shutdown(_fdServer, SHUT_RDWR); // shut both end read and write read(_fdServer, nullptr, 0); // clear buffer close(_fdServer); // close socket close(_pipeFdExit[FD_EXIT_IN]); // close the read end of the pipe close(_pipeFdExit[FD_EXIT_OUT]); // close the write end of the pipe } void Network::ServerHandshake() { if (poll(_pollInFd.data(), _pollInFd.size(), -1) == -1) throw std::runtime_error( "Error: poll failed. Function: RunNetwork, File: Network.cpp"); if (_pollInFd[FD_SERVER_IN].revents & POLLIN) Log::inf << "Message received: " << Log::cleanString(ReceiveMessage()); SendMessage("GRAPHIC\n"); } void Network::Run() { if (!_networkThread.joinable()) _networkThread = std::thread(&Network::RunNetworkInternal, this); } void Network::RunNetworkInternal() { ServerHandshake(); bool isRunning = true; while (isRunning) { if (poll(_pollInFd.data(), _pollInFd.size(), -1) == -1) throw std::runtime_error( "Error: poll failed. Function: RunNetwork, File: Network.cpp"); // server disconnected if (_pollInFd[FD_SERVER_IN].revents & POLLHUP) { Log::failed << "Server disconnected."; isRunning = false; continue; } // exit event if (_pollInFd[FD_PIPE_EXIT].revents & POLLIN) { std::array buffer; if (read(_pollInFd[FD_PIPE_EXIT].fd, buffer.data(), buffer.size()) == -1) throw std::runtime_error( "Error: read failed. Function: RunNetwork, File: Network.cpp"); isRunning = false; Log::inf << "End of network thread requested."; } // server message if (_pollInFd[FD_SERVER_IN].revents & POLLIN) { std::string message = ReceiveMessage(); try { _api->ParseManageCommand(message); } catch (const std::exception &e) { Log::failed << "Exception in ParseManageCommande: " << e.what(); } } // network pipe message if (_pollInFd[FD_PIPE_NETWORK].revents & POLLIN) { static std::string leftover; std::string message; std::array buffer; ssize_t bytesRead = read( _api->GetInFd(), buffer.data(), buffer.size() - 1); if (bytesRead < 0) throw std::runtime_error( "Error: read failed. Function: RunNetwork, File: Network.cpp"); // all messages are separated by '\n' buffer[bytesRead] = '\0'; std::stringstream ss(leftover + std::string(buffer.data(), bytesRead)); while (std::getline(ss, message, '\n')) if (!message.empty()) { // Log::inf // << "Message received from pipe: " << Log::cleanString(message); SendMessage(message + "\n"); } // if there is still data in the stream, save it for the next iteration // (e.g., if the last message did not end with '\n') leftover.clear(); if (!ss.eof()) leftover = ss.str().substr(ss.tellg()); } } } void Network::SendMessage(const std::string &msg) { if (poll(_pollOutFd.data(), _pollOutFd.size(), -1) == -1) throw std::runtime_error( "Error: poll failed, Function: SendMessage, File: Network.cpp"); // Check if the socket is ready for writing if (!(_pollOutFd[FD_SERVER_OUT].revents & POLLOUT)) throw std::runtime_error( "Error: socket not ready for writing, Function: SendMessage, File: " "Network.cpp"); if (send(_fdServer, msg.c_str(), msg.size(), 0) == -1) throw std:: runtime_error("Error: send, Function: SendMessage, File: Network.cpp"); // Log::inf << "Message sent : " << Log::cleanString(msg); } std::string Network::ReceiveMessage() const { constexpr std::size_t chunk_size = 1024; std::vector buffer; while (true) { std::vector chunk(chunk_size); ssize_t bytes_received = recv(_fdServer, chunk.data(), chunk.size(), 0); if (bytes_received < 0) { if (errno == EINTR) continue; throw std::runtime_error( "Error: recv failed, Function: ReceiveMessage, File: Network.cpp"); } if (bytes_received == 0) break; buffer.insert(buffer.end(), chunk.begin(), chunk.begin() + bytes_received); if ((size_t)(bytes_received) < chunk_size) break; } return std::string(buffer.begin(), buffer.end()); } void Network::RequestStop() { Log::inf << "Requesting stop of network thread.\n"; write(_pipeFdExit[FD_EXIT_OUT], "x", 1); }