Tcp 创建侦听端口的简单Rust守护进程

Tcp 创建侦听端口的简单Rust守护进程,tcp,process,daemon,rust,Tcp,Process,Daemon,Rust,我一直在尝试在Rust中创建一个简单的守护进程,它将使用tcp_流侦听端口并打印消息。但是,我遇到了两个问题: 1) 如果我的守护进程使用println!,它崩溃了。如果我删除所有关于println的提及!,守护进程可以工作。创建守护进程时,stdout/stdin是如何工作的 说“对于现代的init系统,比如systemd或launchctl,它工作得非常好,应用程序开发人员不必关心后台监控,日志记录也可以通过stdout完成。”这是什么意思 2) 当我在非守护程序模式下运行下面的代码时,cu

我一直在尝试在Rust中创建一个简单的守护进程,它将使用tcp_流侦听端口并打印消息。但是,我遇到了两个问题:

1) 如果我的守护进程使用println!,它崩溃了。如果我删除所有关于println的提及!,守护进程可以工作。创建守护进程时,stdout/stdin是如何工作的

说“对于现代的init系统,比如systemd或launchctl,它工作得非常好,应用程序开发人员不必关心后台监控,日志记录也可以通过stdout完成。”这是什么意思

2) 当我在非守护程序模式下运行下面的代码时,curls不会立即返回(运行类似于
$curl-XPOST localhost:9337-d'hi'
)。我必须杀掉curl,服务器才能打印一些东西。卷曲不会自动关闭连接吗?发送的字节在发送后,而不是在连接关闭后,不应该对服务器可用吗

extern crate getopts;
use getopts::{optflag,getopts};
use std::io::Command;
use std::io::net::tcp::{TcpListener};
use std::io::{Acceptor,Listener};
use std::os;

fn main() {
    let args: Vec<String> = os::args();
    let opts = [
        optflag("d", "daemon", "conver this into a daemon"),
    ];
    let matches = match getopts(args.tail(), opts) {
        Ok(m) => { m },
        Err(f) => { fail!(f.to_string()) }
    };

    // Create a daemon? if necessary
    if matches.opt_present("d") {
        let child = Command::new(args[0].as_slice())
                            .detached().spawn().unwrap();
        println!("Created child: {}", child.id());

        // Do I wrap this in unsafe?
        child.forget();
        return;
    }

    let listener = TcpListener::bind("127.0.0.1", 9337u16).ok().expect("Failed to bind");
    let mut acceptor = listener.listen().ok().expect("Could not listen");

    loop {
        let mut tcp_stream = acceptor.accept().ok().expect("Could not accept connection");
        println!("Accepted new connection");

        let message = tcp_stream.read_to_string().unwrap();
        println!("Received message {}", message);
    }
}
extern板条箱getopts;
使用getopts::{optflag,getopts};
使用std::io::命令;
使用std::io::net::tcp:{TcpListener};
使用std::io::{Acceptor,Listener};
使用std::os;
fn main(){
让args:Vec=os::args();
让选项=[
optflag(“d”,“daemon”,“将其转换为daemon”),
];
让matches=match getopts(args.tail(),opts){
Ok(m)=>{m},
Err(f)=>{fail!(f.to_string())}
};
//如有必要,创建守护进程
如果匹配,则选择“存在”(“d”){
let child=Command::new(args[0].as_slice())
.detached().spawn().unwrap();
println!(“创建的子项:{}”,child.id());
//我要用不安全的包装吗?
孩子。忘记();
返回;
}
让listener=TcpListener::bind(“127.0.0.1”,9337u16).ok().expect(“绑定失败”);
让mut acceptor=listener.listen().ok().expect(“无法侦听”);
环路{
让mut tcp_stream=acceptor.accept().ok().expect(“无法接受连接”);
println!(“接受的新连接”);
让message=tcp_stream.read_to_string().unwrap();
println!(“收到的消息{}”,消息);
}
}
这是什么意思

他们的意思是你不应该做任何像分叉这样的花哨的事情来创建一个守护程序。您的程序应该只是工作,将其操作直接记录到STDUT中,而像StReD或LaunChCTL这样的init系统将自动处理所有其他事项,包括启动、关机、日志重定向、生命周期管理等。认真考虑这种方法,因为它会使程序变得简单得多。 但是,正确地创建守护进程并不简单。您必须分叉流程,关闭并设置新的文件描述符,调整流程组,添加信号处理程序等等。在谷歌上搜索类似的内容会提供很多关于如何创建守护进程的文章,你会发现这不是一项容易的任务。当然,您可以在Rust中执行类似的操作,因为它通过板条箱公开所有必要的系统调用。不过,可能有一些警告:例如,我不确定Rust运行时对
fork()
系统调用会有什么反应


至于为什么您的“守护进程”在使用
println时失败!()
,我怀疑发生这种情况是因为您从子进程中分离,它的stdio句柄会自动关闭,而生锈的I/O例程对此不满意,并触发任务失败。

在为您创建守护进程时,使用crate来处理大部分棘手的事情?

而systemd和launchctl可能会处理fork()+exec()对于您来说,无论是传统的init系统还是所有现代系统都不会这样做。在illumos上,svc.startd仍然需要经典的守护程序行为,如
smf_方法(5)
的手册页面所述。因此,当针对Linux和MacOS以外的其他平台时,Daemoning仍然是一件事。