mirror of
https://github.com/actix/examples
synced 2024-11-23 14:31:07 +01:00
upgrade ws examples
This commit is contained in:
parent
e4f71e8fd5
commit
769cc5b84e
@ -2,7 +2,7 @@
|
||||
name = "basics"
|
||||
version = "1.0.0"
|
||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||
workspace = "../"
|
||||
workspace = ".."
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
|
@ -2,23 +2,24 @@
|
||||
name = "websocket-example"
|
||||
version = "0.1.0"
|
||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||
workspace = "../"
|
||||
workspace = ".."
|
||||
edition = "2018"
|
||||
|
||||
[[bin]]
|
||||
name = "websocket-chat-server"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
rand = "*"
|
||||
actix = "0.8.0-alpha.2"
|
||||
actix-web = "1.0.0-alpha.1"
|
||||
actix-web-actors = "1.0.0-alpha.1"
|
||||
actix-files = "0.1.0-alpha.1"
|
||||
|
||||
rand = "0.6"
|
||||
bytes = "0.4"
|
||||
byteorder = "1.1"
|
||||
futures = "0.1"
|
||||
futures = "0.1.25"
|
||||
tokio-io = "0.1"
|
||||
tokio-core = "0.1"
|
||||
env_logger = "*"
|
||||
|
||||
env_logger = "0.6"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
|
||||
actix = "0.7"
|
||||
actix-web = "0.7"
|
||||
|
@ -1,22 +1,9 @@
|
||||
#![allow(unused_variables)]
|
||||
extern crate byteorder;
|
||||
extern crate bytes;
|
||||
extern crate env_logger;
|
||||
extern crate futures;
|
||||
extern crate rand;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
extern crate tokio_core;
|
||||
extern crate tokio_io;
|
||||
|
||||
extern crate actix;
|
||||
extern crate actix_web;
|
||||
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use actix::*;
|
||||
use actix_web::server::HttpServer;
|
||||
use actix_web::{fs, http, ws, App, Error, HttpRequest, HttpResponse};
|
||||
use actix_files as fs;
|
||||
use actix_web::{web, App, Error, HttpRequest, HttpResponse, HttpServer};
|
||||
use actix_web_actors::ws;
|
||||
|
||||
mod server;
|
||||
|
||||
@ -25,22 +12,22 @@ 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);
|
||||
|
||||
/// This is our websocket route state, this state is shared with all route
|
||||
/// instances via `HttpContext::state()`
|
||||
struct WsChatSessionState {
|
||||
addr: Addr<server::ChatServer>,
|
||||
}
|
||||
|
||||
/// Entry point for our route
|
||||
fn chat_route(req: &HttpRequest<WsChatSessionState>) -> Result<HttpResponse, Error> {
|
||||
fn chat_route(
|
||||
req: HttpRequest,
|
||||
stream: web::Payload,
|
||||
srv: web::Data<Addr<server::ChatServer>>,
|
||||
) -> Result<HttpResponse, Error> {
|
||||
ws::start(
|
||||
req,
|
||||
WsChatSession {
|
||||
id: 0,
|
||||
hb: Instant::now(),
|
||||
room: "Main".to_owned(),
|
||||
name: None,
|
||||
addr: srv.get_ref().clone(),
|
||||
},
|
||||
&req,
|
||||
stream,
|
||||
)
|
||||
}
|
||||
|
||||
@ -54,10 +41,12 @@ struct WsChatSession {
|
||||
room: String,
|
||||
/// peer name
|
||||
name: Option<String>,
|
||||
/// Chat server
|
||||
addr: Addr<server::ChatServer>,
|
||||
}
|
||||
|
||||
impl Actor for WsChatSession {
|
||||
type Context = ws::WebsocketContext<Self, WsChatSessionState>;
|
||||
type Context = ws::WebsocketContext<Self>;
|
||||
|
||||
/// Method is called on actor start.
|
||||
/// We register ws session with ChatServer
|
||||
@ -71,8 +60,7 @@ impl Actor for WsChatSession {
|
||||
// HttpContext::state() is instance of WsChatSessionState, state is shared
|
||||
// across all routes within application
|
||||
let addr = ctx.address();
|
||||
ctx.state()
|
||||
.addr
|
||||
self.addr
|
||||
.send(server::Connect {
|
||||
addr: addr.recipient(),
|
||||
})
|
||||
@ -88,9 +76,9 @@ impl Actor for WsChatSession {
|
||||
.wait(ctx);
|
||||
}
|
||||
|
||||
fn stopping(&mut self, ctx: &mut Self::Context) -> Running {
|
||||
fn stopping(&mut self, _: &mut Self::Context) -> Running {
|
||||
// notify chat server
|
||||
ctx.state().addr.do_send(server::Disconnect { id: self.id });
|
||||
self.addr.do_send(server::Disconnect { id: self.id });
|
||||
Running::Stop
|
||||
}
|
||||
}
|
||||
@ -126,8 +114,7 @@ impl StreamHandler<ws::Message, ws::ProtocolError> for WsChatSession {
|
||||
// Send ListRooms message to chat server and wait for
|
||||
// response
|
||||
println!("List rooms");
|
||||
ctx.state()
|
||||
.addr
|
||||
self.addr
|
||||
.send(server::ListRooms)
|
||||
.into_actor(self)
|
||||
.then(|res, _, ctx| {
|
||||
@ -149,7 +136,7 @@ impl StreamHandler<ws::Message, ws::ProtocolError> for WsChatSession {
|
||||
"/join" => {
|
||||
if v.len() == 2 {
|
||||
self.room = v[1].to_owned();
|
||||
ctx.state().addr.do_send(server::Join {
|
||||
self.addr.do_send(server::Join {
|
||||
id: self.id,
|
||||
name: self.room.clone(),
|
||||
});
|
||||
@ -175,14 +162,14 @@ impl StreamHandler<ws::Message, ws::ProtocolError> for WsChatSession {
|
||||
m.to_owned()
|
||||
};
|
||||
// send message to chat server
|
||||
ctx.state().addr.do_send(server::ClientMessage {
|
||||
self.addr.do_send(server::ClientMessage {
|
||||
id: self.id,
|
||||
msg: msg,
|
||||
room: self.room.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
ws::Message::Binary(bin) => println!("Unexpected binary"),
|
||||
ws::Message::Binary(_) => println!("Unexpected binary"),
|
||||
ws::Message::Close(_) => {
|
||||
ctx.stop();
|
||||
}
|
||||
@ -194,7 +181,7 @@ impl WsChatSession {
|
||||
/// helper method that sends ping to client every second.
|
||||
///
|
||||
/// also this method checks heartbeats from client
|
||||
fn hb(&self, ctx: &mut ws::WebsocketContext<Self, WsChatSessionState>) {
|
||||
fn hb(&self, ctx: &mut ws::WebsocketContext<Self>) {
|
||||
ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| {
|
||||
// check client heartbeats
|
||||
if Instant::now().duration_since(act.hb) > CLIENT_TIMEOUT {
|
||||
@ -202,7 +189,7 @@ impl WsChatSession {
|
||||
println!("Websocket Client heartbeat failed, disconnecting!");
|
||||
|
||||
// notify chat server
|
||||
ctx.state().addr.do_send(server::Disconnect { id: act.id });
|
||||
act.addr.do_send(server::Disconnect { id: act.id });
|
||||
|
||||
// stop actor
|
||||
ctx.stop();
|
||||
@ -216,38 +203,30 @@ impl WsChatSession {
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _ = env_logger::init();
|
||||
let sys = actix::System::new("websocket-example");
|
||||
fn main() -> std::io::Result<()> {
|
||||
env_logger::init();
|
||||
let sys = System::new("ws-example");
|
||||
|
||||
// Start chat server actor in separate thread
|
||||
let server = Arbiter::start(|_| server::ChatServer::default());
|
||||
// Start chat server actor
|
||||
let server = server::ChatServer::default().start();
|
||||
|
||||
// Create Http server with websocket support
|
||||
HttpServer::new(move || {
|
||||
// Websocket sessions state
|
||||
let state = WsChatSessionState {
|
||||
addr: server.clone(),
|
||||
};
|
||||
|
||||
App::with_state(state)
|
||||
App::new()
|
||||
.data(server.clone())
|
||||
// redirect to websocket.html
|
||||
.resource("/", |r| {
|
||||
r.method(http::Method::GET).f(|_| {
|
||||
HttpResponse::Found()
|
||||
.header("LOCATION", "/static/websocket.html")
|
||||
.finish()
|
||||
})
|
||||
})
|
||||
.service(web::resource("/").route(web::get().to(|| {
|
||||
HttpResponse::Found()
|
||||
.header("LOCATION", "/static/websocket.html")
|
||||
.finish()
|
||||
})))
|
||||
// websocket
|
||||
.resource("/ws/", |r| r.route().f(chat_route))
|
||||
.service(web::resource("/ws/").to(chat_route))
|
||||
// static resources
|
||||
.handler("/static/", fs::StaticFiles::new("static/").unwrap())
|
||||
.service(fs::Files::new("/static/", "static/"))
|
||||
})
|
||||
.bind("127.0.0.1:8080")
|
||||
.unwrap()
|
||||
.bind("127.0.0.1:8080")?
|
||||
.start();
|
||||
|
||||
println!("Started http server: 127.0.0.1:8080");
|
||||
let _ = sys.run();
|
||||
sys.run()
|
||||
}
|
||||
|
@ -2,7 +2,8 @@
|
||||
name = "websocket-tcp-example"
|
||||
version = "0.1.0"
|
||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||
workspace = "../"
|
||||
workspace = ".."
|
||||
edition = "2018"
|
||||
|
||||
[[bin]]
|
||||
name = "websocket-tcp-server"
|
||||
@ -13,18 +14,19 @@ name = "websocket-tcp-client"
|
||||
path = "src/client.rs"
|
||||
|
||||
[dependencies]
|
||||
rand = "*"
|
||||
actix = "0.8.0-alpha.2"
|
||||
actix-web = "1.0.0-alpha.1"
|
||||
actix-web-actors = "1.0.0-alpha.1"
|
||||
actix-files = "0.1.0-alpha.1"
|
||||
|
||||
rand = "0.6"
|
||||
bytes = "0.4"
|
||||
byteorder = "1.1"
|
||||
futures = "0.1"
|
||||
tokio-io = "0.1"
|
||||
tokio-tcp = "0.1"
|
||||
tokio-codec = "0.1"
|
||||
env_logger = "*"
|
||||
|
||||
env_logger = "0.6"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
serde_derive = "1.0"
|
||||
|
||||
actix = "0.7"
|
||||
actix-web = "0.7"
|
||||
|
@ -1,25 +1,14 @@
|
||||
#![allow(unused_variables)]
|
||||
extern crate byteorder;
|
||||
extern crate bytes;
|
||||
extern crate env_logger;
|
||||
extern crate futures;
|
||||
extern crate rand;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
extern crate tokio_codec;
|
||||
extern crate tokio_io;
|
||||
extern crate tokio_tcp;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
#[macro_use]
|
||||
extern crate actix;
|
||||
extern crate actix_web;
|
||||
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use actix::*;
|
||||
use actix_web::server::HttpServer;
|
||||
use actix_web::{fs, http, ws, App, Error, HttpRequest, HttpResponse};
|
||||
use std::time::{Duration, Instant};
|
||||
use actix_files as fs;
|
||||
use actix_web::{web, App, Error, HttpRequest, HttpResponse, HttpServer};
|
||||
use actix_web_actors::ws;
|
||||
|
||||
mod codec;
|
||||
mod server;
|
||||
@ -30,22 +19,22 @@ 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);
|
||||
|
||||
/// This is our websocket route state, this state is shared with all route
|
||||
/// instances via `HttpContext::state()`
|
||||
struct WsChatSessionState {
|
||||
addr: Addr<server::ChatServer>,
|
||||
}
|
||||
|
||||
/// Entry point for our route
|
||||
fn chat_route(req: &HttpRequest<WsChatSessionState>) -> Result<HttpResponse, Error> {
|
||||
fn chat_route(
|
||||
req: HttpRequest,
|
||||
stream: web::Payload,
|
||||
srv: web::Data<Addr<server::ChatServer>>,
|
||||
) -> Result<HttpResponse, Error> {
|
||||
ws::start(
|
||||
req,
|
||||
WsChatSession {
|
||||
id: 0,
|
||||
hb: Instant::now(),
|
||||
room: "Main".to_owned(),
|
||||
name: None,
|
||||
addr: srv.get_ref().clone(),
|
||||
},
|
||||
&req,
|
||||
stream,
|
||||
)
|
||||
}
|
||||
|
||||
@ -59,26 +48,26 @@ struct WsChatSession {
|
||||
room: String,
|
||||
/// peer name
|
||||
name: Option<String>,
|
||||
/// Chat server
|
||||
addr: Addr<server::ChatServer>,
|
||||
}
|
||||
|
||||
impl Actor for WsChatSession {
|
||||
type Context = ws::WebsocketContext<Self, WsChatSessionState>;
|
||||
type Context = ws::WebsocketContext<Self>;
|
||||
|
||||
/// Method is called on actor start.
|
||||
/// We register ws session with ChatServer
|
||||
fn started(&mut self, ctx: &mut Self::Context) {
|
||||
// we'll start heartbeat process on session start.
|
||||
self.hb(ctx);
|
||||
|
||||
// register self in chat server. `AsyncContext::wait` register
|
||||
// future within context, but context waits until this future resolves
|
||||
// before processing any other events.
|
||||
// HttpContext::state() is instance of WsChatSessionState, state is shared
|
||||
// across all routes within application
|
||||
|
||||
// we'll start heartbeat process on session start.
|
||||
self.hb(ctx);
|
||||
|
||||
let addr = ctx.address();
|
||||
ctx.state()
|
||||
.addr
|
||||
self.addr
|
||||
.send(server::Connect {
|
||||
addr: addr.recipient(),
|
||||
})
|
||||
@ -94,9 +83,9 @@ impl Actor for WsChatSession {
|
||||
.wait(ctx);
|
||||
}
|
||||
|
||||
fn stopping(&mut self, ctx: &mut Self::Context) -> Running {
|
||||
fn stopping(&mut self, _: &mut Self::Context) -> Running {
|
||||
// notify chat server
|
||||
ctx.state().addr.do_send(server::Disconnect { id: self.id });
|
||||
self.addr.do_send(server::Disconnect { id: self.id });
|
||||
Running::Stop
|
||||
}
|
||||
}
|
||||
@ -132,8 +121,7 @@ impl StreamHandler<ws::Message, ws::ProtocolError> for WsChatSession {
|
||||
// Send ListRooms message to chat server and wait for
|
||||
// response
|
||||
println!("List rooms");
|
||||
ctx.state()
|
||||
.addr
|
||||
self.addr
|
||||
.send(server::ListRooms)
|
||||
.into_actor(self)
|
||||
.then(|res, _, ctx| {
|
||||
@ -155,7 +143,7 @@ impl StreamHandler<ws::Message, ws::ProtocolError> for WsChatSession {
|
||||
"/join" => {
|
||||
if v.len() == 2 {
|
||||
self.room = v[1].to_owned();
|
||||
ctx.state().addr.do_send(server::Join {
|
||||
self.addr.do_send(server::Join {
|
||||
id: self.id,
|
||||
name: self.room.clone(),
|
||||
});
|
||||
@ -181,14 +169,14 @@ impl StreamHandler<ws::Message, ws::ProtocolError> for WsChatSession {
|
||||
m.to_owned()
|
||||
};
|
||||
// send message to chat server
|
||||
ctx.state().addr.do_send(server::Message {
|
||||
self.addr.do_send(server::Message {
|
||||
id: self.id,
|
||||
msg: msg,
|
||||
room: self.room.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
ws::Message::Binary(bin) => println!("Unexpected binary"),
|
||||
ws::Message::Binary(_) => println!("Unexpected binary"),
|
||||
ws::Message::Close(_) => {
|
||||
ctx.stop();
|
||||
}
|
||||
@ -200,7 +188,7 @@ impl WsChatSession {
|
||||
/// helper method that sends ping to client every second.
|
||||
///
|
||||
/// also this method checks heartbeats from client
|
||||
fn hb(&self, ctx: &mut ws::WebsocketContext<Self, WsChatSessionState>) {
|
||||
fn hb(&self, ctx: &mut ws::WebsocketContext<Self>) {
|
||||
ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| {
|
||||
// check client heartbeats
|
||||
if Instant::now().duration_since(act.hb) > CLIENT_TIMEOUT {
|
||||
@ -208,7 +196,7 @@ impl WsChatSession {
|
||||
println!("Websocket Client heartbeat failed, disconnecting!");
|
||||
|
||||
// notify chat server
|
||||
ctx.state().addr.do_send(server::Disconnect { id: act.id });
|
||||
act.addr.do_send(server::Disconnect { id: act.id });
|
||||
|
||||
// stop actor
|
||||
ctx.stop();
|
||||
@ -222,45 +210,38 @@ impl WsChatSession {
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
fn main() -> std::io::Result<()> {
|
||||
let _ = env_logger::init();
|
||||
let sys = actix::System::new("websocket-example");
|
||||
|
||||
// Start chat server actor in separate thread
|
||||
let server = Arbiter::start(|_| server::ChatServer::default());
|
||||
// Start chat server actor
|
||||
let server = server::ChatServer::default().start();
|
||||
|
||||
// Start tcp server in separate thread
|
||||
let srv = server.clone();
|
||||
Arbiter::new("tcp-server").do_send::<msgs::Execute>(msgs::Execute::new(move || {
|
||||
Arbiter::new().exec(move || {
|
||||
session::TcpServer::new("127.0.0.1:12345", srv);
|
||||
Ok(())
|
||||
}));
|
||||
Ok::<_, ()>(())
|
||||
});
|
||||
|
||||
// Create Http server with websocket support
|
||||
HttpServer::new(move || {
|
||||
// Websocket sessions state
|
||||
let state = WsChatSessionState {
|
||||
addr: server.clone(),
|
||||
};
|
||||
|
||||
App::with_state(state)
|
||||
App::new()
|
||||
.data(server.clone())
|
||||
// redirect to websocket.html
|
||||
.resource("/", |r| {
|
||||
r.method(http::Method::GET).f(|_| {
|
||||
HttpResponse::Found()
|
||||
.header("LOCATION", "/static/websocket.html")
|
||||
.finish()
|
||||
})
|
||||
})
|
||||
.service(web::resource("/").route(web::get().to(|| {
|
||||
HttpResponse::Found()
|
||||
.header("LOCATION", "/static/websocket.html")
|
||||
.finish()
|
||||
})))
|
||||
// websocket
|
||||
.resource("/ws/", |r| r.route().f(chat_route))
|
||||
.service(web::resource("/ws/").to(chat_route))
|
||||
// static resources
|
||||
.handler("/static/", fs::StaticFiles::new("static/").unwrap())
|
||||
.service(fs::Files::new("/static/", "static/"))
|
||||
})
|
||||
.bind("127.0.0.1:8080")
|
||||
.unwrap()
|
||||
.bind("127.0.0.1:8080")?
|
||||
.start();
|
||||
|
||||
println!("Started http server: 127.0.0.1:8080");
|
||||
let _ = sys.run();
|
||||
sys.run()
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use actix::prelude::*;
|
||||
use rand::{self, rngs::ThreadRng, Rng};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use session;
|
||||
use crate::session;
|
||||
|
||||
/// Message for chat server communications
|
||||
|
||||
|
@ -11,8 +11,8 @@ use tokio_tcp::{TcpListener, TcpStream};
|
||||
|
||||
use actix::prelude::*;
|
||||
|
||||
use codec::{ChatCodec, ChatRequest, ChatResponse};
|
||||
use server::{self, ChatServer};
|
||||
use crate::codec::{ChatCodec, ChatRequest, ChatResponse};
|
||||
use crate::server::{self, ChatServer};
|
||||
|
||||
/// Chat server sends this messages to session
|
||||
#[derive(Message)]
|
||||
@ -62,7 +62,7 @@ impl Actor for ChatSession {
|
||||
.wait(ctx);
|
||||
}
|
||||
|
||||
fn stopping(&mut self, ctx: &mut Self::Context) -> Running {
|
||||
fn stopping(&mut self, _: &mut Self::Context) -> Running {
|
||||
// notify chat server
|
||||
self.addr.do_send(server::Disconnect { id: self.id });
|
||||
Running::Stop
|
||||
@ -82,7 +82,7 @@ impl StreamHandler<ChatRequest, io::Error> for ChatSession {
|
||||
self.addr
|
||||
.send(server::ListRooms)
|
||||
.into_actor(self)
|
||||
.then(|res, act, ctx| {
|
||||
.then(|res, act, _| {
|
||||
match res {
|
||||
Ok(rooms) => {
|
||||
act.framed.write(ChatResponse::Rooms(rooms));
|
||||
@ -124,7 +124,7 @@ impl StreamHandler<ChatRequest, io::Error> for ChatSession {
|
||||
impl Handler<Message> for ChatSession {
|
||||
type Result = ();
|
||||
|
||||
fn handle(&mut self, msg: Message, ctx: &mut Context<Self>) {
|
||||
fn handle(&mut self, msg: Message, _: &mut Context<Self>) {
|
||||
// send message to peer
|
||||
self.framed.write(ChatResponse::Message(msg.0));
|
||||
}
|
||||
@ -175,7 +175,7 @@ pub struct TcpServer {
|
||||
}
|
||||
|
||||
impl TcpServer {
|
||||
pub fn new(s: &str, chat: Addr<ChatServer>) {
|
||||
pub fn new(_s: &str, chat: Addr<ChatServer>) {
|
||||
// Create server listener
|
||||
let addr = net::SocketAddr::from_str("127.0.0.1:12345").unwrap();
|
||||
let listener = TcpListener::bind(&addr).unwrap();
|
||||
|
Loading…
Reference in New Issue
Block a user