-
-
Notifications
You must be signed in to change notification settings - Fork 12
chat_tcp_analysis.md
Mohamed edited this page Jun 10, 2025
·
1 revision
-
Location:
example/core_io/chat_tcp/ - Objective: This example demonstrates how to construct a classic client-server TCP chat application using the QB Actor Framework. It's an excellent case study for understanding how actors manage network connections, handle custom communication protocols, manage user sessions, and distribute workload across multiple cores.
By dissecting this example, you'll see practical applications of:
- Asynchronous TCP client and server patterns using
qb::io::use<>. - Custom protocol definition for message framing (
ChatProtocol). - Actor-based session management.
- Inter-actor communication for application logic.
- Multi-core actor distribution.
The chat_server employs a distributed architecture, separating responsibilities across actors typically running on different VirtualCores for scalability.
-
Header:
server/AcceptActor.h,server/AcceptActor.cpp - Core Assignment (Typical): Core 0 (dedicated to accepting new connections).
- Role: Listens for incoming TCP connections on one or more network ports.
-
QB Integration: Inherits from
qb::Actorandqb::io::use<AcceptActor>::tcp::acceptor.- The
tcp::acceptorbase provides thetransport()method (aqb::io::tcp::listener) and handles the low-level asynchronous accept operations.
- The
- **Initialization (
onInit()):It configures its// Inside AcceptActor::onInit() for (const auto& uri_str : _listen_uris) { qb::io::uri u(uri_str.c_str()); if (this->transport().listen(u) != 0) { /* error handling */ } } this->start(); // Start the internal async::input mechanism of the acceptor base
qb::io::tcp::listener(viathis->transport()) to listen on specified URIs (e.g., "tcp://0.0.0.0:3001").this->start()activates the underlying event loop monitoring for new connections. - **Handling New Connections (
on(accepted_socket_type&& new_io)):When a TCP connection is accepted by the// Inside AcceptActor::on(accepted_socket_type&& new_io) if (!_server_pool.empty()) { qb::ActorId target_server = _server_pool[_session_counter % _server_pool.size()]; _session_counter++; push<NewSessionEvent>(target_server, std::move(new_io)); }
listener, this method is invoked by thetcp::acceptorbase.new_iois aqb::io::tcp::socketrepresenting the newly connected client. TheAcceptActorthen dispatches this new socket to one of theServerActorinstances (from_server_pool) using a round-robin strategy, wrapping the socket in aNewSessionEvent. -
Shutdown: Its
on(qb::io::async::event::disconnected const&)handler for the listener socket (if it gets closed or errors out) triggers a broadcast ofqb::KillEventto gracefully shut down other server components.
-
Header:
server/ServerActor.h,server/ServerActor.cpp -
Core Assignment (Typical): Core 1 (or a pool of
ServerActors across multiple cores, e.g., cores 1 & 2). -
Role: Manages a collection of active client connections (
ChatSessioninstances). It acts as a bridge between individual client sessions and the centralChatRoomActor. -
QB Integration: Inherits from
qb::Actorandqb::io::use<ServerActor>::tcp::server<ChatSession>.- The
tcp::server<ChatSession>base provides theio_handlerfunctionality, automatically managing a map ofChatSessionobjects (keyed byqb::uuid).
- The
- **Handling New Sessions (
on(NewSessionEvent&)):Receives the// Inside ServerActor::on(NewSessionEvent& evt) auto& session = registerSession(std::move(evt.socket)); // session is a ChatSession& // The registerSession method (from io_handler base) creates a ChatSession, // associates the socket, starts its I/O, and adds it to the managed session map.
NewSessionEventfrom anAcceptActor. TheregisterSession(std::move(evt.socket))call (provided by theio_handlerpart of its base) instantiates aChatSession, associates the client's socket with it, starts its asynchronous I/O operations, and adds it to an internal session map. - **Message Routing (from
ChatSessiontoChatRoomActor):-
ChatSessioncalls methods likeserver().handleAuth(id(), username)on its managingServerActor. -
ServerActorthen creates specific events (e.g.,AuthEvent,ChatEvent) andpushes them to theChatRoomActor.
-
- **Message Routing (from
ChatRoomActortoChatSession):Receives// Inside ServerActor::on(SendMessageEvent& evt) auto session_ptr = sessions().find(evt.target_session_id); if (session_ptr != sessions().end() && session_ptr->second) { // Send the raw message content using the session's output stream *(session_ptr->second) << evt.message_container.message().payload; }
SendMessageEventfromChatRoomActor. It looks up the targetChatSessionin its session map and sends the message payload directly to the client using the session'soperator<<(which usespublish()). - **Client Disconnects (
handleDisconnect(qb::uuid session_id)):- Called by a
ChatSessionwhen its connection drops. Forwards aDisconnectEventto theChatRoomActor.
- Called by a
-
Header:
server/ChatSession.h,server/ChatSession.cpp -
Context: Instantiated and managed by a
ServerActor, runs on the sameVirtualCoreas its managingServerActor. - Role: Represents and handles all I/O and protocol parsing for a single connected client.
-
QB Integration: Inherits from
qb::io::use<ChatSession>::tcp::client<ServerActor>andqb::io::use<ChatSession>::timeout.-
tcp::client<ServerActor>: Provides the TCP transport and stream capabilities. TheServerActortemplate argument allows the session to call back to its manager (e.g.,server().handleAuth(...)). -
timeout: Adds inactivity timeout functionality.
-
-
Protocol Handling:
-
using Protocol = chat::ChatProtocol<ChatSession>;(defined inshared/Protocol.h). - The constructor calls
this->template switch_protocol<Protocol>(*this);to activate the custom protocol. -
on(chat::Message& msg): This method is invoked by the framework when theChatProtocolsuccessfully parses a complete message from the client. Based onmsg.type(AUTH_REQUEST,CHAT_MESSAGE), it calls the appropriatehandleAuth(...)orhandleChat(...)method on its parentServerActorinstance (accessed viaserver()).
-
-
Lifecycle Events:
-
on(qb::io::async::event::disconnected const&): Handles socket disconnection. Callsserver().handleDisconnect(this->id()). -
on(qb::io::async::event::timer const&): Handles inactivity timeout. Also callsserver().handleDisconnect(this->id()).
-
-
Header:
server/ChatRoomActor.h,server/ChatRoomActor.cpp - Core Assignment (Typical): Core 3 (a separate core for application logic).
-
Role: Manages the chat room's state, including the list of authenticated users and their associated
ServerActor(for routing replies). It handles authentication, message broadcasting, and user presence. -
State:
-
_sessions: A map fromqb::uuid(client session ID) toSessionInfo { qb::ActorId server_id, qb::string username }. -
_usernames: A map fromqb::string usernametoqb::uuidfor quick lookup.
-
-
Event Handlers:
-
on(AuthEvent&): Validates username. If valid, stores session info, sends anAUTH_RESPONSE(chat::MessageType::RESPONSE) back to the specific client via the correctServerActor(usingpush<SendMessageEvent>(evt.server_id, ...)), and broadcasts a join message to all other clients. -
on(ChatEvent&): Retrieves the username for the sending session. Formats the chat message (e.g., "username: message_content"). Broadcasts this formatted message to all connected clients via their respectiveServerActors. -
on(DisconnectEvent&): Removes the user and session information from its state maps. Broadcasts a leave message to remaining clients.
-
-
Message Broadcasting (
broadcastMessage,sendToSessionhelpers): These methods iterate through the_sessionsmap andpushaSendMessageEventto the appropriateServerActorfor each recipient. TheSendMessageEventcontains theqb::uuidof the targetChatSessionand the message payload (as achat::MessageContainer, which usesstd::shared_ptrfor efficient sharing of message data).
The client is simpler, typically running actors on fewer cores.
-
Header:
client/InputActor.h,client/InputActor.cpp - Core Assignment (Typical): Core 0.
- Role: Reads user input from the console asynchronously.
-
QB Integration: Inherits from
qb::Actorandqb::ICallback. -
Functionality (
onCallback()): Usesstd::getline(std::cin, line)(note:std::cinitself can be blocking if not handled carefully, thoughonCallbackis non-blocking with respect to other actors). If the input is "quit", it sends aqb::KillEventto theClientActor. Otherwise, itpushes aChatInputEvent(containing the raw input string) to theClientActor.
-
Header:
client/ClientActor.h,client/ClientActor.cpp - Core Assignment (Typical): Core 1.
- Role: Manages the TCP connection to the server, sends user messages, and displays incoming chat messages to the console.
-
QB Integration: Inherits from
qb::Actorandqb::io::use<ClientActor>::tcp::client<>. -
Protocol:
using Protocol = chat::ChatProtocol<ClientActor>;. - **Connection (
onInit()and connection callback):- Uses
qb::io::async::tcp::connectto establish a non-blocking connection to the server URI. - The callback lambda, upon successful TCP connection, moves the new socket into
this->transport().transport(), switches to theChatProtocol(this->template switch_protocol<Protocol>(*this);), starts I/O event monitoring (this->start();), and then sends an initialAUTH_REQUESTmessage to the server. - If connection fails, it uses
qb::io::async::callbackto schedule a reconnection attempt.
- Uses
-
Event Handling:
-
on(ChatInputEvent&): Receives raw command strings fromInputActor. If connected and authenticated, formats them intochat::Messageobjects (e.g.,CHAT_MESSAGEtype) and sends them to the server using*this << protocol_message << Protocol::end;. -
on(chat::Message&): Receives messages from the server parsed byChatProtocol. HandlesAUTH_RESPONSE(updates authenticated state),CHAT_MESSAGE(prints to console), andERRORmessages. -
on(qb::io::async::event::disconnected const&): Handles server disconnection, clears authenticated state, and attempts to reconnect usingqb::io::async::callback.
-
-
Shutdown (
on(qb::KillEvent&)): Callsthis->disconnect()(which internally callsthis->close()on the transport) and thenthis->kill().
- Client-Server Architecture with Actors: A classic networking pattern implemented using actor principles.
- Multi-Core Actor Distribution: Demonstrates assigning different roles (accepting, session handling, core logic, UI input) to actors potentially running on different cores.
-
Asynchronous TCP Client & Server: Extensive use of
qb::io::use<>templates for TCP operations (tcp::acceptor,tcp::server,tcp::client). -
Custom Protocol (
ChatProtocol): Shows how to define a header-based binary protocol for message framing and howqb::allocator::pipecan be specialized withputfor efficient serialization into the output buffer (seeProtocol.cpp). -
Actor-Based Session Management: The
ServerActoruses theio_handlercapabilities provided byqb::io::use<...>::tcp::server<ChatSession>to manage multipleChatSessionobjects. -
Centralized State Management: The
ChatRoomActoracts as a central authority for shared application state (user lists, subscriptions), ensuring consistent access through sequential event processing. -
Inter-Actor Communication: Clear examples of
pushfor reliable event delivery between actors, and howActorIds are used for addressing. -
Connection Resilience (Client-Side): Basic reconnection logic implemented using
qb::io::async::callback. -
Inactivity Timeouts (Session-Side):
ChatSessionusesqb::io::use<...>::timeoutto detect and handle idle client connections. - Separation of Concerns: Network I/O, user input, and core application logic are well-separated into distinct actor responsibilities.
By studying the chat_tcp example, developers can gain a solid understanding of how to combine qb-core and qb-io to build complex, scalable, and robust networked applications.
(Next Example Analysis: distributed_computing Example Analysis**)