Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

62 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🐗 orcNet

A from-scratch IRC server in C++98, built around a single-threaded epoll event loop, a Singleton server, and a command-dispatcher architecture.


What it is

orcNet is an IRC server written in strict C++98. It accepts multiple simultaneous clients on a single thread, multiplexing I/O with epoll. The project demonstrates how to model network resources, protocol commands, and shared server state without modern smart pointers or multi-threading.


Core design

The server is built around three collaborating objects:

┌────────────────────────────────────────────────────────────┐
│                          Server                            │
│  Singleton. Owns the listening socket, the epoll instance, │
│  and the registries of all Clients and Channels.           │
└──────────────┬──────────────────────────────┬──────────────┘
               │                              │
      ┌────────▼──────────┐        ┌──────────▼──────────┐
      │      Client       │        │      Channel        │
      │ One per TCP conn  │        │ One per #channel    │
      │ Holds read buffer │        │ Holds members & ops │
      │ Has messageStack  │        │ Has mode flags      │
      │ State: READ/WRITE │        │                     │
      └────────┬──────────┘        └─────────────────────┘
               │
    ┌──────────▼───────────┐
    │  MessageValidator    │
    │ Parses, validates &  │
    │ dispatches IRC cmds  │
    └──────────────────────┘

Why this shape?

  • Server is a Singleton — Many validators and command handlers need to ask "is this nick in use?", "does this channel exist?", etc. A single global Server::getServer() keeps every component consistent without passing pointers through every function call.
  • Client owns connection state — Each client knows its file descriptor, registration status, pending outgoing messages (messageStack), and current ConnectionState. That state drives what epoll should watch for next.
  • Channel is the unit of broadcast — It keeps its own member list and operator list. When someone speaks, Server::sendChannelMessage() pushes the message to every other member's outgoing queue and flips them to WRITE.
  • MessageValidator is static — Validation rules don't need state; they inspect the input, consult the singleton server, and return a (bool, payload) pair that the command handler consumes.

Design patterns in use

Pattern Where you see it What it solves
Singleton Server::getServer() One authoritative server instance; accessible from validators and command handlers
RAII Client, Channel, exception classes Sockets, epoll state, and members cleaned up in destructors
Command Dispatcher Client::insert() dispatches after MessageValidator::validate() Separates parsing, validation, and execution so commands are easy to add
State Machine ConnectionState: READ, WRITE, CLOSE Tells epoll exactly when to poll for input or output, and when to tear down
Factory of replies FormattedMessages All IRC numeric replies and prefixed commands generated in one place
Custom exception hierarchy AExceptionSysCallException, ParsingException Distinguishes syscall failures (bind, listen, accept) from setup/logic errors
Pub-Sub broadcast Server::sendChannelMessage() Sends a message to every channel member except the emitter

The event loop

epoll_wait()
    │
    ├── serverFD ready  → acceptConnection() → new Client → EPOLLIN
    │
    ├── EPOLLIN on client → recv() → buffer lines → parseMessage() → queue replies → EPOLLOUT
    │
    └── EPOLLOUT on client → send messageStack → return to EPOLLIN (or CLOSE)

All sockets are non-blocking and registered with edge-triggered (EPOLLET) epoll. A client buffers partial reads in a std::stringstream; only complete \r\n or \n-terminated lines are parsed.

When a handler produces a reply, it pushes it onto the client's messageStack and calls setState(WRITE). The next loop iteration sees EPOLLOUT, drains the queue, then switches back to READ. On QUIT, disconnect, or fatal error, the state becomes CLOSE and the server removes the FD from epoll and frees the Client.


Supported IRC commands

Command What it does
PASS Server password authentication
NICK Set / change nickname
USER Set username / realname
JOIN Join a channel; comma-separated channels supported
PART Leave a channel with optional reason
PRIVMSG Message a channel or a user
WHO List members of a channel
KICK Operator removes a member
INVITE Operator invites a user
MODE View / change modes: o, i, t, k, l
TOPIC View / set a channel topic
QUIT Disconnect
CAP LS 302 Capability negotiation — accepted as a no-op

Build & run

make
./ircserv <port> <password>

Example:

./ircserv 6667 secret123

Then connect:

nc localhost 6667

or point any IRC client at localhost:6667.

Quick registration

PASS secret123
NICK alice
USER alice 0 * :Alice Smith

Highlights worth noticing

  • Single-threaded concurrency: No threads, no locks — all coordination happens through epoll and explicit state on Client objects.
  • Two client dialects: The parser detects whether a connection uses \r\n (HexChat) or plain \n (netcat) by counting \r\n sequences.
  • Buffered output: Outgoing messages are queued so the server never blocks on send().
  • Colored logging: Logger + Color namespaces give timestamped, color-coded debug/info/error output.
  • Manual memory management: Every Client and Channel is freed in Server::deallocate() or in removeFdFromManager() / channel destructors.

Limitations

  • C++98 only — raw pointers are used intentionally.
  • No TLS/SSL.
  • Server-wide operator (OPER) is not fully implemented beyond the numeric reply.
  • Registration expects PASS → NICK → USER order.

Built as part of the 42 curriculum — a focused exercise in sockets, protocol parsing, and event-driven design.

orcNet — small server, big tusks. 🐗

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages