内容简介: 网易聊天室? web微信? 直播?
websocket
网易聊天室?
web微信?
直播?
假如你工作以后,你的老板让你来开发一个内部的微信程序,你需要怎么办?我们先来分析一下里面的技术难点
- 消息的实时性?
- 实现群聊
现在有这样一个需求,老板给到你了,关乎你是否能转正?你要怎么做?
我们先说消息的实时性,按照我们目前的想法是我需要用http协议来做,那么http协议怎么来做那?
是不是要一直去访问我们的服务器,问服务器有没有人给我发消息,有没有人给我发消息?那么大家认为我多长时间去访问一次服务比较合适那? 1分钟1次?1分钟60次?那这样是不是有点问题那?咱们都知道http发起一次请求就需要三次握手,四次断开,那么这样是不是对我服务器资源是严重的浪费啊?对我本地的资源是不是也是严重的浪费啊?这种方式咱们是不是一直去服务器问啊?问有没有我的信息?有我就显示?这种方式咱们一般称为轮询
http协议:
一次请求 一次相应 断开
无状态的 - 你曾经来过 session or cookie
在断开的情况下如果有数据只能等下次再访问的时候返回
那么我们先来总结一下,轮询优缺点
轮询02年之前使用的都是这种技术
每分钟访问60次服务器
优点:消息就基本实时
缺点:双资源浪费
长轮询2000-现在一直在使用
客户端发送一个请求- 服务器接受请求-不返回- 阻塞等待客户端-如果有消息了-返回给客户端
然后客户端立即请求服务器
优点:节省了部分资源,数据实时性略差
缺点:断开连接次数过多
那有没有一种方法是:我的服务器知道我的客户端在哪?有客户端的消息的时候我就把数据发给客户端
websocket是一种基于tcp的新网络协议,它实现了浏览器和服务器之间的双全工通信,允许服务端直接向客户端发送数据
websocket 是一个长连接
现在咱们的前端已经支持websocket协议了,可以直接使用websocket
简单应用
<body> <!-- 输入内容--> <input type="text" id="input"> <!-- 提交数据--> <button> 提交数据</button> <!-- 显示内容--> <div> <div ></div> </div> <script> var input=document.getElementById('input'); var button=document.querySelector('button'); var message=document.querySelector('div'); //websocket在浏览器端如何使用 //现在html已经提供了websocket,我们可以直接使用 var socket= new WebSocket('ws://echo.websocket.org'); socket.onopen=function () { message.innerHTML='连接成功了' }; //socket.addEventListener('open',function (data) { // message.innerHTML='连接成功了' //}); //点击事件 button.onclick=function () { request=input.value; socket.send(request) } //获取返回数据 socket.onmessage=function (data) { message.innerHTML=data.data }; socket.onclose=function (data) { message.innerHTML=data.data } </script> </body>
优化前端代码
button.onclick=function () { request=input.value; socket.send(request); input.value='' } //获取返回数据 socket.onmessage = function (data) { var dv=document.createElement('div'); dv.innerHTML=data.data; message.appendChild(dv) };
websocket 事件
事件 | 事件处理函数 | 描述 |
---|---|---|
open | socket.onopen | 连接建立是触发 |
message | socket.onmessage | 客户端收到服务端数据是触发 |
error | socket.error | 通信发生错误时触发 |
close | socket.close | 连接关闭时触发 |
websocket方法
方法 | 描述 |
---|---|
socket.send() | 使用连接发送数据 |
socket.close() | 关闭连接 |
websocke treadyState值的状态
值 | 描述 |
---|---|
0 (
CONNECTING
)
|
正在链接中 |
1 (
OPEN
)
|
已经链接并且可以通讯 |
2 (
CLOSING
)
|
连接正在关闭 |
3 (
CLOSED
)
|
连接已关闭或者没有链接成功 |
自建websocket服务端
准备前端页面
<!-- chat/templates/chat/index.html --> <!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <title>Chat Rooms</title> </head> <body> What chat room would you like to enter?<br/> <input id="room-name-input" type="text" size="100"/><br/> <input id="room-name-submit" type="button" value="Enter"/> <script> document.querySelector('#room-name-input').focus(); document.querySelector('#room-name-input').onkeyup = function(e) { if (e.keyCode === 13) { // enter, return document.querySelector('#room-name-submit').click(); } }; document.querySelector('#room-name-submit').onclick = function(e) { var roomName = document.querySelector('#room-name-input').value; window.location.pathname = '/web/' + roomName + '/'; }; </script> </body> </html>
编辑django的views,使其返回数据
# chat/views.py from django.shortcuts import render def index(request): return render(request, 'chat/index.html', {})
修改url
from django.conf.urls import url from .views import * urlpatterns = [ url(r'^$', index, name='index'), ]
跟settings同级目录下创建routing.py 文件
# mysite/routing.py from channels.routing import ProtocolTypeRouter application = ProtocolTypeRouter({ # (http->django views is added by default) })
编辑settings文件,将channels添加到installed_apps里面
INSTALLED_APPS = [ 'channels', 'chat', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ]
并添加channel的配置信息
ASGI_APPLICATION = 'mysite.routing.application'
准备聊天室的页面
<!-- chat/templates/chat/room.html --> <!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <title>Chat Room</title> </head> <body> <textarea id="chat-log" cols="100" rows="20"></textarea><br/> <input id="chat-message-input" type="text" size="100"/><br/> <input id="chat-message-submit" type="button" value="Send"/> </body> <script> var roomName = {{ room_name_json|safe }}; var chatSocket = new WebSocket( 'ws://' + window.location.host + '/ws/chat/' + roomName + '/'); chatSocket.onmessage = function(e) { var data = JSON.parse(e.data); var message = data['message']; document.querySelector('#chat-log').value += (message + '\n'); }; chatSocket.onclose = function(e) { console.error('Chat socket closed unexpectedly'); }; document.querySelector('#chat-message-input').focus(); document.querySelector('#chat-message-input').onkeyup = function(e) { if (e.keyCode === 13) { // enter, return document.querySelector('#chat-message-submit').click(); } }; document.querySelector('#chat-message-submit').onclick = function(e) { var messageInputDom = document.querySelector('#chat-message-input'); var message = messageInputDom.value; chatSocket.send(JSON.stringify({ 'message': message })); messageInputDom.value = ''; }; </script> </html>
准备views文件,使其返回页面
def room(request, room_name): return render(request, 'chat/room.html', { 'room_name_json':json.dumps(room_name) })
修改url
from django.conf.urls import url from . import views urlpatterns = [ url(r'^$', views.index, name='index'), url(r'^(?P<room_name>[^/]+)/$', views.room, name='room'), ]
实现简单的发送返回
from channels.generic.websocket import WebsocketConsumer import json class ChatConsumer(WebsocketConsumer): def connect(self): self.accept() def disconnect(self, close_code): pass def receive(self, text_data): text_data_json = json.loads(text_data) message = text_data_json['message'] self.send(text_data=json.dumps({ 'message': message }))
创建ws的路由
# chat/routing.py from django.conf.urls import url from . import consumers websocket_urlpatterns = [ url(r'^ws/chat/(?P<room_name>[^/]+)/$', consumers.ChatConsumer), ]
修改application的信息
# mysite/routing.py from channels.auth import AuthMiddlewareStack from channels.routing import ProtocolTypeRouter, URLRouter import chat.routing application = ProtocolTypeRouter({ # (http->django views is added by default) 'websocket': AuthMiddlewareStack( URLRouter( chat.routing.websocket_urlpatterns ) ), })
执行数据库的迁移命令
python manage.py migrate
要实现群聊功能,还需要准备redis
docker run -p 6379:6379 -d redis:2.8 pip3 install channels_redis
将 redis 添加到settings的配置文件中
# mysite/settings.py # Channels ASGI_APPLICATION = 'mysite.routing.application' CHANNEL_LAYERS = { 'default': { 'BACKEND': 'channels_redis.core.RedisChannelLayer', 'CONFIG': { "hosts": [('127.0.0.1', 6379)], }, }, }
修改consumer.py文件
from asgiref.sync import async_to_sync from channels.generic.websocket import WebsocketConsumer import json class ChatConsumer(WebsocketConsumer): def connect(self): self.room_name = self.scope['url_route']['kwargs']['room_name'] self.room_group_name = 'chat_%s' % self.room_name # Join room group async_to_sync(self.channel_layer.group_add)( self.room_group_name, self.channel_name ) self.accept() def disconnect(self, close_code): # Leave room group async_to_sync(self.channel_layer.group_discard)( self.room_group_name, self.channel_name ) # Receive message from WebSocket def receive(self, text_data): text_data_json = json.loads(text_data) message = text_data_json['message'] # Send message to room group async_to_sync(self.channel_layer.group_send)( self.room_group_name, { 'type': 'chat_message', 'message': message } ) # Receive message from room group def chat_message(self, event): message = event['message'] # Send message to WebSocket self.send(text_data=json.dumps({ 'message': message }))
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 如何快速实现一个聊天室?
- go语言实现聊天室
- 实现一个简单的WebSocket聊天室
- 用go实现聊天室(WebSocket方式)
- SpringBoot 实战 (十七) | 整合 WebSocket 实现聊天室
- golang实现一个简单的websocket聊天室
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
500 Lines or Less
Amy Brown、Michael DiBernardo / 2016-6-28 / USD 35.00
This book provides you with the chance to study how 26 experienced programmers think when they are building something new. The programs you will read about in this book were all written from scratch t......一起来看看 《500 Lines or Less》 这本书的介绍吧!