I broke something with displaying the clients input plainly

This commit is contained in:
ouafabulous 2025-05-21 21:52:20 +02:00
parent b2c5e0f5d7
commit bfe88daf3e
10 changed files with 292 additions and 186 deletions

View file

@ -1,14 +1,14 @@
# **************************************************************************** # #******************************************************************************#
# # # #
# ::: :::::::: # # ::: :::::::: #
# Makefile :+: :+: :+: # # Makefile :+: :+: :+: #
# +:+ +:+ +:+ # # +:+ +:+ +:+ #
# By: sben-tay <sben-tay@student.42.fr> +#+ +:+ +#+ # # By: omoudni <omoudni@student.42paris.fr> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ # # +#+#+#+#+#+ +#+ #
# Created: 2025/05/02 15:40:00 by rparodi #+# #+# # # Created: 2025/05/02 15:40:00 by rparodi #+# #+# #
# Updated: 2025/05/21 13:01:09 by rparodi ### ########.fr # # Updated: 2025/05/21 21:51:13 by omoudni ### ########.fr #
# # # #
# **************************************************************************** # #******************************************************************************#
# Name # Name
@ -30,7 +30,7 @@ SRC = sources/core/logs.cpp \
sources/core/parser.cpp \ sources/core/parser.cpp \
sources/core/main.cpp \ sources/core/main.cpp \
sources/core/Server.cpp \ sources/core/Server.cpp \
sources/user/user.cpp \ sources/core/user.cpp \
sources/channel/channel.cpp sources/channel/channel.cpp
INC_DIR = include/core \ INC_DIR = include/core \
@ -85,30 +85,37 @@ debug: CXXFLAGS += -g
# debug: CXXFLAGS += -fsanitize=address # debug: CXXFLAGS += -fsanitize=address
debug: re debug: re
PORT ?= 4243
test: debug test: debug
@printf '$(GREY) now running with\n\t- Port:\t\t$(GREEN)4243$(GREY)\n\t- Password:\t$(GREEN)irc$(END)\n' @printf '$(GREY) now running with\n\t- Port:\t\t$(GREEN)$(PORT)$(GREY)\n\t- Password:\t$(GREEN)irc$(END)\n'
@if tmux has-session -t $(SESSION) 2>/dev/null; then \ @if tmux has-session -t $(SESSION) 2>/dev/null; then \
tmux kill-session -t $(SESSION); \ tmux kill-session -t $(SESSION); \
fi fi
@tmux new-session -d -s $(SESSION) \ @tmux new-session -d -s $(SESSION) \
'bash -lc "./$(NAME) 4243 irc; exec bash"' 'bash -lc "./$(NAME) $(PORT) irc; exec bash"'
@tmux split-window -h -p 70 -t $(SESSION):0 \ @tmux split-window -h -p 70 -t $(SESSION):0 \
'bash -lc "irssi -c localhost -p 4243 -w irc || exec yes \"irssi exit code: $?\""' 'bash -lc "irssi -c localhost -p $(PORT) -w irc || exec yes \"irssi exit code: $?\""'
@tmux split-window -v -p 50 -t $(SESSION):0.1 \ @tmux split-window -v -p 50 -t $(SESSION):0.1 \
'bash -lc "nc localhost 4243 || exec yes \"netcat exit code: $?\""' 'bash -lc "nc localhost $(PORT) || exec yes \"netcat exit code: $?\""'
@tmux split-window -v -p 50 -t $(SESSION):0.2 \
'bash -lc "nc localhost $(PORT) || exec yes \"netcat exit code: $?\""'
@tmux split-window -v -p 50 -t $(SESSION):0.3 \
'bash -lc "nc localhost $(PORT) || exec yes \"netcat exit code: $?\""'
@tmux attach -t $(SESSION) @tmux attach -t $(SESSION)
run: re run: re
@printf '$(GREY) now running with\n\t- Port:\t\t$(GREEN)4243$(GREY)\n\t- Password:\t$(GREEN)irc$(END)\n' @printf '$(GREY) now running with\n\t- Port:\t\t$(GREEN)$(PORT)$(GREY)\n\t- Password:\t$(GREEN)irc$(END)\n'
@if tmux has-session -t $(SESSION) 2>/dev/null; then \ @if tmux has-session -t $(SESSION) 2>/dev/null; then \
tmux kill-session -t $(SESSION); \ tmux kill-session -t $(SESSION); \
fi fi
@tmux new-session -d -s $(SESSION) \ @tmux new-session -d -s $(SESSION) \
'bash -lc "./$(NAME) 4243 irc; exec bash"' 'bash -lc "./$(NAME) $(PORT) irc; exec bash"'
@tmux split-window -h -p 70 -t $(SESSION):0 \ @tmux split-window -h -p 70 -t $(SESSION):0 \
'bash -lc "irssi -c localhost -p 4243 -w irc || exec yes \"irssi exit code: $?\""' 'bash -lc "irssi -c localhost -p $(PORT) -w irc || exec yes \"irssi exit code: $?\""'
@tmux split-window -v -p 50 -t $(SESSION):0.1 \ @tmux split-window -v -p 50 -t $(SESSION):0.1 \
'bash -lc "nc localhost 4243 || exec yes \"netcat exit code: $?\""' 'bash -lc "nc localhost $(PORT) || exec yes \"netcat exit code: $?\""'
@tmux attach -t $(SESSION) @tmux attach -t $(SESSION)
# Header # Header

