长轮询
- 轮询(Polling):是指不管服务器端有没有更新,客户端(通常是指浏览器)都定时的发送请求进行查询,轮询的结果可能是服务器端有新的更新过来,也可能什么也没有,只是返回个空的信息。不管结果如何,客户端处理完后到下一个定时时间点将继续下一轮的轮询。类似去不断查询银行支付状态。
- 推送或叫长连接(Long-Polling):这种方式的服务其客户端是不做轮询的,客户端在发起一次请求后立即挂起,一直到服务器端有更新的时候,服务器才会主动推送信息到客户端。 在服务器端有更新并推送信息过来之前这个周期内,客户端不会有新的多余的请求发生,服务器端对此客户端也啥都不用干,只保留最基本的连接信息,一旦服务器有更新将推送给客户端,客户端将相应的做出处理,处理完后再重新发起下一轮请求。
优点:在无消息的情况下不会频繁的请求。
缺点:服务器hold连接开销大,浪费资源,消耗流量。
实例:WebQQ、Hi网页版、Facebook IM。
WebSocket
轮询与长轮询都是基于HTTP的,两者本身存在着缺陷:轮询需要更快的处理速度;长轮询则更要求处理并发的能力;两者都是“被动型服务器”的体现:服务器不会主动推送信息,而是在客户端发送ajax请求后进行返回的响应。而理想的模型是"在服务器端数据有了变化后,可以主动推送给客户端",这种"主动型"服务器是解决这类问题的很好的方案。Web Sockets就是这样的方案。
由于长轮询消耗太多资源,主要原因是客户端和服务器并没有连接在一起,能够让客户端和服务器一直保持连接,这就需要用到websocket。
应用场景:实现即时通讯:如股票交易行情分析、聊天室、在线游戏等,替代轮询和长轮询。
优点
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
介绍:
WebSocket 协议是基于 TCP 的一种新的 HTML5 网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并被RFC7936所补充规范。在WebSocket API中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
简单说:客户端和服务器一直连接在一起。
WebSocket客户端编程
浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。当你获取 Web Socket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title> WebSocket </title>
<style>
*{
margin: 0;
padding: 0;
}
.box{
width: 800px;
margin-left: auto;
margin-right: auto;
margin-top: 25px;
}
#text{
width: 685px;
height: 130px;
border: 1px solid skyblue;
border-radius: 10px;
font-size: 20px;
text-indent: 1em;
resize:none;
outline: none;
}
#text::placeholder{
color: skyblue;
}
.btn{
width: 100px;
margin: -27px 0 0px 8px;
}
#messages{
padding-left: 10px;
font-size: 25px;
}
#messages li{
list-style: none;
color: #000;
line-height: 30px;
font-size: 18px;
}
</style>
</head>
<body>
<div class="box">
<div>
<textarea id="text" placeholder="请输入您的内容"></textarea>
<a href="javascript:WebSocketSend();" class="btn btn-primary">发送</a>
</div>
<ul id="messages">
</ul>
</div>
<script type="text/javascript">
var mes = document.getElementById('messages');
if('WebSocket' in window){ /*判断浏览器是否支持WebSocket接口*/
/*创建创建 WebSocket 对象,协议本身使用新的ws://URL格式*/
var Socket = new WebSocket("ws://127.0.0.1:8000/websocket")
/*连接建立时触发*/
Socket.onopen = function () {
alert("连接已建立,可以进行通信")
};
/*客户端接收服务端数据时触发*/
Socket.onmessage = function (ev) {
var received_msg = ev.data; /*接受消息*/
var aLi = "<li>" + received_msg + "</li>";
mes.innerHTML += aLi;
/*jq方式*/
// $(mes).append($(aLi));
};
/*连接关闭时触发*/
Socket.onclose = function () {
mes.innerHTML += "<br>连接已经关闭...";
};
}else{
/*浏览器不支持 WebSocket*/
alert("您的浏览器不支持 WebSocket!");
}
function WebSocketSend() {
/*form 里的Dom元素(input select checkbox textarea radio)都是value*/
var send_msg = document.getElementById('text').value;
//或者JQ中获取
// var send_msg = $("#text").val();
/*使用连接发送消息*/
Socket.send(send_msg);
}
</script>
</body>
</html>
WebSocket服务端编程
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
from data.user_module import User
from tornado.options import define,options
from tornado.web import authenticated #导入装饰器
from pycket.session import SessionMixin
import tornado.websocket
import datetime
define('port', default=8080, help='run port', type=int)
#设置BaseHandler类,重写函数get_current_user
class BaseHandler(tornado.web.RequestHandler, SessionMixin):
def get_current_user(self): #前面有绿色小圆圈带个o,再加一个箭头表示重写
print('********')
current_user = self.session.get('user') #获取加密的cookie
print(current_user)
if current_user:
return current_user
return None
#基类
class BaseWebSocketHandler(tornado.websocket.WebSocketHandler,SessionMixin):
def get_current_user(self):
current_user = self.session.get('user')
if current_user:
return current_user
return None
#跳转
class IndexHandler(BaseHandler):
@authenticated
def get(self):
self.render('websocket.html')
#在LoginHandler类中的post函数里面加上设置cookie
class LoginHandler(BaseHandler):
def get(self):
nextname = self.get_argument('next','') #将原来的路由赋值给nextname
print(nextname)
self.render('login.html',nextname=nextname) #跳转页面带上获取的参数
def post(self,*args,**kwargs):
user = self.get_argument('name','')
username = User.by_name(user)
passwd = self.get_argument('password','')
nextname = self.get_argument('next','') #获取之前页面的路由
if username and passwd == username[0].password:
self.session.set('user', user) # 设置加密cookie
self.redirect(nextname) #跳转到之前的路由
else:
self.render('login.html',nextname=nextname)
'''Tornado 定义了 tornado.websocket.WebSocketHandler 类用于处理 WebSocket 链接的请求,
应用开发者应该继承该类并实现其中的open()、on_message()、on_close() 函数
'''
class MessageHandler(BaseWebSocketHandler):
users = set() #避免重复登录,利用集合的去重性
def open(self): #当一个新的WebSocket连接建立后被调用。
MessageHandler.users.add(self) #类属性,用类名直接调用,
print('----------open------')
def on_message(self, message): #当客户端发送消息message过来时被调用,注意此方法必须被重写。
print(self.request.remote_ip)
for u in self.users: #write_message,向客户端发送消息messagea,message可以是字符串或字典(字典会被转为json字符串
u.write_message('%s-%s-说:%s'%(self.current_user,
datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
message
))
def on_close(self): #当WebSocket连接关闭后被调用。
if self in MessageHandler.users:
MessageHandler.users.remove(self) #断开的时候移除
print(self)
print('-----------close--------')
application = tornado.web.Application(
handlers = [
(r'/login',LoginHandler),
(r'/websocket',MessageHandler),
(r'/',IndexHandler),
],
# cookie_secret='1q2w3e4r',
#设置跳转路由,为了防止在没有登录情况下,直接输入需要登录才可见的url进行访问,做判断,如果没有登录则跳转到这个路由下
login_url='/login',
pycket={
'engine': 'redis',
'storage': {
'host': 'localhost',
'port': 6379,
'db_sessions': 5,
'db_notifications': 11,
'max_connections': 2 ** 31,
},
'cookies': {
'expires_days': 30,
'max_age': 100
},
},
static_path='static',
template_path = 'templates', #想要Tornado能够正确的找到html文件,需要在 Application 中指定文件的位置
cookie_secret='sgsdf',
debug = True #调试模式,修改后自动重启服务,不需要自动重启,生产情况下切勿开启,安全性
)
if __name__ == '__main__':
tornado.options.parse_command_line()
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
运行效果:
image.png
image.png
Tornado 定义了 tornado.websocket.WebSocketHandler 类用于处理 WebSocket 链接的请求,应用开发者应该继承该类并实现其中的open()、on_message()、on_close() 函数,除了这3个 Tornado 框架自动调用的入口函数,WebSocketHandler 还提供了两个开发者主动操作 WebSocket的函数:
WebSocketHandler.write_message(message)函数:用于向与本链接相对应的客户端写消息。
WebSocketHandler.close(code=None,reason=None)函数:主动关闭 WebSocket链接。其中的code和reason用于告诉客户端链接被关闭的原因。参数code必须是一个数值,而reason是一个字符串。
网友评论