mirror of
https://github.com/actix/examples
synced 2025-02-20 08:20:32 +01:00
Merge pull request #42 from botika/master
fix missing websocket heartbeat and python client in websocket-tcp-chat
This commit is contained in:
commit
d653fe4822
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""websocket cmd client for wssrv.py example."""
|
"""websocket cmd client for actix/websocket-tcp-chat example."""
|
||||||
import argparse
|
import argparse
|
||||||
import asyncio
|
import asyncio
|
||||||
import signal
|
import signal
|
||||||
@ -7,46 +7,54 @@ import sys
|
|||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
|
||||||
|
queue = asyncio.Queue()
|
||||||
|
|
||||||
def start_client(loop, url):
|
|
||||||
|
async def start_client(url, loop):
|
||||||
name = input('Please enter your name: ')
|
name = input('Please enter your name: ')
|
||||||
|
|
||||||
# send request
|
ws = await aiohttp.ClientSession().ws_connect(url, autoclose=False, autoping=False)
|
||||||
ws = yield from aiohttp.ClientSession().ws_connect(url, autoclose=False, autoping=False)
|
|
||||||
|
|
||||||
# input reader
|
|
||||||
def stdin_callback():
|
def stdin_callback():
|
||||||
line = sys.stdin.buffer.readline().decode('utf-8')
|
line = sys.stdin.buffer.readline().decode('utf-8')
|
||||||
if not line:
|
if not line:
|
||||||
loop.stop()
|
loop.stop()
|
||||||
else:
|
else:
|
||||||
ws.send_str(name + ': ' + line)
|
# Queue.put is a coroutine, so you can't call it directly.
|
||||||
loop.add_reader(sys.stdin.fileno(), stdin_callback)
|
asyncio.ensure_future(queue.put(ws.send_str(name + ': ' + line)))
|
||||||
|
|
||||||
@asyncio.coroutine
|
loop.add_reader(sys.stdin, stdin_callback)
|
||||||
def dispatch():
|
|
||||||
|
async def dispatch():
|
||||||
while True:
|
while True:
|
||||||
msg = yield from ws.receive()
|
msg = await ws.receive()
|
||||||
|
|
||||||
if msg.type == aiohttp.WSMsgType.TEXT:
|
if msg.type == aiohttp.WSMsgType.TEXT:
|
||||||
print('Text: ', msg.data.strip())
|
print('Text: ', msg.data.strip())
|
||||||
elif msg.type == aiohttp.WSMsgType.BINARY:
|
elif msg.type == aiohttp.WSMsgType.BINARY:
|
||||||
print('Binary: ', msg.data)
|
print('Binary: ', msg.data)
|
||||||
elif msg.type == aiohttp.WSMsgType.PING:
|
elif msg.type == aiohttp.WSMsgType.PING:
|
||||||
ws.pong()
|
await ws.pong()
|
||||||
elif msg.type == aiohttp.WSMsgType.PONG:
|
elif msg.type == aiohttp.WSMsgType.PONG:
|
||||||
print('Pong received')
|
print('Pong received')
|
||||||
else:
|
else:
|
||||||
if msg.type == aiohttp.WSMsgType.CLOSE:
|
if msg.type == aiohttp.WSMsgType.CLOSE:
|
||||||
yield from ws.close()
|
await ws.close()
|
||||||
elif msg.type == aiohttp.WSMsgType.ERROR:
|
elif msg.type == aiohttp.WSMsgType.ERROR:
|
||||||
print('Error during receive %s' % ws.exception())
|
print('Error during receive %s' % ws.exception())
|
||||||
elif msg.type == aiohttp.WSMsgType.CLOSED:
|
elif msg.type == aiohttp.WSMsgType.CLOSED:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
yield from dispatch()
|
await dispatch()
|
||||||
|
|
||||||
|
|
||||||
|
async def tick():
|
||||||
|
while True:
|
||||||
|
await (await queue.get())
|
||||||
|
|
||||||
|
|
||||||
|
async def main(url, loop):
|
||||||
|
await asyncio.wait([start_client(url, loop), tick()])
|
||||||
|
|
||||||
|
|
||||||
ARGS = argparse.ArgumentParser(
|
ARGS = argparse.ArgumentParser(
|
||||||
@ -68,5 +76,5 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
loop.add_signal_handler(signal.SIGINT, loop.stop)
|
loop.add_signal_handler(signal.SIGINT, loop.stop)
|
||||||
asyncio.Task(start_client(loop, url))
|
asyncio.Task(main(url, loop))
|
||||||
loop.run_forever()
|
loop.run_forever()
|
||||||
|
@ -21,6 +21,7 @@ use std::time::Instant;
|
|||||||
use actix::*;
|
use actix::*;
|
||||||
use actix_web::server::HttpServer;
|
use actix_web::server::HttpServer;
|
||||||
use actix_web::{fs, http, ws, App, Error, HttpRequest, HttpResponse};
|
use actix_web::{fs, http, ws, App, Error, HttpRequest, HttpResponse};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
mod codec;
|
mod codec;
|
||||||
mod server;
|
mod server;
|
||||||
@ -68,6 +69,10 @@ impl Actor for WsChatSession {
|
|||||||
// before processing any other events.
|
// before processing any other events.
|
||||||
// HttpContext::state() is instance of WsChatSessionState, state is shared
|
// HttpContext::state() is instance of WsChatSessionState, state is shared
|
||||||
// across all routes within application
|
// across all routes within application
|
||||||
|
|
||||||
|
// we'll start heartbeat process on session start.
|
||||||
|
self.hb(ctx);
|
||||||
|
|
||||||
let addr = ctx.address();
|
let addr = ctx.address();
|
||||||
ctx.state()
|
ctx.state()
|
||||||
.addr
|
.addr
|
||||||
@ -183,6 +188,31 @@ impl StreamHandler<ws::Message, ws::ProtocolError> for WsChatSession {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl WsChatSession {
|
||||||
|
/// helper method that sends ping to client every second.
|
||||||
|
///
|
||||||
|
/// also this method check heartbeats from client
|
||||||
|
fn hb(&self, ctx: &mut ws::WebsocketContext<Self, WsChatSessionState>) {
|
||||||
|
ctx.run_interval(Duration::new(1, 0), |act, ctx| {
|
||||||
|
// check client heartbeats
|
||||||
|
if Instant::now().duration_since(act.hb) > Duration::new(10, 0) {
|
||||||
|
// heartbeat timed out
|
||||||
|
println!("Websocket Client heartbeat failed, disconnecting!");
|
||||||
|
|
||||||
|
// notify chat server
|
||||||
|
ctx.state()
|
||||||
|
.addr
|
||||||
|
.do_send(server::Disconnect { id: act.id });
|
||||||
|
|
||||||
|
// stop actor
|
||||||
|
ctx.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.ping("");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let _ = env_logger::init();
|
let _ = env_logger::init();
|
||||||
let sys = actix::System::new("websocket-example");
|
let sys = actix::System::new("websocket-example");
|
||||||
|
@ -149,7 +149,7 @@ impl ChatSession {
|
|||||||
///
|
///
|
||||||
/// also this method check heartbeats from client
|
/// also this method check heartbeats from client
|
||||||
fn hb(&self, ctx: &mut Context<Self>) {
|
fn hb(&self, ctx: &mut Context<Self>) {
|
||||||
ctx.run_later(Duration::new(1, 0), |act, ctx| {
|
ctx.run_interval(Duration::new(1, 0), |act, ctx| {
|
||||||
// check client heartbeats
|
// check client heartbeats
|
||||||
if Instant::now().duration_since(act.hb) > Duration::new(10, 0) {
|
if Instant::now().duration_since(act.hb) > Duration::new(10, 0) {
|
||||||
// heartbeat timed out
|
// heartbeat timed out
|
||||||
@ -164,7 +164,6 @@ impl ChatSession {
|
|||||||
|
|
||||||
act.framed.write(ChatResponse::Ping);
|
act.framed.write(ChatResponse::Ping);
|
||||||
// if we can not send message to sink, sink is closed (disconnected)
|
// if we can not send message to sink, sink is closed (disconnected)
|
||||||
act.hb(ctx);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user