View file

@ -13,6 +13,7 @@ class Server {
- _password : string - _password : string
- _server_fd : int - _server_fd : int
- _poll : PollManager - _poll : PollManager
- _users : map<int, User>
+ Server(port : int, password : string) + Server(port : int, password : string)
+ start() : void + start() : void
@ -25,12 +26,11 @@ class Server {
' ============================ ' ============================
class PollManager { class PollManager {
- _fds : vector<pollfd> - _fds : vector<pollfd>
- _users : map<int, User>
+ addUser(fd : int) : void + addUser(fd : int) : void
+ removeUser(fd : int) : void + removeUser(fd : int) : void
+ updateUser(fd : int, events : short) : void + updateUser(fd : int, events : short) : void
+ pollLoop(server_fd : int) : void + pollLoop(server_fd : int, newClients : std::vector<int>, disconnected : std::vector<int>) : void
+ readFromUser(fd : int) : void + readFromUser(fd : int) : void
+ writeToUser(fd : int) : void + writeToUser(fd : int) : void
} }

View file

@ -1,14 +1,14 @@
/* ************************************************************************** */ /******************************************************************************/
/* */ /* */
/* ::: :::::::: */ /* ::: :::::::: */
/* PollManager.hpp :+: :+: :+: */ /* PollManager.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */ /* +:+ +:+ +:+ */
/* By: sben-tay <sben-tay@student.42.fr> +#+ +:+ +#+ */ /* By: omoudni <omoudni@student.42paris.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2025/05/19 19:15:13 by omoudni #+# #+# */ /* Created: 2025/05/19 19:15:13 by omoudni #+# #+# */
/* Updated: 2025/05/20 17:22:59 by sben-tay ### ########.fr */ /* Updated: 2025/05/21 21:34:20 by omoudni ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /******************************************************************************/
#pragma once #pragma once
@ -16,19 +16,17 @@
#include <vector> #include <vector>
#include <string> #include <string>
class PollManager { class PollManager
{
public: public:
PollManager(); PollManager();
~PollManager(); ~PollManager();
void addClient(int fd); void addClient(short unsigned fd);
void removeClient(int fd); void removeClient(short unsigned fd);
void updateServer(int fd); void updateServer(short unsigned fd);
void pollLoop(int server_fd); void pollLoop(int server_fd, std::vector<int> &newClients, std::vector<int> &disconnected, std::vector<std::pair<int, std::string> > &readyClients);
private: private:
std::vector<struct pollfd> _fds; std::vector<struct pollfd> _fds;
std::map<int, std::string> _buffers;
}; };

View file

@ -1,14 +1,14 @@
/* ************************************************************************** */ /******************************************************************************/
/* */ /* */
/* ::: :::::::: */ /* ::: :::::::: */
/* core.hpp :+: :+: :+: */ /* core.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */ /* +:+ +:+ +:+ */
/* By: sben-tay <sben-tay@student.42.fr> +#+ +:+ +#+ */ /* By: omoudni <omoudni@student.42paris.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2025/05/12 14:16:03 by rparodi #+# #+# */ /* Created: 2025/05/12 14:16:03 by rparodi #+# #+# */
/* Updated: 2025/05/20 17:23:41 by sben-tay ### ########.fr */ /* Updated: 2025/05/21 21:18:22 by omoudni ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /******************************************************************************/
#pragma once #pragma once
@ -28,8 +28,13 @@ enum e_state {
CMD, CMD,
MSG MSG
}; };
// INCLUDES (not to repeat)
#include <stdexcept>
#include <iostream>
#include <string>
#include "user.hpp"
#include "PollManager.hpp" #include "PollManager.hpp"
#include "color.hpp" #include "color.hpp"
#include "server.hpp" #include "server.hpp"

View file

@ -1,26 +1,28 @@
/* ************************************************************************** */ /******************************************************************************/
/* */ /* */
/* ::: :::::::: */ /* ::: :::::::: */
/* server.hpp :+: :+: :+: */ /* server.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */ /* +:+ +:+ +:+ */
/* By: rparodi <rparodi@student.42.fr> +#+ +:+ +#+ */ /* By: omoudni <omoudni@student.42paris.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2025/05/20 21:50:32 by rparodi #+# #+# */ /* Created: 2025/05/20 21:50:32 by rparodi #+# #+# */
/* Updated: 2025/05/21 13:03:01 by rparodi ### ########.fr */ /* Updated: 2025/05/21 21:19:47 by omoudni ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /******************************************************************************/
#pragma once #pragma once
#include <string> #include "core.hpp"
#include "PollManager.hpp"
class User;
class Server { class Server {
private: private:
int _port; int _port;
int _serverFd; int _serverFd;
std::string _password; std::string _password;
PollManager _pollManager; PollManager _pollManager;
std::map<int, User*> _users;
public: public:
Server(int port, const std::string &password); Server(int port, const std::string &password);
~Server(); ~Server();

View file

@ -1,36 +1,42 @@
/* ************************************************************************** */ /******************************************************************************/
/* */ /* */
/* ::: :::::::: */ /* ::: :::::::: */
/* user.hpp :+: :+: :+: */ /* user.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */ /* +:+ +:+ +:+ */
/* By: rparodi <rparodi@student.42.fr> +#+ +:+ +#+ */ /* By: omoudni <omoudni@student.42paris.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2025/05/20 21:57:49 by rparodi #+# #+# */ /* Created: 2025/05/20 21:57:49 by rparodi #+# #+# */
/* Updated: 2025/05/20 22:13:41 by rparodi ### ########.fr */ /* Updated: 2025/05/21 21:16:56 by omoudni ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /******************************************************************************/
#pragma once #pragma once
#include <string> #include "core.hpp"
#include <iostream>
#include "color.hpp"
class User { class User
private: {
int _fd; private:
bool _registered; short unsigned int _fd;
std::string _nickname; bool _registered;
std::string _hostname; std::string _nickname;
std::string _read_buffer; std::string _hostname;
std::string _write_buffer; std::string _read_buffer;
public: std::string _write_buffer;
int getFd() const; std::string _username;
bool isReadyToSend() const; bool _hasNick;
bool isRegistered() const; bool _hasUser;
std::string getNickname() const;
std::string extractFullCommand(); public:
void appendToReadBuffer(const std::string &data); User(short unsigned fd);
void appendToWriteBuffer(const std::string &data); short unsigned int getFd() const;
void setNickname(const std::string &nickname); bool isReadyToSend() const;
bool isRegistered() const;
std::string getNickname() const;
std::string extractFullCommand();
void appendToReadBuffer(const std::string &data);
void appendToWriteBuffer(const std::string &data);
void setNickname(const std::string &nickname);
void setUsername(const std::string &username);
void checkRegistration();
}; };

View file

@ -10,72 +10,86 @@
PollManager::PollManager() {} PollManager::PollManager() {}
PollManager::~PollManager() { PollManager::~PollManager()
for (size_t i = 0; i < _fds.size(); ++i) { {
close(_fds[i].fd); for (size_t i = 0; i < _fds.size(); ++i)
} {
close(_fds[i].fd);
}
} }
void PollManager::pollLoop(int server_fd) { void PollManager::pollLoop(int server_fd, std::vector<int> &newClients, std::vector<int> &disconnected, std::vector<std::pair<int, std::string> > &readyClients)
{
struct pollfd server_pollfd; struct pollfd server_pollfd;
server_pollfd.fd = server_fd; server_pollfd.fd = server_fd;
server_pollfd.events = POLLIN; server_pollfd.events = POLLIN;
_fds.push_back(server_pollfd); _fds.push_back(server_pollfd);
std::cout << "Serveur prêt à accepter des connexions..." << std::endl; std::cout << "Serveur prêt à accepter des connexions..." << std::endl;
while (true) { // while (true) {
int poll_count = poll(&_fds[0], _fds.size(), -1); int poll_count = poll(&_fds[0], _fds.size(), -1);
if (poll_count == -1) { if (poll_count == -1)
std::cerr << "poll error\n" << std::endl; {
continue; std::cerr << "poll error\n"
} << std::endl;
for (size_t i = 0; i < _fds.size(); ++i) { return;
int fd = _fds[i].fd; }
for (size_t i = 0; i < _fds.size(); ++i)
{
short unsigned fd = _fds[i].fd;
if ((fd == server_fd) && (_fds[i].revents & POLLIN)) { if ((fd == server_fd) && (_fds[i].revents & POLLIN))
int client_fd = accept(server_fd, NULL, NULL); {
if (client_fd == -1) { int client_fd = accept(server_fd, NULL, NULL);
std::cerr << "Error accept()" << std::endl; if (client_fd == -1)
continue; {
} std::cerr << "Error accept()" << std::endl;
addClient(client_fd); continue;
} }
else if (_fds[i].revents & POLLIN) { addClient(client_fd);
char buffer[1024]; newClients.push_back(client_fd);
ssize_t bytes = recv(fd, buffer, sizeof(buffer) -1, 0); }
if (bytes > 0) { else if (_fds[i].revents & POLLIN)
buffer[bytes] = '\0'; {
_buffers[fd] += buffer; char buffer[1024];
std::cout << "Client " << fd << " send : " << buffer; ssize_t bytes = recv(fd, buffer, sizeof(buffer) - 1, 0);
} else { if (bytes > 0)
std::cout << "Client " << fd << " disconected." << std::endl; {
// removeClient(fd); buffer[bytes] = '\0';
--i; readyClients.push_back(std::make_pair(fd, std::string(buffer)));
} }
else
{
removeClient(fd);
disconnected.push_back(fd);
--i;
} }
} }
} }
// }
} }
void PollManager::addClient(int fd) { void PollManager::addClient(short unsigned fd)
{
struct pollfd pfd; struct pollfd pfd;
pfd.fd = fd; pfd.fd = fd;
pfd.events = POLLIN; pfd.events = POLLIN;
_fds.push_back(pfd); _fds.push_back(pfd);
_buffers[fd] = ""; std::cout << "Client connected (fd " << fd << ")" << std::endl;
std::cout << "Client connecté (fd " << fd << ")" << std::endl;
} }
void PollManager::removeClient(int fd) { void PollManager::removeClient(short unsigned fd)
for (size_t i = 0; i < _fds.size(); ++i) { {
if (_fds[i].fd == fd) { for (size_t i = 0; i < _fds.size(); ++i)
{
if (_fds[i].fd == fd)
{
_fds.erase(_fds.begin() + i); _fds.erase(_fds.begin() + i);
break; break;
} }
} }
_buffers.erase(fd);
close(fd); close(fd);
std::cout << "Client retiré (fd " << fd << ")" << std::endl; std::cout << "Client disconnected (fd " << fd << ")" << std::endl;
} }

View file

@ -1,14 +1,14 @@
/* ************************************************************************** */ /******************************************************************************/
/* */ /* */
/* ::: :::::::: */ /* ::: :::::::: */
/* Server.cpp :+: :+: :+: */ /* Server.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */ /* +:+ +:+ +:+ */
/* By: sben-tay <sben-tay@student.42.fr> +#+ +:+ +#+ */ /* By: omoudni <omoudni@student.42paris.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2025/05/13 11:11:07 by rparodi #+# #+# */ /* Created: 2025/05/13 11:11:07 by rparodi #+# #+# */
/* Updated: 2025/05/21 13:03:51 by rparodi ### ########.fr */ /* Updated: 2025/05/21 21:50:03 by omoudni ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /******************************************************************************/
#include "color.hpp" #include "color.hpp"
#include "server.hpp" #include "server.hpp"
@ -37,6 +37,9 @@ Server::Server(int port, const std::string &password) : _port(port), _password(p
*/ */
Server::~Server() { Server::~Server() {
std::cout << CLR_GREY << "Info: Server destructor called" << CLR_RESET << std::endl; std::cout << CLR_GREY << "Info: Server destructor called" << CLR_RESET << std::endl;
if (_serverFd != -1) {
close(_serverFd);
}
} }
@ -61,8 +64,47 @@ void Server::start() {
} }
std::cout << "Serveur lancé sur le port " << _port << std::endl; std::cout << "Serveur lancé sur le port " << _port << std::endl;
std::vector<int> newClients;
std::vector<int> disconnected;
std::vector<std::pair<int, std::string> > readyClients;
while (true)
{
newClients.clear();
disconnected.clear();
readyClients.clear();
_pollManager.pollLoop(_serverFd, newClients, disconnected, readyClients);
_pollManager.pollLoop(_serverFd); // Handle new clients
for (size_t i = 0; i < newClients.size(); ++i)
_users[newClients[i]] = new User(newClients[i]);
// Handle disconnected clients
for (size_t i = 0; i < disconnected.size(); ++i)
{
delete _users[disconnected[i]];
_users.erase(disconnected[i]);
}
for (size_t i = 0; i < readyClients.size(); ++i)
{
int fd = readyClients[i].first;
const std::string &data = readyClients[i].second;
if (_users.count(fd))
{
_users[fd]->appendToReadBuffer(data);
//print users
std::cout << "User " << fd << " is registered: " << _users[fd]->isRegistered() << std::endl;
std::cout << "Received data from fd " << fd << ": " << data << std::endl;
std::string cmd;
while (!(cmd = _users[fd]->extractFullCommand()).empty())
{
// This prints every command/message received from any client
std::cout << "Client " << fd << " says: " << cmd << std::endl;
}
}
}
// Optionally: handle server shutdown, signals, etc.
}
close(_serverFd); close(_serverFd);
} }

108
sources/core/user.cpp Normal file
View file

@ -0,0 +1,108 @@
/******************************************************************************/
/* */
/* ::: :::::::: */
/* user.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: omoudni <omoudni@student.42paris.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/05/21 20:37:12 by omoudni #+# #+# */
/* Updated: 2025/05/21 21:44:58 by omoudni ### ########.fr */
/* */
/******************************************************************************/
#include "core.hpp"
// Constructor
User::User(short unsigned int fd) : _fd(fd), _registered(false), _hasNick(false), _hasUser(false) {}
// Getter for fd
short unsigned int User::getFd() const
{
return _fd;
}
// Getter for nickname
std::string User::getNickname() const
{
return _nickname;
}
void User::setUsername(const std::string &username)
{
_username = username;
_hasUser = true;
checkRegistration();
}
// Setter for nickname (with basic checks)
void User::setNickname(const std::string &nickname)
{
if (nickname.empty())
{
throw std::invalid_argument("Nickname cannot be empty");
}
else if (nickname == "anonymous")
{
throw std::invalid_argument("Nickname cannot be 'anonymous'");
}
else if (nickname.length() > 9)
{
throw std::length_error("Nickname is too long");
}
else if (nickname == _nickname)
{
throw std::invalid_argument("The nickname is the same");
}
else
{
_nickname = nickname;
_hasNick = true;
checkRegistration();
}
}
// Registration state
bool User::isRegistered() const
{
return _registered;
}
// Append to read buffer
void User::appendToReadBuffer(const std::string &data)
{
_read_buffer += data;
}
// Append to write buffer
void User::appendToWriteBuffer(const std::string &data)
{
_write_buffer += data;
}
// Check registration
void User::checkRegistration()
{
if (!_registered && _hasNick && _hasUser)
{
_registered = true;
}
}
// Check if the user is ready to send
bool User::isReadyToSend() const
{
return !_write_buffer.empty();
}
// Extract full command from read buffer
std::string User::extractFullCommand()
{
std::string command;
size_t pos = _read_buffer.find("\r\n");
if (pos != std::string::npos)
{
command = _read_buffer.substr(0, pos);
_read_buffer.erase(0, pos + 2);
}
return command;
}

View file

@ -1,76 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* user.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: rparodi <rparodi@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/05/20 22:03:36 by rparodi #+# #+# */
/* Updated: 2025/05/20 22:15:14 by rparodi ### ########.fr */
/* */
/* ************************************************************************** */
#include "user.hpp"
/**
* @brief Getter for the fd of the user
*
* @return the actual fd of the user
*/
int User::getFd() const {
return this->_fd;
}
/**
* @brief Getter for the nickname of the user
*
* @return the actual nickname of the user
*/
std::string User::getNickname() const {
return this->_nickname;
}
/**
* @brief Setter for the nickname of the user (also checker)
*
* @param nickname the nickname given to set to the user
*/
void User::setNickname(const std::string &nickname) {
if (nickname.empty()) {
throw std::invalid_argument("Nickname cannot be empty");
} else if (nickname == "anonymous") {
throw std::invalid_argument("Nickname cannot be 'anonymous'");
} else if (nickname.length() > 9) {
throw std::length_error("Nickname is too long");
} else if (nickname == this->_nickname) {
throw std::invalid_argument("The nickname is the same");
} else {
this->_nickname = nickname;
}
}
/**
* @brief Getter for the state of the user
*
* @return true if the user is registered, false otherwise
*/
bool User::isRegistered() const {
return this->_registered;
}
void User::appendToReadBuffer(const std::string &data) {
std::cerr << CLR_RED << "Error: Method not found (" << __FILE_NAME__ ":" << __LINE__ << ")" << CLR_RESET << std::endl;
(void)data;
}
void User::appendToWriteBuffer(const std::string &data) {
std::cerr << CLR_RED << "Error: Method not found (" << __FILE_NAME__ ":" << __LINE__ << ")" << CLR_RESET << std::endl;
(void)data;
}
std::string User::extractFullCommand() {
std::cerr << CLR_RED << "Error: Method not found (" << __FILE_NAME__ ":" << __LINE__ << ")" << CLR_RESET << std::endl;
return nullptr;
}
bool User::isReadyToSend() const {
std::cerr << CLR_RED << "Error: Method not found (" << __FILE_NAME__ ":" << __LINE__ << ")" << CLR_RESET << std::endl;
return (false);
}