From bdc24594a4916dfc8eb51e5d5ef55aa711a2bb94 Mon Sep 17 00:00:00 2001 From: iherman- <200969603+iherman-p@users.noreply.github.com> Date: Sat, 9 May 2026 21:52:16 +0200 Subject: [PATCH] Added functional multiple client handling --- Makefile | 4 +- Server/Server.cpp | 188 ++++++++++++++++++++++++++++++++++++++++------ Server/Server.hpp | 38 +++++----- User/User.cpp | 66 ++++++++++------ User/User.hpp | 1 + 5 files changed, 229 insertions(+), 68 deletions(-) diff --git a/Makefile b/Makefile index fecc47a..71b7055 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ NAME = ircserv -SRC = main.cpp Server/Server.cpp Client/Client.cpp +SRC = main.cpp Server/Server.cpp User/User.cpp -HEADERS = Server/Server.hpp +HEADERS = Server/Server.hpp User/User.hpp OBJ = $(SRC:.cpp=.o) diff --git a/Server/Server.cpp b/Server/Server.cpp index 9f982cb..4609f3e 100644 --- a/Server/Server.cpp +++ b/Server/Server.cpp @@ -6,19 +6,66 @@ /* By: iherman- +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#include + const int Server::kConnectionQueueLimit = 10; Server::Server() : port_(PORT_DEFAULT), - password_("") + password_("password") { - // std::cout << "Hello from server" << std::endl; + serverSocket_ = socket(AF_INET, SOCK_STREAM, 0); + if (serverSocket_ < 0) + throw std::runtime_error("Failed to create socket"); + + struct sockaddr_in addr; + std::memset(&addr, 0, sizeof(addr)); + + addr.sin_family = AF_INET; + addr.sin_port = htons(port_); + addr.sin_addr.s_addr = INADDR_ANY; + + if (bind(serverSocket_, (struct sockaddr*)&addr, sizeof(addr))) + throw std::runtime_error("Failed to bind"); + + if (listen(serverSocket_, kConnectionQueueLimit)) + throw std::runtime_error("Failed to listen"); + + std::cout << "Server port: " << port_ + << "\nServer Password: " << password_ + << std::endl; +} + +Server::Server(const Server& other) +{ + port_ = other.port_; + serverSocket_ = other.serverSocket_; + password_ = other.password_; + sockets_ = other.sockets_; } Server::Server(int port, const std::string& password) : @@ -55,6 +102,9 @@ Server::Server(int port, const std::string& password) : Server::~Server() { + for (std::map::iterator it = clients_.begin(); it != clients_.end(); ++it) + close(it->first); + close(serverSocket_); } @@ -65,35 +115,129 @@ Server &Server::operator=(const Server& other) port_ = other.port_; serverSocket_ = other.serverSocket_; password_ = other.password_; + sockets_ = other.sockets_; } return *this; } -void Server::run() +bool Server::handleClient(User& client) { const std::size_t kBufferSize = 1024; char buffer[kBufferSize] = {0}; - struct sockaddr_in client_addr; - socklen_t client_addr_size = sizeof(client_addr); - int clientSocket = accept(serverSocket_, (struct sockaddr*)&client_addr, &client_addr_size); - - std::string message; - while (true) - { - int recv_amount = recv(clientSocket, buffer, kBufferSize, 0); + int recv_amount = recv(client.getFd(), buffer, kBufferSize, 0); - if (recv_amount <= 0) - break ; + if (recv_amount == 0) + return true; - message.append(buffer, recv_amount); - - if (message.find('\n') != std::string::npos) - break ; + if (recv_amount < 0) + { + if (errno == EAGAIN || errno == EWOULDBLOCK) + return false; + return true; } - message = "Server received: " + message; - send(clientSocket, message.c_str(), message.size(), 0); - close(clientSocket); -} \ No newline at end of file + client.appendBuffer(std::string(buffer, recv_amount)); + + if (client.getBuffer().find('\n') != std::string::npos) + { + std::string message = "Server received: " + client.getBuffer(); + send(client.getFd(), message.c_str(), message.size(), 0); + client.clearBuffer(); + } + + return false; +} + +static struct pollfd newPollfd(int fd) +{ + pollfd pfd; + + pfd.fd = fd; + pfd.events = POLLIN; + pfd.revents = 0; + return pfd; +} + +void Server::addClient() +{ + int newClientSocket = accept(serverSocket_, NULL, NULL); + + if (newClientSocket == -1) + { + // Not sure if program should terminate + std::cerr << "Problem occured accepting client" << std::endl; + return ; + } + + fcntl(newClientSocket, F_SETFL, O_NONBLOCK); + sockets_.push_back(newPollfd(newClientSocket)); + clients_[newClientSocket] = User(newClientSocket); + std::cout << "Client with fd: " << newClientSocket << " connected" << std::endl; +} + +void Server::run() +{ + // add serverSocket_ to sockets_ + sockets_.push_back(newPollfd(serverSocket_)); + + while (true) + { + int ret = poll(sockets_.data(), sockets_.size(), -1); + + if (ret <= 0) + { + // not sure if program should terminate + std::cerr << "Poll failed" << std::endl; + continue ; + } + + for (std::vector::iterator it = sockets_.begin(); it != sockets_.end(); ) + { + if (it->revents & (POLLERR | POLLHUP | POLLNVAL)) + { + // remove client + } + + if (!(it->revents & POLLIN)) + { + it++; + continue ; + } + + std::cout << "Checking client with fd: " << it->fd << std::endl; + + // check if listening port is open, if so add client + if (it->fd == serverSocket_) + { + addClient(); + it++; + break ; + } + + // call handleClient for each other socket + std::map::iterator clientIt = clients_.find(it->fd); + if (clientIt == clients_.end()) + { + std::cerr << "Unknown fd in poll list: " << it->fd << std::endl; + close(it->fd); + it = sockets_.erase(it); + continue; + } + + bool disconnected = handleClient(clientIt->second); + + if (disconnected) + { + close(it->fd); + std::cout << "Client with fd: " << it->fd << " disconnected " << std::endl; + + clients_.erase(it->fd); + it = sockets_.erase(it); + } + else + it++; + } + } +} diff --git a/Server/Server.hpp b/Server/Server.hpp index 05760c6..2b9000b 100644 --- a/Server/Server.hpp +++ b/Server/Server.hpp @@ -6,7 +6,7 @@ /* By: iherman- -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include // C++ lib functions # include @@ -38,25 +22,39 @@ # include # include +# include +# include + +# include "../User/User.hpp" + # define PORT_DEFAULT 9898 class Server { private: static const int kConnectionQueueLimit; - + int port_; int serverSocket_; std::string password_; + std::vector sockets_; + std::map clients_; + + bool handleClient(User& client); + + void addClient(); + std::vector::iterator removeClient(std::vector::iterator client); + + Server(const Server& other); + Server &operator=(const Server& other); + public: Server(); Server(int port, const std::string& password); ~Server(); - Server &operator=(const Server& other); - void run(); }; diff --git a/User/User.cpp b/User/User.cpp index c9c2049..4dc572b 100644 --- a/User/User.cpp +++ b/User/User.cpp @@ -16,25 +16,42 @@ // Constructors // ////////////////// -User::User() +User::User() : + fd(-1), + authenticated(false), + registered(false) { - // std::cout << "User default constructor called" << std::endl; + // std::cout << "User default constructor called" << std::endl; +} + +User::User(int fd) : + fd(fd), + authenticated(false), + registered(false) +{ + // std::cout << "User default constructor called" << std::endl; } User::User(const User &other) { - *this = other; - // std::cout << "User copy constructor called" << std::endl; + *this = other; + // std::cout << "User copy constructor called" << std::endl; } User& User::operator=(const User &other) { - if (this != &other) - { - // Copy attributes here - } - // std::cout << "User copy assignment operator called" << std::endl; - return (*this); + if (this != &other) + { + fd = other.fd; + nick = other.nick; + username = other.username; + realname = other.realname; + buffer = other.buffer; + authenticated = other.authenticated; + registered = other.registered; + } + // std::cout << "User copy assignment operator called" << std::endl; + return (*this); } User::~User() @@ -44,22 +61,23 @@ User::~User() // Getters -int User::getFd() const { return (this->fd) }; -std::string User::getNick() const { return (this->nick) }; -std::string User::getUsername() const { return (this->username) }; -std::string User::getRealname() const { return (this->realname) }; -std::string &User::getBuffer() { return (this->buffer) }; -bool User::isAuthenticated() const { return (this->authenticated) }; -bool User::isRegistered() const { return (this->registered) }; +int User::getFd() const { return (this->fd); } +std::string User::getNick() const { return (this->nick); } +std::string User::getUsername() const { return (this->username); } +std::string User::getRealname() const { return (this->realname); } +std::string &User::getBuffer() { return (this->buffer); } +bool User::isAuthenticated() const { return (this->authenticated); } +bool User::isRegistered() const { return (this->registered); } // Setters -void setNick(std::string nick) { this->nick = nick; } -void setUsername(std::string username) { this->username = username; } -void setRealname(std::string realname) { this->realname = realname; } -void appendBuffer(std::string buff) { this->buffer = buff; } -void clearBuffer() { this->buffer.clear(); } +void User::appendBuffer(std::string buff) { this->buffer.append(buff); } +void User::clearBuffer() { this->buffer.clear(); } -void setAuthenticated(bool value) { this->authenticated = value; } -void setRegistered(bool value) { this->registered = value; } \ No newline at end of file +void User::setNick(std::string nick) { this->nick = nick; } +void User::setUsername(std::string username) { this->username = username; } +void User::setRealname(std::string realname) { this->realname = realname; } + +void User::setAuthenticated(bool value) { this->authenticated = value; } +void User::setRegistered(bool value) { this->registered = value; } \ No newline at end of file diff --git a/User/User.hpp b/User/User.hpp index da62791..0b6d3c6 100644 --- a/User/User.hpp +++ b/User/User.hpp @@ -29,6 +29,7 @@ class User public: User(); + User(int fd); User(const User &other); User& operator=(const User &other); ~User();