前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【投稿】actix-websocket 使用 protocol的一点个人理解

【投稿】actix-websocket 使用 protocol的一点个人理解

作者头像
MikeLoveRust
发布2021-01-07 10:19:41
9770
发布2021-01-07 10:19:41
举报
文章被收录于专栏:Rust语言学习交流

actix的docs和example对protocol的使用有点省略,对着源码实验了下,终于搞清楚了。

标准浏览器websocket的构造函数WebSocket(url[, protocols])会有个可选参数protocols,即一个字符串形式的约定协议。

对于actix的websocket的例子一般如下,即用actix_web_actors::ws::start来初始化websocket。

代码语言:javascript
复制
struct MyWebSocket{}

async fn ws_index(r: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {
    ws::start(MyWebSocket::new(), &r, stream)
}

如果前端传了protocol,actix会响应请求然后自动关闭连接,并不能正常构建websocket连接。

这里整了我半天,后来才发现actix_web_actors::ws有一个初始函数叫start_with_protocols,必须用这个函数才能接收带有protocol的websocket连接。

代码语言:javascript
复制
pub fn start_with_protocols<A, T>(
    actor: A,
    protocols: &[&str],
    req: &HttpRequest,
    stream: T,
) -> Result<HttpResponse, Error>
where
    A: Actor<Context = WebsocketContext<A>>
        + StreamHandler<Result<Message, ProtocolError>>,
    T: Stream<Item = Result<Bytes, PayloadError>> + 'static,
{
    let mut res = handshake_with_protocols(req, protocols)?;
    Ok(res.streaming(WebsocketContext::create(actor, stream)))
}

start_with_protocols比普通的start多了个protocols参数,试试了这个protocols其实代表的是合法的协议名列表,即前端传来的protocol必须在protocols里面才能正常构建websocket连接。

然后顺便看了start方法的源码,发现其实也调用了handshake_with_protocols这个方法,但会默认令合法协议protocols=&[],也就是置空, 这样如果前端如果没传protocol,actix处理出的protocol则为None,可以满足构建要求, 如果不为空,则必然不在合法协议列表protocols=&[]里面,所以无法正常构建websocket连接。

不过令人困惑的是,在初始化以后,如果想在websocket的帧里获取protocol是什么,并不能像在处理路由句柄时里的r: HttpRequest里面直接r.headers().get(&header::SEC_WEBSOCKET_PROTOCOL).unwrap().to_str().unwrap()获取,所以我的解决办法是在自定义的MyWebSocket结构体里进行保存。

代码语言:javascript
复制
struct MyWebSocket {
    protocol: String
}

impl MyWebSocket {
    fn new(protocol: String) -> Self {
        Self {protocol}
    }
}

const PROTOCOLS: &[&str] = &["chat"];

async fn ws_index(r: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {
    let protocol = r.headers().get(&header::SEC_WEBSOCKET_PROTOCOL).unwrap().to_str().unwrap();
    ws::start_with_protocols(MyWebSocket::new(protocol.to_string()), PROTOCOLS, &r, stream)
}

这样就可以在接收流处理句柄里拿到protocol了,根据websocket的帧协议来看,似乎确实不包含protocol的信息,所以大概也只能从请求头headers那里拿到protocol信息了。本来想在ctx: &mut Self::Context里找信息的,不过看了看源码似乎并没有找到存取protocol的api,实在整不明白,只能自己存了,不知道还有没有更正统的办法。

代码语言:javascript
复制
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for MyWebSocket {
    fn handle(
        &mut self,
        msg: Result<ws::Message, ws::ProtocolError>,
        ctx: &mut Self::Context,
    ) {

        println!("WS({}): {:?}", self.protocol, msg);

        match msg {
            Ok(ws::Message::Ping(msg)) => ctx.pong(&msg),
            Ok(ws::Message::Pong(_)) => (),
            Ok(ws::Message::Text(text)) => ctx.text(text),
            Ok(ws::Message::Binary(bin)) => ctx.binary(bin),
            Ok(ws::Message::Close(reason)) => {
                ctx.close(reason);
                ctx.stop();
            }
            _ => ctx.stop(),
        }
    }
}

总结一下,关于actix的websocket连接里获取protocol方法就是用start_with_protocols来初始化连接,通过HttpRequest.headers().get(&header::SEC_WEBSOCKET_PROTOCOL).unwrap().to_str().unwrap()来获取protocol,如果想在流处理获取protocol的信息,则需要在处理路由句柄时通过自定义结构体里提前存储protocol的信息。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-12-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Rust语言学习交流 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档