multithread-server/server2/server.cpp

388 lines
11 KiB
C++
Raw Normal View History

2024-12-12 04:09:30 +03:00
#include <iostream>
#include <ctime>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <cstring>
#include <pthread.h>
#include <arpa/inet.h>
#include <ncurses.h>
#include <regex>
#include <chrono>
#include <thread>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fstream>
#include <cstdio>
#include <termios.h>
#include <unistd.h>
2024-12-12 16:43:48 +03:00
#include <signal.h>
#include <sys/resource.h>
2024-12-12 04:09:30 +03:00
/* Configuration */
// Name of the pipe used to transfer logs to the log server
2024-12-12 23:29:58 +03:00
std::string LOG_FIFO_NAME = "./../tmp/log";
2024-12-12 04:09:30 +03:00
// Type of the server: server1 - last error & cursor position, server2 - mem % & vmem %
std::string SERVER_TYPE = "server2";
// Port to run server
int PORT = 9002;
// Maximum amount of socket connections
int MAX_CONNECTIONS = 128;
// Amount of log messages displayed on screen
const int CMD_LOG_BUFFER = 10;
// Amount of milliseconds between screen rerenders
int CMD_REFRESH_TIME = 500;
/* Current status to display */
// Buffer with recent log messages
std::string last_log_messages[CMD_LOG_BUFFER];
// Amount of active client sessions served by different threads
int active_connections = 0;
// Total amount of handled requests
int handndled_requests = 0;
2024-12-12 23:29:58 +03:00
// Log pipe file descriptor
int is_log_ok = false;
2024-12-12 04:09:30 +03:00
// Initializes ncurses lib
void init_ncurses() {
initscr();
cbreak();
noecho();
clear();
}
// Returns info about last error
std::string get_last_error() {
const int error_code = errno;
const char* error_description = std::strerror(errno);
return "Last error: " + std::to_string(error_code) + " - " + error_description + "\n";
}
// void disable_buffering() {
// struct termios t;
// tcgetattr(STDIN_FILENO, &t); // Get the terminal settings
// t.c_lflag &= ~ICANON; // Disable canonical mode (no buffering)
// t.c_lflag &= ~ECHO; // Disable echo (no terminal output)
// t.c_cc[VMIN] = 1; // Set the minimum number of characters to read (1 char)
// t.c_cc[VTIME] = 0; // Disable timeout
// tcsetattr(STDIN_FILENO, TCSANOW, &t); // Apply the new settings
// }
// void restore_buffering() {
// struct termios t;
// tcgetattr(STDIN_FILENO, &t); // Get the terminal settings
// t.c_lflag |= ICANON; // Enable canonical mode (buffering)
// t.c_lflag |= ECHO; // Enable echo
// tcsetattr(STDIN_FILENO, TCSANOW, &t); // Apply the restored settings
// }
// Returns info about current cursor position in console
std::string get_cursor_position() {
int x, y;
// // Disable buffering
// disable_buffering();
// printf("\033[6n"); // Request cursor position
// scanf("\033[%d;%dR", &x, &y); // Read cursor position
// // Print the cursor position
// printf("Cursor position: (%d, %d)\n", x, y);
// // Restore the terminal settings
// restore_buffering();
getyx(stdscr, y, x);
return "Cursor position: y: " + std::to_string(y) + " x: " + std::to_string(x) + "\n";
}
// Returns info about used physical memory
std::string get_mem_percent() {
std::ifstream meminfo("/proc/meminfo");
if(!meminfo) {
return "Error: failed to open /proc/meminfo\n";
}
std::string line;
long free_mem = -1, total_mem = -1;
while((std::getline(meminfo, line))) {
if(line.find("MemAvailable:") == 0) {
std::sscanf(line.c_str(), "MemAvailable: %ld kB", &free_mem);
} else if(line.find("MemTotal:") == 0) {
std::sscanf(line.c_str(), "MemTotal: %ld kB", &total_mem);
}
if(free_mem != -1 && total_mem != -1) {
break;
}
}
meminfo.close();
if(total_mem == -1 || free_mem == -1) {
return "Error: failed to read info from /proc/meminfo\n";
}
return "Physical memory: " + std::to_string(100 * (total_mem - free_mem)/total_mem) + "%\n";
}
2024-12-12 16:43:48 +03:00
std::string get_vmem() {
std::ifstream meminfo("/proc/self/status");
if(!meminfo) {
return "Error: failed to open /proc/meminfo\n";
}
std::string line;
long not_free_mem = -1, total_mem = -1;
while((std::getline(meminfo, line))) {
if(line.find("VmRSS:") == 0) {
std::sscanf(line.c_str(), "VmRSS: %ld kB", &not_free_mem);
} else if(line.find("VmSize:") == 0) {
std::sscanf(line.c_str(), "VmSize: %ld kB", &total_mem);
}
if(not_free_mem != -1 && total_mem != -1) {
break;
}
}
meminfo.close();
if(total_mem == -1 || not_free_mem == -1) {
return "Error: failed to read info from /proc/meminfo\n";
}
return "Virtual memory of the proccess: " + std::to_string(100.0 * not_free_mem/total_mem) + "%\n";
}
2024-12-12 04:09:30 +03:00
// Returns message to send according to the server type
std::string get_sysinfo() {
if (SERVER_TYPE == "server1") {
return '\t' + get_last_error() + "\t\t\t" + get_cursor_position();
}
2024-12-12 16:43:48 +03:00
return '\t' + get_mem_percent() + "\t\t\t" + get_vmem(); // TODO: vmem
2024-12-12 04:09:30 +03:00
}
// Writes log message on screen and passes it to the logging pipe
void log(std::string msg) {
// Add log message to the screen buffer
for (int i = CMD_LOG_BUFFER - 1; i > 0; i--) {
last_log_messages[i] = last_log_messages[i-1];
}
time_t now = time(0);
struct tm tstruct;
tstruct = *localtime(&now);
char timestamp[32];
std::strftime(timestamp, 32, "%H:%M:%S ", &tstruct);
last_log_messages[0] = timestamp + std::string(" ") + msg;
2024-12-12 23:29:58 +03:00
// Write message to the logging pipe
int log_fd = open(LOG_FIFO_NAME.c_str(), O_WRONLY);
if(log_fd != -1) {
is_log_ok = true;
write(log_fd, msg.c_str(), msg.length());
close(log_fd);
} else {
is_log_ok = false;
}
2024-12-12 04:09:30 +03:00
}
2024-12-12 23:29:58 +03:00
// // Opens file descriptor for logging pipe TODO
// void init_log_pipe() {
// log_fd = open(LOG_FIFO_NAME.c_str(), O_WRONLY);
// }
2024-12-12 04:09:30 +03:00
// Returns file descriptor for server socket
int start_socket() {
int server_socket = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in server_address;
server_address.sin_family = AF_INET;
server_address.sin_port = htons(PORT);
server_address.sin_addr.s_addr = INADDR_ANY;
bind(server_socket, (struct sockaddr*)&server_address, sizeof(server_address));
if (errno == 98) {
log("Error: " + std::string(std::strerror(errno)));
return -1;
}
int status = listen(server_socket, MAX_CONNECTIONS);
if (status != 0) {
log("Error: can't listen with socket");
return -1;
}
return server_socket;
}
// Handles one connection in sepparate thread
void* handle_connection(void* arg) {
// Cast client socket descriptor
int client_socket = *(int*)arg;
delete (int*)arg;
std::string info = "";
2024-12-12 23:29:58 +03:00
log("New thread created for socket " + std::to_string(client_socket) + '\0');
2024-12-12 04:09:30 +03:00
while (true) {
// Recieve message from client
char buffer[5];
ssize_t bytes_recieved = recv(client_socket, buffer, sizeof(buffer), 0);
// If connection closed
if (bytes_recieved == 0) {
log("Connection with client " + std::to_string(client_socket) + " closed");
active_connections--;
break;
}
if (bytes_recieved == -1) {
2024-12-12 23:29:58 +03:00
log("Error while recieving message: " + std::to_string(errno) + " - " + std::strerror(errno) + '\0');
2024-12-12 04:09:30 +03:00
break;
}
2024-12-12 23:29:58 +03:00
log("Got new message from client " + std::to_string(client_socket) + '\0');
2024-12-12 04:09:30 +03:00
std::string new_info = get_sysinfo();
if (info != new_info) {
info = new_info;
time_t now = time(0);
struct tm tstruct;
tstruct = *localtime(&now);
char timestamp[32];
std::strftime(timestamp, 32, "%Y/%m/%d %H:%M:%S ", &tstruct);
std::string msg = timestamp + info;
send(client_socket, msg.c_str(), strlen(msg.c_str()) + 1, 0);
2024-12-12 23:29:58 +03:00
log("Sent new info to client\0");
2024-12-12 16:43:48 +03:00
handndled_requests++;
2024-12-12 04:09:30 +03:00
} else {
2024-12-12 23:29:58 +03:00
log("Actual info already on client\0");
2024-12-12 04:09:30 +03:00
}
}
close(client_socket);
return nullptr;
}
// Refreshes console output
void* draw(void* arg) {
while (true) {
// Update info
clear();
2024-12-12 16:43:48 +03:00
printw("Use q + Enter to quit\n\n");
2024-12-12 04:09:30 +03:00
// Print configuration
printw(
"LOG_FIFO_NAME=%s\nSERVER_TYPE=%s\nPORT=%i\nMAX_CONNECTIONS=%i\nCMD_LOG_BUFFER=%i\nCMD_REFRESH_TIME=%i\n\n",
LOG_FIFO_NAME.c_str(), SERVER_TYPE.c_str(), PORT, MAX_CONNECTIONS, CMD_LOG_BUFFER, CMD_REFRESH_TIME
);
2024-12-12 23:29:58 +03:00
if (is_log_ok) {
printw("Log server connected\n\n");
} else {
printw("Log server is not connected\n\n");
}
2024-12-12 04:09:30 +03:00
// Print statistics
2024-12-12 16:43:48 +03:00
printw("Active connections: %i\nHandled messages: %i\n", active_connections, handndled_requests);
2024-12-12 04:09:30 +03:00
// Print logs
for (int i = CMD_LOG_BUFFER-1; i >= 0; i--) {
printw("\n%s", last_log_messages[i].c_str());
}
refresh();
// Wait to update again
std::this_thread::sleep_for(std::chrono::milliseconds(CMD_REFRESH_TIME));
}
}
2024-12-12 16:43:48 +03:00
// Read commands from screen
void* commands(void* arg) {
// Cast server socket descriptor
int server_socket = *(int*)arg;
delete (int*)arg;
while (true) {
std::string command;
std::cin >> command;
if (command == "quit" || command == "exit" || command == "q") {
endwin();
close(server_socket);
kill(0, 15);
}
}
}
2024-12-12 04:09:30 +03:00
int main() {
init_ncurses();
// Create thread to redraw console output
pthread_t tid;
if (pthread_create(&tid, nullptr, &draw, nullptr) != 0) {
std::cout << ("Failed to create drawing thread") << std::endl;
2024-12-12 16:43:48 +03:00
endwin();
2024-12-12 04:09:30 +03:00
return 2;
}
pthread_detach(tid);
2024-12-12 16:43:48 +03:00
2024-12-12 04:09:30 +03:00
int server_socket = start_socket();
2024-12-12 23:29:58 +03:00
log("Socket created\0");
2024-12-12 16:43:48 +03:00
// Create thread to read commands
pthread_t tcid;
int* server_socket_copy = new int(server_socket);
if (pthread_create(&tcid, nullptr, &commands, server_socket_copy) != 0) {
std::cout << ("Failed to create commands thread") << std::endl;
endwin();
return 2;
}
pthread_detach(tcid);
2024-12-12 04:09:30 +03:00
while (true) {
int client_socket = accept(server_socket, nullptr, nullptr);
if (client_socket == -1) {
continue;
}
active_connections++;
// Create thread for each connection
int* client_socket_copy = new int(client_socket);
pthread_t tid;
if (pthread_create(&tid, nullptr, &handle_connection, client_socket_copy) != 0) {
delete client_socket_copy;
}
pthread_detach(tid);
}
return 0;
}