Tcp 当客户端连接到tokio proto服务器时,如何显示欢迎信息/横幅?

Tcp 当客户端连接到tokio proto服务器时,如何显示欢迎信息/横幅?,tcp,rust,rust-tokio,Tcp,Rust,Rust Tokio,SMTP服务器应在建立连接(220服务就绪)时显示欢迎消息,这是客户端开始发送命令的信号。这似乎与tokio proto的请求-响应范式相冲突 我可以想象,协议可以完全颠倒,比如服务器发送请求和客户端响应(不推荐使用),但目前我只关心连接时的欢迎消息,即横幅。在此之后,将支持客户端请求=>服务器响应 我一直在想把它挂在哪里,但是bind_服务器,bind_传输对我来说太神秘了。我需要实施运输吗 我在编解码器的decode方法中有这个。问题是,除非有数据可用于解码,否则不会调用decode方法,哪

SMTP服务器应在建立连接(220服务就绪)时显示欢迎消息,这是客户端开始发送命令的信号。这似乎与tokio proto的请求-响应范式相冲突

我可以想象,协议可以完全颠倒,比如服务器发送请求和客户端响应(不推荐使用),但目前我只关心连接时的欢迎消息,即横幅。在此之后,将支持客户端请求=>服务器响应

我一直在想把它挂在哪里,但是
bind_服务器
bind_传输
对我来说太神秘了。我需要实施运输吗

我在编解码器的
decode
方法中有这个。问题是,除非有数据可用于解码,否则不会调用
decode
方法,哪种方法有意义。我希望会有一些连接初始化方法,但我什么也没找到

fn decode(&mut self, buf: &mut BytesMut) -> Result {

    if !self.initialized {
        println!(
            "new connection from {:?} to {:?}",
            self.peer_addr,
            self.local_addr
        );

        self.requests.push(SmtpCommand::Connect {
            local_addr: self.local_addr,
            peer_addr: self.peer_addr,
        });

        self.initialized = true;
    }
    //... snip
    match self.requests.is_empty() {
        true => Ok(None),
        false => Ok(Some(self.requests.remove(0))),
    }
}

我和我也实现了。

实现了我自己的有状态传输装饰器(
SmtpConnectTransport
)。它将在初始化时注入给定的帧。我想,通过将initframe类型作为参数,它可以成为一个通用的解决方案。最终,除了解析和序列化之外,编解码器不需要做任何异常的事情

通过连接时马上出现的框架,服务可以生成所需的欢迎消息或横幅。我将本地和远程套接字地址包含在
SmtpCommand::Connect
中,以便于该服务用于垃圾邮件检测

我的预感是正确的,但解决这个问题感觉就像生锈的金属研磨:我现在很高兴这是怎么回事。下面是一些代码:

use std::io;
use std::str;
use bytes::Bytes;
use model::response::SmtpReply;
use model::request::SmtpCommand;
use protocol::codec::SmtpCodec;
use tokio_proto::streaming::pipeline::{Frame, Transport, ServerProto};
use tokio_io::codec::Framed;
use futures::{Stream, Sink, StartSend, Poll, Async};
use protocol::parser::SmtpParser;
use protocol::writer::SmtpSerializer;

type Error = io::Error;
type CmdFrame = Frame<SmtpCommand, Bytes, Error>;
type RplFrame = Frame<SmtpReply, (), Error>;

pub struct SmtpProto;

impl<TIO: NetSocket + 'static> ServerProto<TIO> for SmtpProto {
    type Error = Error;
    type Request = SmtpCommand;
    type RequestBody = Bytes;
    type Response = SmtpReply;
    type ResponseBody = ();
    type Transport = SmtpConnectTransport<Framed<TIO, SmtpCodec<'static>>>;
    type BindTransport = io::Result<Self::Transport>;

    fn bind_transport(&self, io: TIO) -> Self::BindTransport {
        // save local and remote socket address so we can use it as the first frame
        let initframe = Frame::Message {
            body: false,
            message: SmtpCommand::Connect {
                local_addr: io.local_addr().ok(),
                peer_addr: io.peer_addr().ok(),
            },
        };
        let codec = SmtpCodec::new(
            SmtpParser::session_parser(),
            SmtpSerializer::answer_serializer(),
        );
        let upstream = io.framed(codec);
        let transport = SmtpConnectTransport::new(upstream, initframe);
        Ok(transport)
    }
}

pub struct SmtpConnectTransport<TT> {
    initframe: Option<CmdFrame>,
    upstream: TT,
}

