/* Copyright (C) 2022 The Qt Company Ltd. * * SPDX-License-Identifier: GPL-3.0-only WITH Qt-GPL-exception-1.0 */ #include "tcpserver.h" namespace QLicenseService { TcpServer::TcpServer(uint16_t serverPort) : m_serverPort(serverPort) { } TcpServer::~TcpServer() { doCloseSocket(m_masterSocket); for (type_socket sd: m_clientSocket) { doCloseSocket(sd); } m_clientSocket.clear(); } bool TcpServer::init() { int opt = TRUE; #ifdef _WIN32 // Initialize Winsock WSADATA wsaData; int iResult = 0; iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); if (iResult != NO_ERROR) { wprintf(L"Error at WSAStartup()\n"); return false; } #endif // Create a master socket if ((m_masterSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == 0) { perror("socket failed"); return false; } // Set master socket to allow multiple connections if (setsockopt(m_masterSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0) { perror("setsockopt failed"); return false; } timeval tv {1, 0}; //t.tv_sec = 2; t.tv_usec = 0; int ret = setsockopt(m_masterSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(timeval)); if (ret < 0) { printf("Socket timeout error code: %d\n", ret); perror("Socket timeout failed"); } // Type of socket created m_address.sin_family = AF_INET; m_address.sin_addr.s_addr = INADDR_ANY; m_address.sin_port = htons(m_serverPort); // Bind the socket to localhost if (bind(m_masterSocket, (struct sockaddr *)&m_address, sizeof(m_address)) < 0) { doCloseSocket(m_masterSocket); perror("bind failed"); return false; } std::cout << "Listening to port " << m_serverPort << std::endl; // Try to specify maximum of 3 pending connections for the master socket if (listen(m_masterSocket, 3) < 0) { doCloseSocket(m_masterSocket); perror("listen"); return false; } m_addrlen = sizeof(m_address); // Accept the incoming connection std::cout << "Waiting for connections ...\n"; return true; } bool TcpServer::listenToClients(uint16_t &socketId, std::string *data) { // Clear the socket set FD_ZERO(&m_readfds); type_socket max_sd = m_masterSocket; // Add master socket to set FD_SET(m_masterSocket, &m_readfds); // Add child sockets to set for (type_socket sd : m_clientSocket) { // If valid socket descriptor then add to read list if (sd > 0) FD_SET(sd, &m_readfds); // Highest file descriptor number, need it for the select function if (sd > max_sd) max_sd = sd; } // timeout timeval tv{1, 0}; //t.tv_sec = 2; t.tv_usec = 0; // Wait for an activity on one of the sockets. int activity = select((int)max_sd + 1, &m_readfds, NULL, NULL, &tv); if ((activity < 0) && (errno != EINTR)) { std::cout << "select error \n"; } // If anything happens on the master socket, its an incoming connection if (FD_ISSET(m_masterSocket, &m_readfds)) { type_socket new_socket; if ((new_socket = accept(m_masterSocket, (struct sockaddr *)&m_address, (socklen_t *)&m_addrlen)) < 0) { perror("accept"); return false; } // Add new socket to set of sockets m_clientSocket.insert(new_socket); std::cout << "New connection - added to list of sockets with id " << new_socket << std::endl; } // Else its some IO operation on some other socket for (type_socket sd : m_clientSocket) { if (FD_ISSET(sd, &m_readfds)) { // Check if it was for closing , and also read the // incoming message #ifdef _WIN32 int valread = recv(sd, m_buffer, sizeof(m_buffer), 0); #else int valread = read(sd, m_buffer, sizeof(m_buffer)); #endif if (valread == 0) { // Somebody disconnected, get his details getpeername(sd, (struct sockaddr *)&m_address, (socklen_t *)&m_addrlen); socketId = sd; *data = SOCKET_CLOSED_MSG; } else { // Set the string terminating NULL byte // on the end of the data read m_buffer[valread] = '\0'; socketId = sd; *data = m_buffer; } } } return true; } int TcpServer::sendResponse(int socketId, const std::string &message) { if (send((type_socket)socketId, message.c_str(), (int)message.length(), 0) != message.length()) { printf("[TcpServer] Send failed\n"); return 1; } return 0; } void TcpServer::closeClientSocket(int socketId) { std::cout << "Closing socket " << socketId << std::endl; doCloseSocket((type_socket &)socketId); m_clientSocket.erase(socketId); } void TcpServer::doCloseSocket(type_socket &socketFD) { #if __APPLE__ || __MACH__ || __linux__ close(socketFD); #else closesocket(socketFD); #endif } } // namespace QLicenseService