websocket可以在用户的浏览器和服务器之间打开交互式通信会话,使用websocket可以向服务器发送消息并接收事件驱动的响应,而无需通过轮询服务器的方式以获得响应。
首先介绍一下前端websocket一些基本接口。
let socket = new Websocket(url[, protocols])
// 实例化直接建立连接,连接完成可以开始通讯了
socket.send(msg)
socket.close()
socket.onmessage = (event) => {
console.log(event.data)
}
实例化后,socket的可能的状态码可以通过访问socket对象的常量属性获得。
可能的状态码:
- socket.CONNECTING - 0 # 正在链接,实例化至此时onopen触发之间
- socket.OPEN - 1 # 触发onopen,之后一直保持该状态直到断开连接
- socket.CLOSING - 2 # 关闭连接挥手阶段
- socket.CLOSED - 3 # 连接已关闭或者连接未建立。 当服务器关闭或者调用close断开连接并且挥手结束后
socket当前的状态码可以通过readyState属性进行访问:
if (socket.readyState == socket.OPEN) {
// 判断是否在正常连接状态
xxx
}
只是一个非常简易的demo,效果图如下:
github地址:
前端需要主动建立连接,同时实现收发消息功能。
1.我们封装一个websocket类,对类进行实例化就创建了一个socket连接:
文件路径:src/socket/socket.js
class CWebSocket {
constructor (url) {
this.socket = new Websocket(url)
}
}
class CWebSocket {
constructor (url) {
this.socket = new Websocket(url)
this.waitingList = []
this.socket.open = (...args) => {
this.checkWaitingList()
}
}
checkWaitingList () {
this.waitingList.forEach(this.C2SMessage)
},
C2SMessage (data) {
if (this.socket.readyState !== this.socket.OPEN) {
this.waitingList.push(data)
return
}
let msg = JSON.stringify(data)
this.socket.send(msg)
}
}
class CWebSocket {
constructor (url) {
// ...
this.listenerList = []
this.socket.onmessage = (...args) => {
this.S2CMessage(...args)
}
}
addListener (func) {
this.listenerList.push(func)
}
removeListener (func) {
let index = this,listenerList.indexOf(func)
index != -1 && this.listenerList.splice(index, 1)
}
S2CMessage (event) {
let msg = JSON.parse(event.data)
this.listenerList.forEach(func => func(msg))
}
}
至此,就实现websocket的绝大部分功能。
4. 销毁实例,有类实例创建、初始化就应该有对应的销毁函数:
class CWebSocket {
// ...
close () {
if (this.socket) {
this.listenerList = []
this.socket.onopen = null
this.socket.onmessage = null
this.socket.close()
this.socket = null
}
}
}
同时导出这个类,方便外部引用:
export class CWebSocket {
xxx
}
简易但是完整的scoket封装类就完成了,接下来就是使用了。为每个用户生成一个uuid作为ID,组件创建和销毁的时候分别分别创建和销毁socket对象,
简单写一个页面:
<template>
<div id="app">
<div class="title">简易websocket聊天室({
{ userId }})div>
<div class="message-list">
<div class="message" v-for="(data, idx) of messageList" :key="idx">
<div class="user-id">{
{ data.userId }}<template v-if="data.userId == userId">(我)template>:div>
<div class="msg">{
{ data.msg }}div>
div>
div>
<div class="msg-send">
<textarea placeholder="输入消息开始聊天吧" @keypress.ctrl.enter.stop="C2SMessage" v-model="message" cols="30" rows="5">textarea>
<button @click.stop="C2SMessage">发送button>
div>
div>
template>
<script>
import uuidv4 from 'uuid/v4'
import {
CWebSocket } from '@/socket/socket.js'
export default {
data () {
messageList: [],
message: '',
userId: uuidv4(),
socket: null
}
created () {
this.socket = new CWebSocket()
this.socket.addListener((...args) => {
this.S2CMessage(...args)
})
},
beforeDestroy () {
this.socket.close()
},
methods: {
S2CMessage (data) {
this.messageList.push(data)
},
C2SMessage () {
if (!this.message) return
this.socket.C2SMessage({
msg: this.message,
userId: this.userId
})
this.message = ''
}
}
}
script>
<style>
body {
background: #FAFBFC
}
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
box-shadow: 0 0 3px 0 #3361d8;
width: 50rem;
max-width: 100%;
background: #ECF5FD;
position: fixed;
border-radius: 4px;
transform: translate(-50%, 0);
left: 50%;
display: flex;
flex-direction: column;
top: 5rem;
bottom: 5rem;
}
.title {
flex-shrink: 0;
padding: .5rem;
}
.message-list {
flex: auto;
background: white;
overflow: auto
}
.message-list .message {
text-align: left;
padding: .5rem;
}
.message-list .message .user-id {
color: blue;
}
.message-list .message .msg {
margin: 0 1rem;
padding: 8px;
background: #ECF2FC;
border-radius: 4px;
}
.msg-send {
flex-shrink: 0;
display: flex;
padding: 4px;
}
.msg-send textarea {
flex: auto;
border-radius: 4px;
resize: none;
padding: 8px 4px;
}
.msg-send button {
float: right;
margin: 4px;
align-self: flex-end;
border-radius: 4px;
border: none;
background: #57a3f3;
color: white;
height: 2rem;
padding: 0 1rem;
line-height: 2rem;
}
style>
使用python的web框架bottle,代码非常简单:
from bottle import get, run
from bottle.ext.websocket import GeventWebSocketServer
from bottle.ext.websocket import websocket
users = set()
@get('/websocket/', apply=[websocket])
def chat(ws):
users.add(ws)
while True:
msg = ws.receive() # 接客户端的消息
if msg:
print(msg)
for user in users:
user.send(msg) # 广播消息
else:
break
print('退出聊天')
users.remove(ws)
run(host='127.0.0.1', port=8000, server=GeventWebSocketServer)
一个简易的聊天室就完成了,详细代码可查看github: