{"id":28560744,"url":"https://github.com/thomvanoorschot/wire","last_synced_at":"2026-02-27T23:16:09.056Z","repository":{"id":292498053,"uuid":"981078327","full_name":"Thomvanoorschot/wire","owner":"Thomvanoorschot","description":"Wire is a networking library written in Zig, designed to simplify building non-blocking TCP-based client-server applications. It integrates with the xev event loop for asynchronous I/O operations and is able to run non-blocking on a single thread.","archived":false,"fork":false,"pushed_at":"2025-05-27T05:29:32.000Z","size":890,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-27T06:29:49.452Z","etag":null,"topics":["event-loop","libxev","networking","non-blocking","tcp","tcp-client","tcp-server","zig-package"],"latest_commit_sha":null,"homepage":"","language":"Zig","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Thomvanoorschot.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-05-10T09:40:30.000Z","updated_at":"2025-05-27T05:29:36.000Z","dependencies_parsed_at":"2025-05-10T11:21:36.605Z","dependency_job_id":"07b4ba48-efc6-43e8-86d2-4e4e1654fc9f","html_url":"https://github.com/Thomvanoorschot/wire","commit_stats":null,"previous_names":["thomvanoorschot/wire"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thomvanoorschot%2Fwire","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thomvanoorschot%2Fwire/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thomvanoorschot%2Fwire/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thomvanoorschot%2Fwire/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Thomvanoorschot","download_url":"https://codeload.github.com/Thomvanoorschot/wire/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thomvanoorschot%2Fwire/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259049912,"owners_count":22798059,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["event-loop","libxev","networking","non-blocking","tcp","tcp-client","tcp-server","zig-package"],"created_at":"2025-06-10T09:38:05.470Z","updated_at":"2026-02-27T23:16:04.020Z","avatar_url":"https://github.com/Thomvanoorschot.png","language":"Zig","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"wire.png\" alt=\"Project Logo\" width=\"500\" /\u003e\n\u003c/p\u003e\n\n# Wire: A TCP Networking Library for Zig\n\n## Overview\n\nWire is a networking library written in Zig, designed to simplify building TCP-based client-server applications. It leverages Zig's capabilities for memory safety and performance, and integrates with the `xev` event loop for asynchronous I/O operations able to run on a single thread.\n\n## Goals\n*   Provide a straightforward API for TCP client and server creation.\n*   Implement a simple message framing protocol for clear data exchange.\n*   Enable asynchronous, non-blocking network communication.\n*   Offer a callback-based mechanism for handling network events.\n*   Serve as a practical example of Zig for network programming.\n\n## Architecture\n\nWire is built around two main components:\n\n*   **`Client`**: Manages a connection to a TCP server. It handles connecting, sending data, and receiving framed messages.\n*   **`Server`**: Listens for incoming TCP connections and manages multiple `ClientConnection` instances.\n*   **`ClientConnection`**: Represents a connection from a client to the server, handling reading and writing of framed data.\n*   **`Frame` / `FrameHeader`**: Defines the structure for messages, where each message is prefixed with a header indicating its type and the length of its payload. This allows for structured communication between client and server.\n\n## Features\n\n*   **Asynchronous Operations**: Utilizes `xev` for non-blocking network I/O.\n*   **Message Framing**: Implements a basic framing protocol (message type + payload length) to delineate messages over TCP streams.\n*   **Callback-Driven**: Uses callbacks to notify application code of events such as new connections, incoming data, and disconnections.\n*   **Memory Management**: Leverages Zig's allocators for explicit memory control.\n*   **Client and Server Abstractions**: Provides easy-to-use `Client` and `Server` types.\n\n## Learning Outcomes\n\nThis project can provide insights into:\n*   Network programming in Zig.\n*   Working with event loops (specifically `xev`).\n*   Implementing basic network protocols (framing).\n*   Zig's error handling and memory management in a networking context.\n*   Callback-based event handling.\n\n## Getting Started\n\n(Instructions for integrating and using the Wire library will be added as development progresses.)\n\n## Usage Example\n\nHere's a basic example of how to use the `Client` to connect to a server and handle messages:\n\n```zig\nconst std = @import(\"std\");\nconst xev = @import(\"xev\"); // Assuming xev is available\nconst wire = @import(\"wire\");\n\n// 1. Define the message types your application will use.\n//    This enum will be used by the client to dispatch messages to the correct callbacks.\npub const MessageTypes = enum {\n    myMessageA,\n    myMessageB,\n};\n\n// 2. Define callback functions for each message type.\n//    These functions will be called when a message of the corresponding type is received.\nfn handleMyMessageA(context: ?*anyopaque, payload: []const u8) anyerror!void {\n    // 'context' is the optional context pointer provided during client initialization.\n    // 'payload' is the raw byte slice of the message.\n    // Process payload for myMessageA\n    std.debug.print(\"Received myMessageA: {s}\\n\", .{payload});\n    _ = context; // Avoid unused variable warning if context is not used\n}\n\nconst MyCustomStruct = struct {\n    const Self = @This();\n\n    fn handleMyMessageB(context: ?*anyopaque, payload: []const u8) anyerror!void {\n        // Process payload for myMessageB\n        const self = @as(*Self, @ptrCast(@alignCast(context)));\n        std.debug.print(\"Received myMessageB: {s}\\n\", .{payload});\n        _ = context;\n    }\n}\n\nvar customStruct = MyCustomStruct{};\n\npub fn main() !void {\n    var gpa = std.heap.GeneralPurposeAllocator(.{}){};\n    const allocator = gpa.allocator();\n    defer _ = gpa.deinit();\n\n    var loop = try xev.Loop.init(.{});\n    defer loop.deinit();\n\n    \n    // 3. Initialize the Client.\n    var client = try wire.Client(MessageTypes).init(\n        allocator,\n        \u0026loop,\n        .{ // ClientOptions\n            .server_addr = try std.net.Address.parseIp4(\"127.0.0.1\", 8080), // Target server address\n            // .keep_alive = false, // Optional: defaults to false\n        },\n        .{ // Callbacks for each MessageType\n            .myMessageA = .{\n                .context = null,\n                .cb = handleMyMessageA\n            },\n            .myMessageB = .{\n                .context = @ptrCast(\u0026customStruct),\n                .cb = handleMyMessageB\n            },\n        },\n    );\n\n    // 4. Connect to the server.\n    //    The connection happens asynchronously.\n    //    You might want a connection callback in a real application to know when it's established.\n    client.connect();\n\n    // 5. Start reading messages from the server.\n    //    This tells the client to begin listening for incoming framed messages.\n    client.startReading();\n\n    // Run the event loop to process network events.\n    try loop.run();\n}\n\n```\nThis example demonstrates:\n*   Defining `MessageTypes`.\n*   Creating callback functions for these types.\n*   Initializing the `wire.Client` with server address, options, and callbacks.\n*   Connecting the client using `client.connect()`.\n*   Initiating message reading with `client.startReading()`.\n*   Running the `xev` event loop.\n\n### Server Usage Example\n\nHere's a basic example of how to use the `Server` to accept connections and handle client messages:\n\n```zig\nconst std = @import(\"std\");\nconst xev = @import(\"xev\");\nconst wire = @import(\"wire\");\n\n// 1. Define message types (must be the same as the client's MessageTypes).\npub const MessageTypes = enum {\n    myMessageA,\n    myMessageB,\n};\n\n// Forward declaration for ConnectionContext if needed for callbacks\nconst ConnectionContext = struct {\n    allocator: std.mem.Allocator,\n    client_conn: *wire.ClientConnection,\n};\n\n// 2. Implement the callback for when a new client is accepted.\nfn serverAcceptCallback(\n    server_context: ?*anyopaque, // Context provided during Server.init\n    loop: *xev.Loop,\n    accept_completion: *xev.Completion,\n    client_conn: *wire.ClientConnection,\n) xev.CallbackAction {\n    _ = loop;\n    _ = accept_completion;\n\n    const allocator = @as(*std.mem.Allocator, @ptrCast(@alignCast(server_context))).?;\n\n    std.debug.print(\"Server: Client connected (fd: {d})\\n\", .{client_conn.socket.fd});\n\n    // 3. For each connection, set up context and start reading.\n    const conn_ctx = allocator.create(ConnectionContext) catch |err| {\n        std.debug.print(\"Server: Failed to allocate context for connection: {any}\\n\", .{err});\n        // client_conn.close(null); // Close if context allocation fails\n        return .rearm; // Continue accepting other connections\n    };\n    conn_ctx.* = .{\n        .allocator = allocator,\n        .client_conn = client_conn,\n    };\n\n    // Set a callback for when this specific client connection closes\n    client_conn.setCloseCallback(@ptrCast(conn_ctx), clientCloseCallback);\n\n    // Start reading messages from this client\n    client_conn.read(@ptrCast(conn_ctx), clientReadCallback);\n\n    // Server should continue to accept new connections\n    return .rearm;\n}\n\n// 4. Implement the callback for reading data from a client.\nfn clientReadCallback(\n    context: ?*anyopaque,\n    payload: []const u8,\n) void {\n    const conn_ctx = @as(*ConnectionContext, @ptrCast(@alignCast(context))).?;\n    std.debug.print(\"Server: Received from client (fd: {d}): {s}\\n\", .{conn_ctx.client_conn.socket.fd, payload});\n\n    // Example: Echo the message back or send a different response\n    const response_payload = \"Server acknowledges your message!\" catch unreachable; // Using a string literal\n    conn_ctx.client_conn.write(\n        MessageTypes, // The enum type\n        .myMessageA, // The specific message type from the enum\n        response_payload,\n    ) catch |err| {\n        std.debug.print(\"Server: Failed to write to client (fd: {d}): {any}\\n\", .{conn_ctx.client_conn.socket.fd, err});\n        // The connection might be closed by the write error handler in ClientConnection\n    };\n}\n\n// 5. Implement the callback for when a client connection is closed.\nfn clientCloseCallback(context: ?*anyopaque) anyerror!void {\n    const conn_ctx = @as(*ConnectionContext, @ptrCast(@alignCast(context))).?;\n    std.debug.print(\"Server: Client disconnected (fd: {d})\\n\", .{conn_ctx.client_conn.socket.fd});\n    // Deinitialize/free the ConnectionContext\n    conn_ctx.allocator.destroy(conn_ctx);\n}\n\npub fn main() !void {\n    var gpa = std.heap.GeneralPurposeAllocator(.{}){};\n    const allocator = gpa.allocator();\n    defer _ = gpa.deinit();\n\n    var loop = try xev.Loop.init(.{});\n    defer loop.deinit();\n\n    // 6. Initialize the Server.\n    var server = try wire.Server.init(\n        allocator,\n        \u0026loop,\n        .{ // ServerOptions\n            .address = try std.net.Address.parseIp4(\"127.0.0.1\", 8080),\n            .max_connections = 10,\n        },\n        @ptrCast(\u0026allocator), // Pass allocator as server context for acceptCallback\n        serverAcceptCallback,\n    );\n    defer server.deinit(); // Ensure server resources are cleaned up\n\n    // 7. Start accepting connections.\n    //    This will continuously listen for new clients in the background via the event loop.\n    server.accept();\n    std.debug.print(\"Server listening on 127.0.0.1:8080\\n\", .{});\n\n    // Run the event loop to process network events.\n    try loop.run();\n}\n```\n\nThis server example shows:\n*   Initializing `wire.Server` with an address and an `acceptCallback`.\n*   The `acceptCallback` is invoked for each new client.\n*   Inside `acceptCallback`, `client_conn.read()` is called with a `readCallback` to process incoming data from that specific client.\n*   `client_conn.write()` is used to send framed messages back to the client.\n*   `client_conn.setCloseCallback()` is used to register a function to be called when the client disconnects, allowing for resource cleanup.\n\n## Project Status\n\n🚧 Early Development – The core client and server components, along with message framing, are implemented. Further development will focus on robustness, features, and examples.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthomvanoorschot%2Fwire","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthomvanoorschot%2Fwire","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthomvanoorschot%2Fwire/lists"}