介绍:
readyState表示连接有四种状态:
-
connecting (0):表示还没建立连接;
-
open (1): 已经建立连接,可以进行通讯;
-
closing (2):通过关闭握手,正在关闭连接;
-
closed (3):连接已经关闭或无法打开;
前端代码表示
原生JS(HTML+JS)
HTML文件
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>通信</title>
</head>
<body>
<ul id="list"></ul>
<input type="text" id="message" placeholder="请输入内容" />
<button id="send">发送</button>
<script src="XXX.js> </script>
</body>
</html>
JS文件
//立即执行函数,避免全局变量之间的污染
;((doc)=>{
const oList=doc.querySelector('#list');
const oMsg=doc.querySelector('#message');
const oSendBtn.querySelector('#send');
const ws=new Socket('ws:localhost:8000');//创建一个Socket实例
//初始化函数
const init=()=>{
bindEvent();
}
//绑定事件函数,监听oSendBtn,
//最后参数是指定事件是否在捕获或冒泡阶段执行,false是默认冒泡阶段执行,true捕获阶段执行
function bindEvent(){
oSendBtn.addEventListener('click',handSendBtnClick,false);
ws.addEventListener('open',handOpen,false);
ws.addEventListener('close',handClose,false);
ws.addEventListener('error',handError,false);
ws.addEventListener('message',handMessage,false);
}
//具体的点击事件
function handSendBtnClick(){
console.log('发送的具体消息')
const msg=oMsg.value
if(!msg.trim().length){
return
}
//发送的应该为字符串
ws.send(JSON.stringfy({
dataTime:new Data().getTime(),
message:msg
}))
oMsg.value=''
}
//socket的四个事件
function handOpen(){
console.log('打开')
}
function handClose(){
console.log('关闭')
}
function handError(){
console.log('')
}
function handMessage(e){
console.log('接收消息',e.data)
const msgData=JSON.parse(e.data) //转化为对象
oList.appendChild(createMsg(msgData)) //转为li
}
//做成LI的格式
function createMsg(data){
const {user,dataTime,message}=data
const oItem=doc.createElement('li')
oItem.innerHTML=`
<p>
<span>${user}</span>
<li>${new Data()}</li>
</p>
<p>消息:${message}</p>
`
return oItem
}
init()
})(document);
纯HTML
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>聊天室</title>
<script type="text/javascript">
var ws = null;
var interval = null;
var uri = "ws://192.168.110.92:8080/schedule/websocket/connect";
var myVar = null;
var reconnectState = null;
function WebSocketTest() {
if ("WebSocket" in window) {
createWebSocket();
} else {
// 浏览器不支持 WebSocket
alert("您的浏览器不支持 WebSocket!");
}
}
/**
* 创建websocket
*/
function createWebSocket() {
alert("开启长连接")
// 打开一个 web socket
ws = new WebSocket(uri);
ws.onopen = function () {
document.getElementById("reconnectShow").innerText = "无";
document.getElementById("online").innerText = "在线";
reconnectState = false;
clearInterval(myVar);
// 心跳检测
interval = setInterval(function () {
// 使用 send() 方法发送数据
sendMsg("ping");
}, 5000);
};
ws.onmessage = function (evt) {
var received_msg = evt.data;
console.log("回复",evt.data);
if (received_msg == "pong") {
console.log(received_msg);
} else {
document.getElementById("num").innerText = received_msg;
}
};
ws.onclose = function () {
webscoketClose();
};
}
/**
* websocket重连
*/
function reconnect() {
// 检测到websocket连接断开
// 0 (WebSocket.CONNECTING)正在链接中
// 1 (WebSocket.OPEN)已经链接并且可以通讯
// 2 (WebSocket.CLOSING) 连接正在关闭
// 3 (WebSocket.CLOSED)连接已关闭或者没有链接成功
if (reconnectState == false) {
reconnectState = true;
var reconnectNum = 1;
console.log(ws.readyState);
myVar = setInterval(function () {
if (ws.readyState == 2 || ws.readyState == 3) {
if (reconnectNum == 6) {
clearInterval(myVar);
document.getElementById("reconnectShow").innerText = "停止重连";
return;
}
document.getElementById("reconnectShow").innerText = "第" + reconnectNum + "次尝试重连中";
createWebSocket();
reconnectNum++;
}
}, 5 * 1000)
}
}
function noReconnectclose() {
reconnectState = true;
webscoketClose();
}
/**
* websocket关闭
*/
function webscoketClose() {
ws.close();
clearInterval(interval)
console.log("websocket关闭");
document.getElementById("online").innerText = "离线";
// 开启断线重连
reconnect();
}
function sendMsg(str) {
if (ws.readyState == 1) {
ws.send(str);
// alert("发送内容");
} else {
alert("未连接上服务器");
}
}
function add() {
sendMsg("+1");
}
function remove() {
sendMsg("-1");
}
</script>
</head>
<body>
<div id="sse">
<a href="javascript:WebSocketTest()">运行 WebSocket</a>
<a href="javascript:noReconnectclose()">停止 WebSocket</a>
</div>
<br>
当前webscoket状态:<span id="online">离线</span>
<br>
当前重连状态:<span id="reconnectShow">无</span>
<br>
当前怒气值:<span id="num">0</span>
<br>
<a href="javascript:add()">+1</a>
<a href="javascript:remove()">-1</a>
</body>
</html>
vue简单的逻辑
客户端页面:
<div id="app" >
<ul>
<li v-for="item in msgList" :key="item.id">
<p>
<span>{{item.username}}:</span>
<span>{{new Date(item.dataTime)}}</span>
</p>
<p>消息:{{item.msg}}</p>
</li>
</ul>
<input type="text" placeholder="请输入消息" v-model="msg" />
<button @click="sendMsg">发送</button>
</div>
<script>
const ws = new WebSocket('ws://localhost:8000')
new Vue({
el: '#app',
data: {
msg: '',
username: '',
msgList: []
},
mounted() {
ws.addEventListener('open', this.handleWsOpen.bind(this), false) // 监听连接打开 前面的this指向绑定的ws,bind里面的this为了改变指向,指向当前vue实例
ws.addEventListener('close', this.handleWsClose.bind(this), false) // 监听连接关闭
ws.addEventListener('error', this.handleWsError.bind(this), false) // 监听连接错误
ws.addEventListener('message', this.handleWsMessage.bind(this), false) // 监听消息
},
methods: {
sendMsg() {
//把绑定的消息取出来
const msg=this.msg
//如果去掉两边空格,长度还为0,则返回不再进行下一步
if(!msg.trim().length){
return
}
//把当前时间戳
const dataTime=new Date().getTime()
//把用户名
const username=this.username
//把消息发送到服务器
ws.send(JSON.stringify({
msg,
dataTime,
username
}))
//不可以传对象 要转化为JSON字符串
// ws.send({
// id:new Data().getTime(),
// user:this.username,
// dataTime:new Data().getTime(),
// msg:this.msg
// })
this.msg='' //注意发送完要清空输入框
},
handleWsOpen(e) {
console.log('连接成功')
},
handleWsClose(e) {
console.log('连接关闭')
},
handleWsError(e) {
console.log('连接错误')
},
handleWsMessage(event) {
const data = JSON.parse(event.data) //内容包裹在data中 且需要转换为对象
this.msgList.push(data)
console.log(data)
}
}
})
</script>
服务器部分:
增加长连接的方式 :
<template>
<ul>
<li v-for="item in msgList" :key="item.id">
<p>
<span>{{item.username}}:</span>
<span>{{new Date(item.dataTime)}}</span>
</p>
<p>消息:{{item.msg}}</p>
</li>
</ul>
<input type="text" placeholder="请输入消息" v-model="msg" />
<button @click="sendDataToServer">发送</button>
</template>
<script>
export default {
name: "WebSocket",
data() {
return {
wsIsRun: false, // ws是否启动
webSocket: null, // 定义ws对象
ws: '', // ws请求链接(类似于ws后台地址)
wsTimer: null, // ws定时器
msg: '',
username: '',
msgList: [], //包含传过去的id,username,dataTime
}
},
async mounted() {
this.wsIsRun = true
this.wsInit()
},
methods: {
sendDataToServer() {
if (this.webSocket.readyState === 1) {
// this.webSocket.send('来自前端的数据')
//把绑定的消息取出来
const msg = this.msg
//如果去掉两边空格,长度还为0,则返回不再进行下一步
if (!msg.trim().length) {
return
}
//当前时间戳
const dataTime = new Date().getTime()
//用户名
const username = this.username
//把消息发送到服务器
this.webSocket.send(JSON.stringify({
msg,
dataTime,
username
}))
this.msg = '' //注意发送完要清空输入框
} else {
throw Error('服务未连接')
}
},
/**
* 初始化ws
*/
wsInit() {
const wsuri = 'ws://10.229.36.158:7777/websocket/badao'
this.ws = wsuri
if (!this.wsIsRun) return
// 销毁ws
this.wsDestroy()
// 初始化ws
this.webSocket = new WebSocket(this.ws)
// ws连接建立时触发
this.webSocket.addEventListener('open', this.wsOpenHanler)
// ws服务端给客户端推送消息
this.webSocket.addEventListener('message', this.wsMessageHanler)
// ws通信发生错误时触发
this.webSocket.addEventListener('error', this.wsErrorHanler)
// ws关闭时触发
this.webSocket.addEventListener('close', this.wsCloseHanler)
// 检查ws连接状态,readyState值为0表示尚未连接,
// 1表示建立连接,2正在关闭连接,3已经关闭或无法打开
clearInterval(this.wsTimer)
this.wsTimer = setInterval(() => {
if (this.webSocket.readyState === 1) {
clearInterval(this.wsTimer)
} else {
console.log('ws建立连接失败')
this.wsInit()
}
}, 3000)
},
wsOpenHanler(event) {
console.log('ws建立连接成功')
},
wsMessageHanler(e) {
console.log(e)
const data = JSON.parse(e.data) //内容包裹在data中 且需要转换为对象
this.msgList.push(data)
},
/**
* ws通信发生错误
*/
wsErrorHanler(event) {
console.log(event, '通信发生错误')
this.wsInit()
},
/**
* ws关闭
*/
wsCloseHanler(event) {
console.log(event, 'ws关闭')
this.wsInit()
},
/**
* 销毁ws
*/
wsDestroy() {
if (this.webSocket !== null) {
this.webSocket.removeEventListener('open', this.wsOpenHanler)
this.webSocket.removeEventListener('message', this.wsMessageHanler)
this.webSocket.removeEventListener('error', this.wsErrorHanler)
this.webSocket.removeEventListener('close', this.wsCloseHanler)
this.webSocket.close()
this.webSocket = null
clearInterval(this.wsTimer)
}
},
}
}
</script>
<style scoped>
</style>
页面布局:
:style="i.userId == userId? 'float: right' : '' "
:class="i.userId == userId? 'right':'left'"
可用于区分聊天内容在左侧还是右侧
.left {
background: white;
animation: toLeft 0.5s ease both 1;
}
.right {
background: #53a8ff;
color: white;
animation: toright 0.5s ease both 1;
}
@keyframes toLeft {
0% {
opacity: 0;
transform: translateX(-10px);
}
100% {
opacity: 1;
transform: translateX(0px);
}
}
@keyframes toright {
0% {
opacity: 0;
transform: translateX(10px);
}
100% {
opacity: 1;
transform: translateX(0px);
}
}
}
}