impl<TT> SmtpConnectTransport<TT> {
    pub fn new(upstream: TT, initframe: CmdFrame) -> Self {
        Self {
            upstream,
            initframe: Some(initframe),
        }
    }
}

impl<TT> Stream for SmtpConnectTransport<TT>
where
    TT: 'static + Stream<Error = Error, Item = CmdFrame>,
{
    type Error = Error;
    type Item = CmdFrame;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        match self.initframe.take() {
            Some(frame) => {
                println!("transport initializing");
                Ok(Async::Ready(Some(frame)))
            }
            None => self.upstream.poll(),
        }
    }
}

impl<TT> Sink for SmtpConnectTransport<TT>
where
    TT: 'static + Sink<SinkError = Error, SinkItem = RplFrame>,
{
    type SinkError = Error;
    type SinkItem = RplFrame;

    fn start_send(&mut self, request: Self::SinkItem) -> StartSend<Self::SinkItem, io::Error> {
        self.upstream.start_send(request)
    }

    fn poll_complete(&mut self) -> Poll<(), io::Error> {
        self.upstream.poll_complete()
    }

    fn close(&mut self) -> Poll<(), io::Error> {
        self.upstream.close()
    }
}

impl<TT> Transport for SmtpConnectTransport<TT>
where
    TT: 'static,
    TT: Stream<Error = Error, Item = CmdFrame>,
    TT: Sink<SinkError = Error, SinkItem = RplFrame>,
{
}


pub trait NetSocket: AsyncRead + AsyncWrite {
    fn peer_addr(&self) -> Result<SocketAddr>;
    fn local_addr(&self) -> Result<SocketAddr>;
}

impl NetSocket for TcpStream {
    fn peer_addr(&self) -> Result<SocketAddr> {
        self.peer_addr()
    }
    fn local_addr(&self) -> Result<SocketAddr> {
        self.local_addr()
    }
}
使用std::io;
使用std::str;
使用字节::字节;
使用model::response::SmtpReply;
使用model::request::SmtpCommand;
使用协议::编解码器::SmtpCodec;
使用tokio_proto::streaming::pipeline:{Frame,Transport,ServerProto};
使用tokio_io::codec::Framed;
使用未来:{Stream,Sink,StartSend,Poll,Async};
使用protocol::parser::SmtpParser;
使用协议::编写器::SmtpSerializer;
类型错误=io::错误;
类型CmdFrame=Frame;
RplFrame类型=帧;
发布结构SmtpProto;
impl>>;
类型BindTransport=io::Result;
fn bind_传输(&self,io:TIO)->self::BindTransport{
//保存本地和远程套接字地址,以便我们可以将其用作第一帧
让initframe=Frame::Message{
正文:错,
消息:SmtpCommand::Connect{
本地地址:io.local\u addr().ok(),
peer\u addr:io.peer\u addr().ok(),
},
};
让codec=SmtpCodec::新建(
SmtpParser::session_parser(),
SmtpSerializer::answer\u serializer(),
);
让上游=io.framed(编解码器);
let transport=SmtpConnectTransport::new(上游,initframe);
Ok(运输)
}
}
发布结构SmtpConnectTransport{
initframe:选项,
上游:TT,
}
impl SmtpConnectTransport{
pub fn new(上游:TT,initframe:CmdFrame)->Self{
自我{
上游
initframe:Some(initframe),
}
}
}
SmtpConnectTransport的impl流
哪里
TT:'静态+流,
{
类型错误=错误;
类型Item=CmdFrame;
fn轮询(&mut self)->轮询{
匹配self.initframe.take(){
一些(帧)=>{
println!(“传输初始化”);
Ok(异步::就绪(一些(帧)))
}
None=>self.upstream.poll(),
}
}
}
SmtpConnectTransport的impl接收器
哪里
TT:“静态+接收器,
{
类型SinkError=错误;
类型SinkItem=RplFrame;
fn开始发送(&mut self,请求:self::SinkItem)->StartSend{
self.upstream.start\u发送(请求)
}
fn轮询\u完成(&mut self)->轮询{
self.upstream.poll_complete()
}
fn关闭(&mut self)->轮询{
self.upstream.close()
}
}
SmtpConnectTransport的impl传输
哪里
TT:“静态,
TT:Stream,
TT:水槽,
{
}
发布特性NetSocket:AsyncRead+AsyncWrite{
fn peer_addr(&self)->结果;
fn本地地址(&self)->结果;
}
用于TcpStream的impl NetSocket{
fn peer_addr(&self)->结果{
self.peer_addr()
}
fn本地地址(&self)->结果{
self.local_addr()
}
}