#ipc #server #lazy-evaluation #unix-socket #message #inter-process #client-server

ipc-server

轻量级、懒加载、异步的进程间消息传递服务器/客户端接口

5个不稳定版本

0.3.2 2024年7月29日
0.3.1 2024年7月21日
0.3.0 2024年7月21日
0.2.0 2024年7月21日
0.1.0 2024年7月20日

978网络编程

Download history 162/week @ 2024-07-16 72/week @ 2024-07-23 75/week @ 2024-07-30

每月309次下载

MIT/Apache

12KB
125

ipc-server:懒加载和异步IPC服务器

此库允许同一台机器上的独立客户端和服务器进程通过Unix套接字传递消息,Unix套接字仅由当前用户可读和可写。这种进程间通信(IPC)适用于管理员管理运行时可配置变量等用例,事实上它受到了solana的admin rpc服务的启发。

与其他服务器不同,此服务器是懒加载和异步的。没有专门的服务器线程处于忙碌循环或等待消息。相反,服务器类型公开了一个轮询方法,您可以在应用程序中的某个循环中集成该方法。

概述

此系统的三个相关组件是客户端、服务器以及它们之间传递的消息。我们将从最后一个组件开始介绍。

请参阅examples/simple/simple.rs,这是一个完全自包含且自解释的示例。我们将在整个README中参考此示例。

消息

此服务器使用bincode进行消息的序列化和反序列化。首先,您必须为您的消息实现IpcServerCommand

pub trait IpcServerCommand: Serialize + for<'a> Deserialize<'a> + std::fmt::Debug {
    type Response: Serialize + for<'a> Deserialize<'a> + std::fmt::Debug;
    type Context<'a>;

    fn process<'a, 'b>(self, context: &'b mut Self::Context<'a>) -> Self::Response;
}

此特性告诉服务器如何处理命令。我们建议使用枚举来允许多种消息类型。类型Context<'a>允许服务器访问服务器外部的状态(内存或持久状态,如向量、哈希表或持久/外部数据库)。例如,在simple示例中,有三个类型的消息

#[derive(Subcommand, Serialize, Deserialize, Debug)]
enum ClientCommand {
    Print { payload: String },
    Add { a: u64, b: u64 },
    Push { x: u64 },
}

这些变体是自解释的。Print 打印有效载荷。 Add 添加两个数字,打印它们并返回结果。 Push 将一个值压入栈中。在这种情况下,栈(即 Context<'a>),以及每个变体的处理方式和消息响应类型都在特质实现中指定

impl IpcServerCommand for ClientCommand {
    type Response = ClientResponse;
    type Context<'a> = &'a mut Vec<u64>;
    fn process<'a, 'b>(self, context: &'b mut Self::Context<'a>) -> ClientResponse {
        match self {
            ClientCommand::Print { payload } => {
                println!("Print command received: {}", payload);
                ClientResponse::PrintAck
            }
            ClientCommand::Add { a, b } => {
                let c = a + b;
                println!("Add command received: {} + {} = {}", a, b, c);
                ClientResponse::Add { c }
            }
            ClientCommand::Push { x } => {
                println!("Push command received: {}", x);
                context.push(x);
                ClientResponse::Push { x }
            }
        }
    }
}

#[derive(Serialize, Deserialize, Debug)]
enum ClientResponse {
    /// A simple ack
    PrintAck,
    /// The sum result
    Add { c: u64 },
    /// The value pushed
    Push { x: u64 },
}

这完全指定了客户端可以发送的消息、服务器如何处理它们以及客户端可以期望哪些类型的响应。

服务器

IpcServer 类型对命令是泛型的。在指定上一节中描述的消息类型后,要获得服务器,你需要做的是

let socket_path = "ipc-server-path-example.sock"
let server = IpcServer::<ClientCommand>::new(socket_path).unwrap();

现在服务器已经初始化了。但请记住,没有专用的服务器线程。除非调用 server.handle_new_messages(..) 方法,否则不会接收或处理任何消息。你的应用程序必须定义如何准备 Context<'a>。这可能是空或对数据结构或数据库的引用。这个轮询方法将读取、处理并响应所有待处理的消息。

客户端

没有客户端类型。只有一个 fn client_send<C: IpcServerCommand>(command: &C, socket_path: &str),它通过指向提供的套接字地址的 UnixStream 序列化和发送给定的命令。

注意事项

服务器和客户端使用固定大小的 1024 字节缓冲区来处理消息。如果你的消息大小超过这个,可能会遇到问题。

依赖项

~1–8.5MB
~72K SLoC