Asynchronous 如何在Actix web中正确调用WebSocket处理程序中的异步函数
我在这方面取得了一些进展,使用了Asynchronous 如何在Actix web中正确调用WebSocket处理程序中的异步函数,asynchronous,websocket,rust,actix-web,Asynchronous,Websocket,Rust,Actix Web,我在这方面取得了一些进展,使用了到\u actor().spawn(),但是我正在努力访问异步块中的ctx变量 我将首先显示web套接字处理程序的编译片段,然后是处理程序的失败片段,然后是完整的代码示例以供参考 工作代码段: 关注匹配案例Ok(ws::Message::Text(Text)) 完整代码段拆分为两个文件。 梅因 这里是基本的。你可能需要在这里和那里做一些工作,但这是有效的 use actix::prelude::*; use tokio::process::Command; use
到\u actor().spawn()
,但是我正在努力访问异步块中的ctx
变量
我将首先显示web套接字处理程序的编译片段,然后是处理程序的失败片段,然后是完整的代码示例以供参考
工作代码段:
关注匹配案例Ok(ws::Message::Text(Text))
完整代码段拆分为两个文件。
梅因
这里是基本的。你可能需要在这里和那里做一些工作,但这是有效的
use actix::prelude::*;
use tokio::process::Command;
use actix_web::{ web, App, Error, HttpRequest, HttpResponse, HttpServer};
use actix_web_actors::ws;
use tokio::io::{ AsyncBufReadExt};
use actix::AsyncContext;
use tokio::stream::{ StreamExt};
use tokio::io::{BufReader};
use std::process::Stdio;
#[derive(Message)]
#[rtype(result = "Result<(), ()>")]
struct CommandRunner(String);
/// Define HTTP actor
struct MyWs;
impl Actor for MyWs {
type Context = ws::WebsocketContext<Self>;
}
#[derive(Debug)]
struct Line(String);
impl StreamHandler<Result<Line, ws::ProtocolError>> for MyWs {
fn handle(
&mut self,
msg: Result<Line, ws::ProtocolError>,
ctx: &mut Self::Context,
) {
match msg {
Ok(line) => ctx.text(line.0),
_ => () //Handle errors
}
}
}
/// Handler for ws::Message message
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for MyWs {
fn handle(
&mut self,
msg: Result<ws::Message, ws::ProtocolError>,
ctx: &mut Self::Context,
) {
match msg {
Ok(ws::Message::Ping(msg)) => ctx.pong(&msg),
Ok(ws::Message::Text(text)) => {
ctx.notify(CommandRunner(text.to_string()));
},
Ok(ws::Message::Binary(bin)) => ctx.binary(bin),
_ => (),
}
}
}
impl Handler<CommandRunner> for MyWs {
type Result = Result<(), ()>;
fn handle(&mut self, msg: CommandRunner, ctx: &mut Self::Context) -> Self::Result {
let mut cmd = Command::new(msg.0);
// Specify that we want the command's standard output piped back to us.
// By default, standard input/output/error will be inherited from the
// current process (for example, this means that standard input will
// come from the keyboard and standard output/error will go directly to
// the terminal if this process is invoked from the command line).
cmd.stdout(Stdio::piped());
let mut child = cmd.spawn()
.expect("failed to spawn command");
let stdout = child.stdout.take()
.expect("child did not have a handle to stdout");
let reader = BufReader::new(stdout).lines();
// Ensure the child process is spawned in the runtime so it can
// make progress on its own while we await for any output.
let fut = async move {
let status = child.await
.expect("child process encountered an error");
println!("child status was: {}", status);
};
let fut = actix::fut::wrap_future::<_, Self>(fut);
ctx.spawn(fut);
ctx.add_stream(reader.map(|l| Ok(Line(l.expect("Not a line")))));
Ok(())
}
}
async fn index(req: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {
let resp = ws::start(MyWs {}, &req, stream);
println!("{:?}", resp);
resp
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| App::new().route("/ws/", web::get().to(index)))
.bind("127.0.0.1:8080")?
.run()
.await
}
使用actix::prelude::*;
使用tokio::process::命令;
使用actix_web::{web,App,Error,HttpRequest,HttpResponse,HttpServer};
使用actix\u web\u actors::ws;
使用tokio::io::{AsyncBufReadExt};
使用actix::AsyncContext;
使用tokio::stream::{StreamExt};
使用tokio::io::{BufReader};
使用std::process::Stdio;
#[导出(消息)]
#[rtype(result=“result”)]
结构命令运行程序(字符串);
///定义HTTP参与者
结构MyWs;
MyWs的impl-Actor{
类型Context=ws::WebsocketContext;
}
#[导出(调试)]
结构行(字符串);
MyWs的impl StreamHandler{
fn手柄(
&莫特·赛尔夫,
msg:结果,,
ctx:&mut Self::Context,
) {
配味精{
Ok(行)=>ctx.text(行0),
_=>()//处理错误
}
}
}
///ws::Message的处理程序
MyWs的impl StreamHandler{
fn手柄(
&莫特·赛尔夫,
msg:结果,,
ctx:&mut Self::Context,
) {
配味精{
Ok(ws::Message::Ping(msg))=>ctx.pong(&msg),
确定(ws::Message::Text(Text))=>{
通知(CommandRunner(text.to_string());
},
Ok(ws::Message::Binary(bin))=>ctx.Binary(bin),
_ => (),
}
}
}
MyWs的impl处理程序{
类型结果=结果;
fn handle(&mut self,msg:CommandRunner,ctx:&mut self::Context)->self::Result{
让mut cmd=Command::new(msg.0);
//指定我们希望命令的标准输出通过管道传回给我们。
//默认情况下,标准输入/输出/错误将从
//当前流程(例如,这意味着标准输入将
//来自键盘和标准输出/错误将直接转到
//终端(如果从命令行调用此进程)。
cmd.stdout(Stdio::piped());
让mut child=cmd.spawn()
.expect(“生成命令失败”);
让stdout=child.stdout.take()
.expect(“孩子没有stdout的句柄”);
让reader=BufReader::new(stdout.lines();
//确保在运行时生成子进程,以便
//在我们等待任何产出的同时,自己取得进展。
让fut=异步移动{
让status=child.wait
.expect(“子进程遇到错误”);
println!(“子状态为:{}”,状态);
};
让fut=actix::fut::wrap_future::(fut);
ctx.spawn(fut);
ctx.add_-stream(reader.map(| l | Ok(Line)(l.expect(“nota Line”))));
好(())
}
}
异步fn索引(req:HttpRequest,stream:web::Payload)->结果{
让resp=ws::start(MyWs{},&req,stream);
println!(“{:?}”,resp);
响应
}
#[actix_web::main]
异步fn main()->std::io::Result{
HttpServer::new(| | App::new().route(“/ws/”,web::get().to(index)))
.bind(“127.0.0.1:8080”)?
.run()
.等待
}
运行ls
看起来是这样的
因此,在我找到公认答案的同时,我才明白出了什么问题 被接受的答案提出了一个干净的解决方案,但我认为我会提出另一种观点,我在下面提出的代码片段对我最初的尝试(如问题所示)进行了较少的更改,希望它能证明我的基本理解 我的代码的根本问题是我忽略了“每个参与者都有自己的上下文”这一规则。正如您从问题中的编译错误中所看到的,幸运的是,Actix使用rust编译器来强制执行此规则 现在我明白了这一点,看起来我试图做的错误事情是生成另一个参与者,并让该参与者在原始参与者的上下文中进行移动/复制,这样它就可以用流程输出行进行响应。当然,没有必要这样做,因为Actor模型就是让Actor通过消息进行通信 相反,在生成新的参与者时,我应该将原始参与者的地址传递给它,允许新生成的参与者将更新发送回。原始参与者使用处理程序处理这些消息(
struct Line
)
正如我所说的,公认的答案也可以做到这一点,但使用的映射器看起来比我的循环更优雅
mod processrunner;
use std::time::{Duration, Instant};
use actix::prelude::*;
use actix_files as fs;
use actix_web::{middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer};
use actix_web_actors::ws;
/// How often heartbeat pings are sent
const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5);
/// How long before lack of client response causes a timeout
const CLIENT_TIMEOUT: Duration = Duration::from_secs(10);
/// do websocket handshake and start `MyWebSocket` actor
async fn ws_index(r: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {
println!("{:?}", r);
let res = ws::start(MyWebSocket::new(), &r, stream);
println!("{:?}", res);
res
}
/// websocket connection is long running connection, it easier
/// to handle with an actor
struct MyWebSocket {
/// Client must send ping at least once per 10 seconds (CLIENT_TIMEOUT),
/// otherwise we drop connection.
hb: Instant,
}
impl Actor for MyWebSocket {
type Context = ws::WebsocketContext<Self>;
/// Method is called on actor start. We start the heartbeat process here.
fn started(&mut self, ctx: &mut Self::Context) {
self.hb(ctx);
}
}
#[derive(Message)]
#[rtype(result = "()")]
pub struct Line {
line: String,
}
impl Handler<Line> for MyWebSocket {
type Result = ();
fn handle(&mut self, msg: Line, ctx: &mut Self::Context) {
ctx.text(msg.line);
}
}
/// Handler for `ws::Message`
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for MyWebSocket {
fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
// process websocket messages
println!("WS: {:?}", msg);
match msg {
Ok(ws::Message::Ping(msg)) => {
self.hb = Instant::now();
ctx.pong(&msg);
}
Ok(ws::Message::Pong(_)) => {
self.hb = Instant::now();
}
Ok(ws::Message::Text(text)) => {
let recipient = ctx.address().recipient();
let future = async move {
let reader = processrunner::run_process(text).await;
let mut reader = reader.ok().unwrap();
while let Some(line) = reader.next_line().await.unwrap() {
println!("line = {}", line);
recipient.do_send(Line { line });
}
};
future.into_actor(self).spawn(ctx);
}
Ok(ws::Message::Binary(bin)) => ctx.binary(bin),
Ok(ws::Message::Close(reason)) => {
ctx.close(reason);
ctx.stop();
}
_ => ctx.stop(),
}
}
}
impl MyWebSocket {
fn new() -> Self {
Self { hb: Instant::now() }
}
/// helper method that sends ping to client every second.
///
/// also this method checks heartbeats from client
fn hb(&self, ctx: &mut <Self as Actor>::Context) {
ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| {
// check client heartbeats
if Instant::now().duration_since(act.hb) > CLIENT_TIMEOUT {
// heartbeat timed out
println!("Websocket Client heartbeat failed, disconnecting!");
// stop actor
ctx.stop();
// don't try to send a ping
return;
}
ctx.ping(b"");
});
}
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
std::env::set_var("RUST_LOG", "actix_server=info,actix_web=info");
env_logger::init();
HttpServer::new(|| {
App::new()
// enable logger
.wrap(middleware::Logger::default())
// websocket route
.service(web::resource("/ws/").route(web::get().to(ws_index)))
// static files
.service(fs::Files::new("/", "static/").index_file("index.html"))
})
// start http server on 127.0.0.1:8080
.bind("127.0.0.1:8080")?
.run()
.await
}
modprocessrunner;
使用std::time::{Duration,Instant};
使用actix::prelude::*;
使用actix_文件作为fs;
使用actix_web::{中间件、web、应用程序、错误、HttpRequest、HttpResponse、HttpServer};
使用actix\u web\u actors::ws;
///发送心跳ping的频率
常量心跳间隔:持续时间=持续时间::从秒(5);
///在缺少客户端响应导致超时之前多长时间
const CLIENT_TIMEOUT:Duration=Duration::from_secs(10);
///进行websocket握手并启动'MyWebSocket'actor
异步fn ws_索引(r:HttpRequest,stream:web::Payload)->Result{
println!(“{:?}”,r);
让res=ws::start(MyWebSocket::new(),&r,stream);
println!(“{:?}”,res);
物件
}
///websocket连接是长时间运行的连接,更容易
///与演员打交道
结构MyWebSocket{
///客户端必须至少每10秒发送一次ping(客户端超时),
///否则我们就断开连接。
hb:瞬间,
}
MyWebSocket的impl Actor{
类型Context=ws::WebsocketContext;
///方法被调用
//! Simple echo websocket server.
//! Open `http://localhost:8080/ws/index.html` in browser
//! or [python console client](https://github.com/actix/examples/blob/master/websocket/websocket-client.py)
//! could be used for testing.
mod processrunner;
use std::time::{Duration, Instant};
use actix::prelude::*;
use actix_files as fs;
use actix_web::{middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer};
use actix_web_actors::ws;
/// How often heartbeat pings are sent
const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5);
/// How long before lack of client response causes a timeout
const CLIENT_TIMEOUT: Duration = Duration::from_secs(10);
/// do websocket handshake and start `MyWebSocket` actor
async fn ws_index(r: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {
println!("{:?}", r);
let res = ws::start(MyWebSocket::new(), &r, stream);
println!("{:?}", res);
res
}
/// websocket connection is long running connection, it easier
/// to handle with an actor
struct MyWebSocket {
/// Client must send ping at least once per 10 seconds (CLIENT_TIMEOUT),
/// otherwise we drop connection.
hb: Instant,
}
impl Actor for MyWebSocket {
type Context = ws::WebsocketContext<Self>;
/// Method is called on actor start. We start the heartbeat process here.
fn started(&mut self, ctx: &mut Self::Context) {
self.hb(ctx);
}
}
/// Handler for `ws::Message`
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for MyWebSocket {
fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
// process websocket messages
println!("WS: {:?}", msg);
match msg {
Ok(ws::Message::Ping(msg)) => {
self.hb = Instant::now();
ctx.pong(&msg);
}
Ok(ws::Message::Pong(_)) => {
self.hb = Instant::now();
}
Ok(ws::Message::Text(text)) => {
let future = async move {
let reader = processrunner::run_process(text).await;
let mut reader = reader.ok().unwrap();
while let Some(line) = reader.next_line().await.unwrap() {
// ctx.text(line);
println!("line = {}", line);
}
};
future.into_actor(self).spawn(ctx);
}
Ok(ws::Message::Binary(bin)) => ctx.binary(bin),
Ok(ws::Message::Close(reason)) => {
ctx.close(reason);
ctx.stop();
}
_ => ctx.stop(),
}
}
}
impl MyWebSocket {
fn new() -> Self {
Self { hb: Instant::now() }
}
/// helper method that sends ping to client every second.
///
/// also this method checks heartbeats from client
fn hb(&self, ctx: &mut <Self as Actor>::Context) {
ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| {
// check client heartbeats
if Instant::now().duration_since(act.hb) > CLIENT_TIMEOUT {
// heartbeat timed out
println!("Websocket Client heartbeat failed, disconnecting!");
// stop actor
ctx.stop();
// don't try to send a ping
return;
}
ctx.ping(b"");
});
}
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
std::env::set_var("RUST_LOG", "actix_server=info,actix_web=info");
env_logger::init();
HttpServer::new(|| {
App::new()
// enable logger
.wrap(middleware::Logger::default())
// websocket route
.service(web::resource("/ws/").route(web::get().to(ws_index)))
// static files
.service(fs::Files::new("/", "static/").index_file("index.html"))
})
// start http server on 127.0.0.1:8080
.bind("127.0.0.1:8080")?
.run()
.await
}
extern crate tokio;
use tokio::io::*;
use tokio::process::Command;
use std::process::Stdio;
//#[tokio::main]
pub async fn run_process(
text: String,
) -> std::result::Result<
tokio::io::Lines<BufReader<tokio::process::ChildStdout>>,
Box<dyn std::error::Error>,
> {
let mut cmd = Command::new(text);
cmd.stdout(Stdio::piped());
let mut child = cmd.spawn().expect("failed to spawn command");
let stdout = child
.stdout
.take()
.expect("child did not have a handle to stdout");
let lines = BufReader::new(stdout).lines();
// Ensure the child process is spawned in the runtime so it can
// make progress on its own while we await for any output.
tokio::spawn(async {
let status = child.await.expect("child process encountered an error");
println!("child status was: {}", status);
});
Ok(lines)
}
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/main.rs:57:41
|
57 | let future = async move {
| _________________________________________^
58 | | let reader = processrunner::run_process(text).await;
59 | | let mut reader = reader.ok().unwrap();
60 | | while let Some(line) = reader.next_line().await.unwrap() {
... |
63 | | }
64 | | };
| |_________________^
|
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the method body at 45:5...
--> src/main.rs:45:5
|
45 | / fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
46 | | // process websocket messages
47 | | println!("WS: {:?}", msg);
48 | | match msg {
... |
74 | | }
75 | | }
| |_____^
note: ...so that the types are compatible
--> src/main.rs:57:41
|
57 | let future = async move {
| _________________________________________^
58 | | let reader = processrunner::run_process(text).await;
59 | | let mut reader = reader.ok().unwrap();
60 | | while let Some(line) = reader.next_line().await.unwrap() {
... |
63 | | }
64 | | };
| |_________________^
= note: expected `&mut actix_web_actors::ws::WebsocketContext<MyWebSocket>`
found `&mut actix_web_actors::ws::WebsocketContext<MyWebSocket>`
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `actix::fut::FutureWrap<impl std::future::Future, MyWebSocket>` will meet its required lifetime bounds
--> src/main.rs:66:41
|
66 | future.into_actor(self).spawn(ctx);
| ^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0495`.
[package]
name = "removed"
version = "0.1.0"
authors = ["removed"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
tokio = { version = "0.2", features = ["full"] }
actix = "0.10"
actix-codec = "0.3"
actix-web = "3"
actix-web-actors = "3"
actix-files = "0.3"
awc = "2"
env_logger = "0.7"
futures = "0.3.1"
bytes = "0.5.3"
use actix::prelude::*;
use tokio::process::Command;
use actix_web::{ web, App, Error, HttpRequest, HttpResponse, HttpServer};
use actix_web_actors::ws;
use tokio::io::{ AsyncBufReadExt};
use actix::AsyncContext;
use tokio::stream::{ StreamExt};
use tokio::io::{BufReader};
use std::process::Stdio;
#[derive(Message)]
#[rtype(result = "Result<(), ()>")]
struct CommandRunner(String);
/// Define HTTP actor
struct MyWs;
impl Actor for MyWs {
type Context = ws::WebsocketContext<Self>;
}
#[derive(Debug)]
struct Line(String);
impl StreamHandler<Result<Line, ws::ProtocolError>> for MyWs {
fn handle(
&mut self,
msg: Result<Line, ws::ProtocolError>,
ctx: &mut Self::Context,
) {
match msg {
Ok(line) => ctx.text(line.0),
_ => () //Handle errors
}
}
}
/// Handler for ws::Message message
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for MyWs {
fn handle(
&mut self,
msg: Result<ws::Message, ws::ProtocolError>,
ctx: &mut Self::Context,
) {
match msg {
Ok(ws::Message::Ping(msg)) => ctx.pong(&msg),
Ok(ws::Message::Text(text)) => {
ctx.notify(CommandRunner(text.to_string()));
},
Ok(ws::Message::Binary(bin)) => ctx.binary(bin),
_ => (),
}
}
}
impl Handler<CommandRunner> for MyWs {
type Result = Result<(), ()>;
fn handle(&mut self, msg: CommandRunner, ctx: &mut Self::Context) -> Self::Result {
let mut cmd = Command::new(msg.0);
// Specify that we want the command's standard output piped back to us.
// By default, standard input/output/error will be inherited from the
// current process (for example, this means that standard input will
// come from the keyboard and standard output/error will go directly to
// the terminal if this process is invoked from the command line).
cmd.stdout(Stdio::piped());
let mut child = cmd.spawn()
.expect("failed to spawn command");
let stdout = child.stdout.take()
.expect("child did not have a handle to stdout");
let reader = BufReader::new(stdout).lines();
// Ensure the child process is spawned in the runtime so it can
// make progress on its own while we await for any output.
let fut = async move {
let status = child.await
.expect("child process encountered an error");
println!("child status was: {}", status);
};
let fut = actix::fut::wrap_future::<_, Self>(fut);
ctx.spawn(fut);
ctx.add_stream(reader.map(|l| Ok(Line(l.expect("Not a line")))));
Ok(())
}
}
async fn index(req: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {
let resp = ws::start(MyWs {}, &req, stream);
println!("{:?}", resp);
resp
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| App::new().route("/ws/", web::get().to(index)))
.bind("127.0.0.1:8080")?
.run()
.await
}
mod processrunner;
use std::time::{Duration, Instant};
use actix::prelude::*;
use actix_files as fs;
use actix_web::{middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer};
use actix_web_actors::ws;
/// How often heartbeat pings are sent
const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5);
/// How long before lack of client response causes a timeout
const CLIENT_TIMEOUT: Duration = Duration::from_secs(10);
/// do websocket handshake and start `MyWebSocket` actor
async fn ws_index(r: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {
println!("{:?}", r);
let res = ws::start(MyWebSocket::new(), &r, stream);
println!("{:?}", res);
res
}
/// websocket connection is long running connection, it easier
/// to handle with an actor
struct MyWebSocket {
/// Client must send ping at least once per 10 seconds (CLIENT_TIMEOUT),
/// otherwise we drop connection.
hb: Instant,
}
impl Actor for MyWebSocket {
type Context = ws::WebsocketContext<Self>;
/// Method is called on actor start. We start the heartbeat process here.
fn started(&mut self, ctx: &mut Self::Context) {
self.hb(ctx);
}
}
#[derive(Message)]
#[rtype(result = "()")]
pub struct Line {
line: String,
}
impl Handler<Line> for MyWebSocket {
type Result = ();
fn handle(&mut self, msg: Line, ctx: &mut Self::Context) {
ctx.text(msg.line);
}
}
/// Handler for `ws::Message`
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for MyWebSocket {
fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
// process websocket messages
println!("WS: {:?}", msg);
match msg {
Ok(ws::Message::Ping(msg)) => {
self.hb = Instant::now();
ctx.pong(&msg);
}
Ok(ws::Message::Pong(_)) => {
self.hb = Instant::now();
}
Ok(ws::Message::Text(text)) => {
let recipient = ctx.address().recipient();
let future = async move {
let reader = processrunner::run_process(text).await;
let mut reader = reader.ok().unwrap();
while let Some(line) = reader.next_line().await.unwrap() {
println!("line = {}", line);
recipient.do_send(Line { line });
}
};
future.into_actor(self).spawn(ctx);
}
Ok(ws::Message::Binary(bin)) => ctx.binary(bin),
Ok(ws::Message::Close(reason)) => {
ctx.close(reason);
ctx.stop();
}
_ => ctx.stop(),
}
}
}
impl MyWebSocket {
fn new() -> Self {
Self { hb: Instant::now() }
}
/// helper method that sends ping to client every second.
///
/// also this method checks heartbeats from client
fn hb(&self, ctx: &mut <Self as Actor>::Context) {
ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| {
// check client heartbeats
if Instant::now().duration_since(act.hb) > CLIENT_TIMEOUT {
// heartbeat timed out
println!("Websocket Client heartbeat failed, disconnecting!");
// stop actor
ctx.stop();
// don't try to send a ping
return;
}
ctx.ping(b"");
});
}
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
std::env::set_var("RUST_LOG", "actix_server=info,actix_web=info");
env_logger::init();
HttpServer::new(|| {
App::new()
// enable logger
.wrap(middleware::Logger::default())
// websocket route
.service(web::resource("/ws/").route(web::get().to(ws_index)))
// static files
.service(fs::Files::new("/", "static/").index_file("index.html"))
})
// start http server on 127.0.0.1:8080
.bind("127.0.0.1:8080")?
.run()
.await
}