1)通知功能:
2)数据收集:
3)加密 && 认证:
4)反向控制钩子:
1、websocket则允许我们在一条ws连接上同时并发多个请求,即在A请求发出后A响应还未到达,就可以继续发出B请求。由于TCP的慢启动特性(新连接速度上来是需要时间的),以及连接本身的握手损耗,都使得websocket协议的这一特性有很大的效率提升。
2、webSocket的复用性可以利用上一条请求内容
3、websocket支持服务器推送消息,这带来了及时消息通知的更好体验,也是ajax请求无法达到的。
1、服务器长期维护长连接需要一定的成本
2、各个浏览器支持程度不一
3、websocket 是长连接,受网络限制比较大,需要处理好重连,比如用户进电梯或电信用户打个电话网断了,这时候就需要重连
参考博客
步骤:
1.后端
a.搭建服务器和处理器
2.前端
a.判断是否支持webSocket
b.创建webSocket对象设置参数uri
c.设计发送(send)、接收和显示(show)消息方法
d.心跳机制:确保客户端或服务器活着
e.重连机制:当客户端或服务器恢复后能够重新连上
<dependency>
<groupId>io.nettygroupId>
<artifactId>netty-allartifactId>
<version>4.1.77.Finalversion>
dependency>
package com.wxl.websocket;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
public class WebSocketServer {
public static void main(String[] args) {
try {
EventLoopGroup master = new NioEventLoopGroup();
EventLoopGroup salve=new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(master,salve);
bootstrap.channel(NioServerSocketChannel.class);
bootstrap.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(Channel channel) throws Exception {
ChannelPipeline pipeline = channel.pipeline();
//http编码器
pipeline.addLast(new HttpServerCodec());
pipeline.addLast(new HttpObjectAggregator(1024*10));
pipeline.addLast(new WebSocketServerProtocolHandler("/"));//此处设置映射路径
//自定义客户端处理器
pipeline.addLast(new WebSocketServerHandler());
}
});
ChannelFuture channelFuture = bootstrap.bind(8081);
channelFuture.sync();
System.out.println("服务端启动成功。。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
注意:在添加处理器的位置添加客户端超过多久未发送信息便自动断开连接的处理器——//客户端10s不发送信息自动断开
pipeline.addLast(new ReadTimeoutHandler(10, TimeUnit.SECONDS));
package com.wxl.websocket;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.EventExecutorGroup;
public class WebSocketServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception {
System.out.println("读取客户端的内容:"+textWebSocketFrame.text());
String text=textWebSocketFrame.text();
if (text.equals("heard")){//处理心跳
//响应heard到客户端
TextWebSocketFrame heard = new TextWebSocketFrame("heard");
channelHandlerContext.writeAndFlush(heard);
return;
}
TextWebSocketFrame textWebSocketFrame1 = new TextWebSocketFrame("嗯");
channelHandlerContext.writeAndFlush(textWebSocketFrame1);
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("新客户端建立连接。。。。");
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("客户端断开连接。。。。");
}
}
//心跳机制
sendHeard(){
//每5秒发送一次
this.sendHeardTime=setInterval(function() {
//向服务端发送消息
ws.send("heard");
}, 5000);
},
注意:在data方法中定义sendHeardTime:undefined==》用于后序clearInterval(sendHeardTime)清除该定时事件
//关闭连接
closeConn(){
this.closeConnTime=setTimeout(function(){
ws.close();
},10000);
},
//重连机制
reConn(){
console.info("重连");
setTimeout(()=>{
this.initWebSocket();
},5000);
}
服务划分:
1、搭建eureka注册中心
2、config服务,将所有配置集中放到该服务
3、用户服务——处理各种业务逻辑
4、网关服务
5、netty服务——通讯
同时创建各自的处理类,将处理类添加到websocket服务类
实体类:
package com.wxl;
import io.netty.channel.Channel;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* 保存所有的客户端的连接 设备id,channel
*/
public class ChannelGroup {
/**
* key:设备id
* channel:设备id的连接对象
*/
public static Map<String, Channel> channelMap=new HashMap<>();
/**
* 添加channel到容器中
* @param did
* @param channel
*/
public static void addChannel(String did,Channel channel){
channelMap.put(did,channel);
}
/**
* 获取channel对象
* @param did
* @return
*/
public static Channel getChannel(String did){
return channelMap.get(did);
}
/**
* 删除channel对象
* @param did
*/
public static void removeChannel(String did){
channelMap.remove(did);
}
public static void removeChannel(Channel channel){
if (channelMap.containsKey(channel)){
Set<Map.Entry<String, Channel>> entries = channelMap.entrySet();
for (Map.Entry<String, Channel> entry : entries) {
if (entry.getValue()==channel){
channelMap.remove(entry.getKey());
break;
}
}
}
}
}
主要步骤
1、引入redis存储用户id和设备id
2、引入rabbitmq,通过交换机和队列,来实现用户服务查询websocket服务中注册的channel
3、创建ChannelGroup实体类,并定义一个map属性来存储设备id和channel
1、目的:根据设备id获取对应的channel
用户端和netty服务端引入依赖,添加配置
在客户端断开连接后需在主webSocketHandler处理器的unregistered方法中将当前channel从ChannelGroup中删除
在ConnMsgHandler类(客户端连接处理器)中使用构造器的方法将websocket主服务类中注入的redisTemplate通过参数传入该类
<template>
<view>
<view style="width: 400px;height: 300px;border: 1px solid red;" id="showMgs">
view>
<view style="border: 1px solid red;width: 400px;">
<input style="float: left;border: 1px solid green; width: 200px;height: 40px;" type="text" v-model="cnt"/>
<button style="width: 100px;height: 40px;line-height: 40px;" @click="sendMsg">发送button>
view>
view>
template>
<script>
import $ from 'jquery';
var ws;
export default {
data() {
return {
cnt:'',
flage:true,
closeConnTime:undefined, //超过多少时间关闭连接的事件
sendHeardTime:undefined, //定时发送心跳机制
}
},
created() {
this.initWebSocket();
},
destroyed() {
clearInterval(this.sendHeardTime);
ws.close();
//this.reConn();//重连
},
methods: {
initWebSocket(){
console.log("1");
//是否支持websocket属性
if(window.WebSocket){
console.log("2");
//连接服务器
ws=new WebSocket("ws://localhost:8081/");
ws.onmessage = this.websocketonmessage;
ws.onopen = this.websocketonopen;
ws.onerror = this.websocketonerror;
ws.onclose = this.websocketclose;
}else{
alert("不支持websocket服务器");
}
},
//客户端接收服务端数据时触发
websocketonmessage(data){
if(data.data!="heard"){
console.info("客户端响应数据"+data.data);
$("#showMgs").append("我:"+data.data+"
");
}else{
clearTimeout(this.closeConnTime);//清除定时关闭的连接
this.closeConn();
console.info("心跳:"+data.data);
}
//this.msgs.push("我:"+data.data+"
");
},
websocketonopen(){//连接建立时触发
console.info("客户端连接成功。。。。");
$("#showMgs").append("客户端连接成功
");
//this.msgs.push({cnt:"客户端连接成功
"});
//成功连接之后调用心跳
this.sendHeard();
this.closeConn();
},
//通信发生错误时触发
websocketonerror(){
console.info("通信发生错误。。。");
},
websocketclose(){//连接关闭时触发
clearInterval(this.sendHeardTime);//清除定时发送心跳事件
this.reConn();//重连
console.info("客户端断开连接。。。");
$("#showMgs").append("客户端断开连接
");
//this.msgs.push("客户端断开连接
");
},
sendMsg(){
//var msg=$("#cnt").val();
var msg=this.cnt;
console.info(msg);
//发送消息
ws.send(msg);//将数据发送到webSocket服务端
//将数据显示到页面
$("#showMgs").append("我:"+msg+"
");
//this.msgs.push("我:"+msg+"
");
},
//心跳机制
sendHeard(){
//每5秒发送一次
this.sendHeardTime=setInterval(function() {
//向服务端发送消息
ws.send("heard");
}, 5000);
},
//关闭连接
closeConn(){
this.closeConnTime=setTimeout(function(){
ws.close();
},10000);
},
//重连机制
reConn(){
console.info("重连");
setTimeout(()=>{
this.initWebSocket();
},5000);
}
}
}
script>
<style>
style>