网络请求-轮询/长轮询/websocket - 实现服务器有更新的化,及时将数据发给用户
- 需求:【可以做qq聊天和投票系统】
用户前端需要及时的了解用户需要的信息是否更新,如投票系统,大屏幕需及时的反馈投票变更数
- 分析
想要及时更新屏幕数据,就需要服务器一旦有更新数据就发给用户
- 解决方案
1. 轮询
前端通过js事件setInterval(function, timeout) 来每个多少毫秒执行一次前的函数,函数可以进行ajax请求
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script> </head> <body> <div id="text"></div> <script> setInterval(get_data, 2000); function get_data(){ $.ajax({ url: '/xx/', success:function(data){ $('#text').html(data) } }) } </script> </body> </html>
缺点:不能实时更新数据,会增加服务器的IO压力
2. 长轮询
前端通过递归方法,不断的向后端发送ajax请求,后端记录每个请求并分配一个队列,利用队列的特性,当队列中没有值时可以阻塞一定时长,再返回错误信息(需要try一下因为当没有数据时到时间时获取会报错),如果有值就可以马上返回,这样就可以实现实时更新数据,又能相对轮询来减轻服务端的压力。
【前端发送请求,如果对应的队列中没有数据,请求就会被阻塞住,等待设定的时间过去后再给前端发送更新数据或提示还没有跟新的数据,前端收到后,递归又会继续执行函数继续新的请求,从而通过这种方式及时获取更新数据】
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script> </head> <body> <h1>长轮询测试:伪装为一个数累加,每个人填入数字都进行累加,实时展示</h1> <div> <input type="text" id="txt"> <input type="button" value="发送" onclick="sendMsg();"> </div> <div id="text" style="width: 100px;height: 100px;background-color: #1b6d85;color: red;text-align: center;line-height: 100px"> </div> <script> //长轮询 $(function () { function get_msg() { $.ajax({ // 第一次打开页面将用户名返回,这里就可以带上用户名,后端通过用户名去对应得队列中取值 url: '/get_data/?username={{ username }}', type: 'get', success: function (result) { console.log(result); if(result){ var old = Number($('#text').html()); old += Number(result); $('#text').html(old) } get_msg() } }) } get_msg(); }); // 将输入框中的值发送过去 function sendMsg(){ var val = $('#txt').val(); $.ajax({ url: '/send_data/', data:{ txt:val },success:function(result){ {#console.log(result);#} $('#txt').val("") } }) } </script> </body> </html>
urlpatterns = [
url(r'^get_page/$', views.get_page, name='get_page'),
url(r'^get_data/$', views.get_data),
url(r'^send_data/$', views.send_data),
// 省略了用户登录模块
]
import queue
USER_TASK_DICT = {} # 保存用户消息的队列字典
def get_page(request):
"""获取网页
前提用户需要登录,本例以用户名放在url上示范
思路:获取用户,为每个用户配置一个队列
数据格式为{username:[]}
"""
# 用户登录成功时,有将用户名写入用户得session
username = request.session.get('user_name', '')
print(username)
# username = request.GET.get('username', '')
if username and username not in USER_TASK_DICT:
# 如果用户不在字典中就新增
USER_TASK_DICT[username] = queue.Queue()
print(USER_TASK_DICT)
return render(request, 'test.html', {'username': username})
def get_data(request):
"""获取数据"""
username = request.GET.get('username', '')
try:
ret = USER_TASK_DICT[username].get(timeout=5)
except queue.Empty:
ret = ""
return HttpResponse(ret)
def send_data(request):
"""接收数据
群聊接收,让每个队列都接收数据
"""
val = request.GET.get('txt', '')
for i in USER_TASK_DICT.values():
i.put(val)
return HttpResponse('发送成功')
缺点:还是会增加服务端的压力,相对websocket来说,它可以兼容低版本的ie,现在流行的网页版qq和微信就是基于长轮询解决的。
注:此实例中的队列可以换成redis实现,可以持久化,而python自带的队列一旦重启所有数据就会丢失。
3. websocket
【定义】:同样基于tcp协议,的一种网络请求方式,会长期保持连接
【核心】:服务端主动向客户端发送消息,实时
【原理】:浏览器请求头中含有Sec-WebSocket-Key:Nh51LaQnPxXyRGr4eg/T6Q== ,发送websocket连接,进入握手环节
服务端,接到客户端请求,通过websocket-key判断为是websocket请求,然后将WebSocket-Key 和margin-str【全球唯一】拼接在一起,然后进行sha1加密,再进行bese64加密生成一个握手信息,
然后定义一个响应头,将其放在其中,返回为客户端,从而和客户端建立起了一个持久连接
服务端接到客户端发送的数据,会进行解密,和127做与或运算,有3中情况127/126/125,得到数据的长度,从而得到数据内容
服务端会给客户端的数据也会进行加密,根据数据长度与126比,进行处理成前部分为数据长度,后半部分为数据内容,发给客户端。
python中通过struck模块进行pack将不同长度的数据固定成一个固定值,接收端通过unpack进行解包,获取数据长度,然后按长度接收数据。
- websocket实现实时聊天