IRC server implementation in C++98 for the 42 School ft_irc project.
This project implements a standalone IRC server compatible with a standard IRC client. The server was thouroughly tested with the irssi client that runs fully in the terminal.
Disclaimer: the server does not work on macOS. It was developed for linux systems.
make./ircserv <port> <password>port: TCP port to listen onpassword: password required by clients
- Non-blocking TCP server (single
poll()or equivalent) - Multiple clients without forking
- Communication over IPv4 or IPv6
- Client registation (IRC protocol specification):
PASSNICKUSER
- Channel management:
JOIN,PART- Channel message broadcasting
- Private messages:
PRIVMSG
- Regular users
- Channel operators:
KICK– remove a user from a channelINVITE– invite a user to a channelTOPIC– set/view channel topicMODE:iinvite-only channelttopic restricted to operatorskchannel key (password)ogive/take operator statusluser limit
- Use non-blocking sockets with a single
poll()loop - Maintain per-client read and write buffers
- Never send without
poll()signalingPOLLOUT - Validate command order in IRC standard (
PASS→NICK→USER) - Aggregate TCP input per client into buffers until
\r\nbefore parsing commands - Centralize error replies (numeric responses)
- Cleanly remove clients on disconnect or error
- Avoid STL iterator invalidation when modifying client/channel lists (look at correct patterns)
- Enforce channel modes at command entry points
- Treat TCP disconnect (
recv == 0) as immediate quit
- Low bandwidth
- Recieving partial data
- C++98 only
- No external libraries
- No blocking I/O
- One
poll()(or equivalent) for all FDs - Compile with
-Wall -Wextra -Werror
- Incoming data is buffered to handle partial reads.
- Commands are processed only after full
\r\ntermination. - Designed to behave similarly to a real IRC server for supported features.
Warning! The non-blocking socket below is for demo only and does not use poll(), thus violating the rules.
- EAGAIN || EWOULDBLOCK: error code for no data is available to read right now
- O_NONBLOCK: flag that makes a file descriptor non-blocking
- F_SETFL: fcntl command to set file descriptor flags (like O_NONBLOCK)
- AF_INET: address family for IPv4
- SOCK_STREAM: socket type for TCP (connection-oriented, reliable)
int fd = socket(AF_INET, SOCK_STREAM, 0);
/* fd is blocking by default */
char buf[512];
recv(fd, buf, sizeof(buf), 0); // ← execution stops here
printf("This line runs only after data arrives\n");int fd = socket(AF_INET, SOCK_STREAM, 0);
/* make socket non-blocking (only allowed flags for fcntl btw) */
fcntl(fd, F_SETFL, O_NONBLOCK);
char buf[512];
/* read() equivalent for network sockets */
ssize_t n = recv(fd, buf, sizeof(buf), 0);
if (n == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
/* no data available right now */
printf("recv() returned immediately\n");
} else if (n == 0) {
/* client disconnected */
} else {
/* data recieved */
}Problems with this :
- spins the CPU
- scales poorly
- breaks under load
struct pollfd {
int fd; // file descriptor
short events; // what you want to watch
short revents; // what actually happened
};// fds : array of pollfd structures (one per file descriptor)
// nfds : number of elements in the fds array
// timeout : time to wait in milliseconds
// -1 = wait forever
// 0 = return immediately
// > 0 = wait up to timeout ms
int poll(struct pollfd *fds, nfds_t nfds, int timeout);- POLLIN: At least one byte is available to read or the peer closed the connection
- POLLOUT: There is space in the socket send buffer (kernel‑managed memory that temporarily stores data you send before it goes onto the network)
- POLLERR: A socket error occurred (must close the fd)
- POLLHUP: The peer hung up (connection closed)
- POLLNVAL: Invalid file descriptor (bug: fd closed or never valid)
revents flags are bitwise :
- POLLIN = 0001
- POLLOUT = 0010
- POLLERR = 0100
- POLLHUP = 1000
- POLLNVAL (set by kernel)
revents = POLLIN | POLLERR → 0101
struct pollfd pfd;
pfd.fd = fd;
pfd.events = POLLIN;
int ready = poll(&pfd, 1, -1); // kernel waits here
if (ready > 0) {
if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
// close connection
}
else if (pfd.revents & POLLIN) {
ssize_t n = recv(fd, buf, sizeof(buf), 0);
if (n > 0) {
/* data received */
} else if (n == 0) {
/* client disconnected */
}
}
}