Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ project(znet-parent)

add_subdirectory(vendor/fmt)
add_subdirectory(znet)
add_subdirectory(relay_server)

# examples
add_subdirectory(examples/basic/server)
add_subdirectory(examples/basic/client)
add_subdirectory(examples/relay_client)

# tests
enable_testing()
Expand Down
11 changes: 6 additions & 5 deletions TODO
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,20 @@
- [x] Client Implementation
- [ ] Compression (optional if zlib or zstd is added)
- [x] Encryption
- [ ] Protocol version check
- [ ] TCP options
- [x] Windows support
- [x] Linux support (Linux should be the same)
- [x] Endianness
- [ ] Saving packet buffer to a file
- [ ] Async Example
- [ ] More events
- [ ] Better error handling
- [x] Unit tests
- [ ] More unit tests
- [ ] Documentation
- [ ] Error handling in SendPacket and SendRaw
- [x] Documentation
- [ ] Error handling in SendPacket
- [ ] Proxy support
- [ ] Unix socket support
- [ ] Limit array/vector read sizes
- [ ] Limit array/vector/string read sizes
- [ ] Connection throttling
- [ ] Peer to peer TCP hole-punching
- [ ] Validate IP addresses
56 changes: 40 additions & 16 deletions examples/basic/client/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,76 +14,100 @@

using namespace znet;

// MyPacketHandler manages network communication with a simple request-response pattern.
// It inherits from PacketHandler and specifically handles DemoPacket type messages.
class MyPacketHandler : public PacketHandler<MyPacketHandler, DemoPacket> {
public:
// Creates a new handler associated with the given peer session
MyPacketHandler(std::shared_ptr<PeerSession> session) : session_(session) { }

// Called when a demo packet is received. Logs the event and sends back
// a response with a greeting message.
void OnPacket(std::shared_ptr<DemoPacket> p) {
ZNET_LOG_INFO("Received demo_packet.");
std::shared_ptr<DemoPacket> pk = std::make_shared<DemoPacket>();
pk->text = "Got ya! Hello from server!";
session_->SendPacket(pk);
}

// Handles any unknown packet types that might be received.
// Currently just ignores them as a fallback mechanism.
void OnUnknown(std::shared_ptr<Packet> p) {
// fallback for unknown types
}

private:
// Stores the session we use to send responses back to the client
std::shared_ptr<PeerSession> session_;
};

// Sets up a new client connection with appropriate packet handling and encoding.
// Returns false to allow other handlers to process the event if needed.
bool OnConnectEvent(ClientConnectedToServerEvent& event) {
PeerSession& session = *event.session();

// Codecs are what serializes and deserializes the packets,
// You can set an initial codec, then get the protocol version from the client
// then set a different codec for different versions. This provides much more flexibility
// Codecs can be swapped mid-session
// Set up how packets will be encoded/decoded
// Note: It's more efficient to create this codec once and share it
// between clients, but for this example we create it per-connection

// Additionally, it is wiser to have this created once and shared between clients
// For this example it is created on the spot like this
std::shared_ptr<Codec> codec = std::make_shared<Codec>();
codec->Add(PACKET_DEMO, std::make_unique<DemoSerializer>());
session.SetCodec(codec);

// Handlers are what handles the packets, like codecs this can be swapped mid-session
// For example during the login phase, you can give a different handler to only
// handle those are sent during the login, then change the codec.
// Set up how packets will be processed
// The handler can be changed during the session - for example,
// you might use different handlers for login vs. game play
session.SetHandler(std::make_shared<MyPacketHandler>(event.session()));

// Send an initial greeting to the other peer
std::shared_ptr<DemoPacket> pk = std::make_shared<DemoPacket>();
pk->text = "Hello from client!";
event.session()->SendPacket(pk);
return false;
}

// Main event dispatcher - routes different types of events
// to their appropriate handlers
void OnEvent(Event& event) {
EventDispatcher dispatcher{event};
// Route for different types of events
dispatcher.Dispatch<ClientConnectedToServerEvent>(
ZNET_BIND_GLOBAL_FN(OnConnectEvent));
}

int main() {
// Create config
// Create a client configuration
// We're connecting to localhost (127.0.0.1) on port 25000
// In a real application, you'd typically get these values from
// command line arguments or a config file or from ui
ClientConfig config{"127.0.0.1", 25000};

// Create client with the config
// Initialize the network client with our configuration
// This sets up the internal client state but doesn't connect yet
Client client{config};

// Set event callback
// Register our event handler to process network events
// OnEvent will be called for events.
client.SetEventCallback(ZNET_BIND_GLOBAL_FN(OnEvent));

// Bind and connect
// Try to bind the client to a local network interface
// This is required before we can connect to the server
if (client.Bind() != Result::Success) {
return 1;
return 1; // Exit if binding fails (e.g., port already in use)
}

// Attempt to establish a connection to the server
// This initiates the connection process but doesn't wait. (async)
// It runs on another thread
if (client.Connect() != Result::Success) {
return 1; // Failed to connect
return 1; // Exit if connection fails (e.g., server unreachable)
}

// Wait for the connection to complete
// Note: In a real application, you typically wouldn't block here
// Instead, you'd continue your program and do other stuff
client.Wait();

// Connection was successful and completed.
// Connection is finished (disconnected)
return 0;
}
69 changes: 48 additions & 21 deletions examples/basic/server/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,67 +13,83 @@

using namespace znet;

// MyPacketHandler manages network communication, handling incoming messages
// from connected clients and sending appropriate responses
class MyPacketHandler : public PacketHandler<MyPacketHandler, DemoPacket> {
public:
// Creates a new handler for a specific client session
MyPacketHandler(std::shared_ptr<PeerSession> session) : session_(session) { }

// Processes incoming demo packets from clients
// Responds with a greeting message to demonstrate two-way communication
void OnPacket(std::shared_ptr<DemoPacket> p) {
ZNET_LOG_INFO("Received demo_packet.");
std::shared_ptr<DemoPacket> pk = std::make_shared<DemoPacket>();
pk->text = "Got ya! Hello from server!";
session_->SendPacket(pk);
}

// Fallback handler for any unrecognized packet types
// Currently ignores them, but you could add logging or error handling here
void OnUnknown(std::shared_ptr<Packet> p) {
// fallback for unknown types
}

private:
// The client session this handler is responsible for
std::shared_ptr<PeerSession> session_;
};

bool OnNewSessionEvent(ServerClientConnectedEvent& event) {
// Called whenever a new client connects to the server
// Sets up the communication channel with proper encoding and handling
bool OnNewSessionEvent(IncomingClientConnectedEvent& event) {
PeerSession& session = *event.session();

// Codecs are what serializes and deserializes the packets,
// You can set an initial codec, then get the protocol version from the client
// then set a different codec for different versions. This provides much more flexibility
// Codecs can be swapped mid-session

// Additionally, it is wiser to have this created once and shared between clients
// For this example it is created on the spot like this
// Create and set up the packet codec (encoder/decoder)
// In production, you'd typically share one codec instance among all clients
// rather than creating a new one for each connection
std::shared_ptr<Codec> codec = std::make_shared<Codec>();
codec->Add(PACKET_DEMO, std::make_unique<DemoSerializer>());
session.SetCodec(codec);

// Handlers are what handles the packets, like codecs this can be swapped mid-session
// For example during the login phase, you can give a different handler to only
// handle those are sent during the login, then change the codec.
// Set up the packet handler for this client
// Different handlers can be used for different connection states
// (e.g., one for login, another for gameplay)
session.SetHandler(std::make_shared<MyPacketHandler>(event.session()));
return false;
}

// Called when a client disconnects from the server
// You can add cleanup code here if needed
bool OnDisconnectSessionEvent(ServerClientDisconnectedEvent& event) {
return false;
}

// Central event dispatcher that routes all server events
// to their appropriate handler functions
void OnEvent(Event& event) {
EventDispatcher dispatcher{event};
// Bind desired events
dispatcher.Dispatch<ServerClientConnectedEvent>(
// Route for different types of events
dispatcher.Dispatch<IncomingClientConnectedEvent>(
ZNET_BIND_GLOBAL_FN(OnNewSessionEvent));
dispatcher.Dispatch<ServerClientDisconnectedEvent>(
ZNET_BIND_GLOBAL_FN(OnDisconnectSessionEvent));
}

int main() {
// Create config
// Create the server configuration
// We're listening on localhost (127.0.0.1) port 25000
// In a real application, you'd typically get these values from
// command line arguments or a config file or from ui
ServerConfig config{"127.0.0.1", 25000};

// Create server with the config
// Initialize the server with our configuration
// This sets up the internal server state but doesn't start listening yet
Server server{config};

// Register signal handlers (optional)
// Set up signal handling for graceful shutdown
// This ensures the server closes cleanly when interrupted (Ctrl+C)
// This part is optional
RegisterSignalHandler([&server](Signal sig) -> bool {
if (sig == znet::kSignalInterrupt) {
// stop the server when SIGINT is received
Expand All @@ -83,17 +99,28 @@ int main() {
return false;
});

// Set event callback
// Register our event handler to process server events
// OnEvent will be called for client connections, disconnections,
// and other server events
server.SetEventCallback(ZNET_BIND_GLOBAL_FN(OnEvent));

// Bind and listen
// Try to bind the server to the configured network interface
// This reserves the port for our use
if (server.Bind() != Result::Success) {
return 1;
return 1; // Exit if binding fails (e.g., port already in use)
}

// Start listening for incoming client connections
// This begins accepting clients but doesn't block the main thread (async)
if (server.Listen() != Result::Success) {
return 1; // failed to listen
return 1; // Exit if we can't start listening
}

// Wait for the server to stop
// Note: In a real application, you typically wouldn't block here
// Instead, you'd continue your program and do other stuff
server.Wait();
return 0; // successfully completed

// Server has shut down cleanly
return 0;
}
10 changes: 10 additions & 0 deletions examples/relay_client/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 3.16.3)
project(relay-client)

add_executable(relay-client main.cc)
set_target_properties(relay-client PROPERTIES
CXX_STANDARD 14
CXX_STANDARD_REQUIRED ON
)
target_link_libraries(relay-client PRIVATE znet)

Loading
Loading