websocket实现通讯——Java


参考博客0

一、webSocket的作用

1)通知功能:
2)数据收集:
3)加密 && 认证:
4)反向控制钩子:

二、webSocket的优缺点

1、优点

1、websocket则允许我们在一条ws连接上同时并发多个请求,即在A请求发出后A响应还未到达,就可以继续发出B请求。由于TCP的慢启动特性(新连接速度上来是需要时间的),以及连接本身的握手损耗,都使得websocket协议的这一特性有很大的效率提升。
2、webSocket的复用性可以利用上一条请求内容
3、websocket支持服务器推送消息,这带来了及时消息通知的更好体验,也是ajax请求无法达到的。

2、缺点

1、服务器长期维护长连接需要一定的成本
2、各个浏览器支持程度不一
3、websocket 是长连接,受网络限制比较大,需要处理好重连,比如用户进电梯或电信用户打个电话网断了,这时候就需要重连

3、webSocket与Http协议的异同

参考博客

三、webSocket重要步骤

步骤:
	1.后端
		a.搭建服务器和处理器
	2.前端
		a.判断是否支持webSocket
		b.创建webSocket对象设置参数uri
		c.设计发送(send)、接收和显示(show)消息方法
		d.心跳机制:确保客户端或服务器活着
		e.重连机制:当客户端或服务器恢复后能够重新连上

1、后端

1.1、webSocket服务器搭建

  • 导入依赖
		<dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.77.Final</version>
        </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("客户端断开连接。。。。");
    }
}

2、前端

1.1、心跳机制

  1. 心跳代码
			//心跳机制
			sendHeard(){
				//每5秒发送一次
				this.sendHeardTime=setInterval(function() {
					//向服务端发送消息
					ws.send("heard");
				}, 5000);
			},

注意:在data方法中定义sendHeardTime:undefined==》用于后序clearInterval(sendHeardTime)清除该定时事件

  1. 超时关闭连接的方法
  • 当客户端发送心跳后多久后未收到服务端回的心跳,便断开连接。
  • 实现代码
			//关闭连接
			closeConn(){
				this.closeConnTime=setTimeout(function(){
					ws.close();
				},10000);
			},
  1. 在onopen方法中调用——两方法
  2. 在onmessage方法中收到服务端回的心跳后,调用clearTimeout,并再次调closeConn方法
  3. 在onclose和destroyed方法中clearInterval

1.2、重连机制

  1. 重连的代码
			//重连机制
			reConn(){
				console.info("重连");
				setTimeout(()=>{
					this.initWebSocket();
				},5000);
			}
  1. 在onclose方法中调用

四、通讯项目思想

在这里插入图片描述

1、服务搭建

服务划分:
	1、搭建eureka注册中心
	2、config服务,将所有配置集中放到该服务
	3、用户服务——处理各种业务逻辑
	4、网关服务
	5、netty服务——通讯

2、创建多个实体类处理不同业务

在这里插入图片描述

同时创建各自的处理类,将处理类添加到websocket服务类

实体类:

  • ConnMsg实体类
    在这里插入图片描述

  • NettyMsg
    在这里插入图片描述

  • ShutDownMsg
    在这里插入图片描述

  • ChannelGroup实体类

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;
                }
            }
        }
    }

}
		
  • websocket的主处理类webSocketHandler,先执行该处理类再处理其他处理类
    在这里插入图片描述

3、实现用户挤下线的功能

主要步骤
	1、引入redis存储用户id和设备id
	2、引入rabbitmq,通过交换机和队列,来实现用户服务查询websocket服务中注册的channel
	3、创建ChannelGroup实体类,并定义一个map属性来存储设备id和channel

1、redis

  1. 引入依赖和添加配置
  2. 在用户服务注入StringRedisTemplate——存入redis的数据对为(用户id:设备id)
  3. 在login业务中——根据用户id查询设备id
  4. 判断设备id与前端传来的是否相等,不等创建shutDownMsg对象,rabbitmq发送消息
    在这里插入图片描述
  5. 挤下操作

    1、目的:根据设备id获取对应的channel

2、rabbitmq

  1. 用户端和netty服务端引入依赖,添加配置

  2. 用户端注入Rabbitmq,主要负责给交换机发送数据
    在这里插入图片描述

  3. 创建队列和交换机,同时绑定队列和交换机
    在这里插入图片描述

  4. 创建监听类——监听是否向交换机发送了消息
    在这里插入图片描述

  5. 前端获取服务端消息onmassege()——方法查询html5plus官网
    在这里插入图片描述

  6. 在客户端断开连接后需在主webSocketHandler处理器的unregistered方法中将当前channel从ChannelGroup中删除
    在这里插入图片描述

  7. 在ConnMsgHandler类(客户端连接处理器)中使用构造器的方法将websocket主服务类中注入的redisTemplate通过参数传入该类
    在这里插入图片描述

五、整体代码

1.vue

<template>
	<view>
		<view style="width: 400px;height: 300px;border: 1px solid red;" id="showMgs">
			<!-- <view v-for="(item,index) in msgs" :key="index" v-html="item.cnt"></view> -->
		</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("<span>我:"+data.data+"</span><br>");						
				}else{
					clearTimeout(this.closeConnTime);//清除定时关闭的连接
					this.closeConn();
					console.info("心跳:"+data.data);
				}
				//this.msgs.push("<span>我:"+data.data+"</span><br>");
			},
			websocketonopen(){//连接建立时触发
				console.info("客户端连接成功。。。。");
				$("#showMgs").append("<span style='color:green;'>客户端连接成功</span><br>");
				//this.msgs.push({cnt:"<span style='color:green;'>客户端连接成功</span><br>"});
				//成功连接之后调用心跳
				this.sendHeard();
				this.closeConn();
			},
			//通信发生错误时触发
			websocketonerror(){
				console.info("通信发生错误。。。");
			},
			websocketclose(){//连接关闭时触发
				clearInterval(this.sendHeardTime);//清除定时发送心跳事件
				this.reConn();//重连
				console.info("客户端断开连接。。。");
				$("#showMgs").append("<span style='color:red;'>客户端断开连接</span><br>");
				//this.msgs.push("<span style='color:red;'>客户端断开连接</span><br>");
			},
			sendMsg(){
				//var msg=$("#cnt").val();
				var msg=this.cnt;
				console.info(msg);
				//发送消息
				ws.send(msg);//将数据发送到webSocket服务端
				//将数据显示到页面
				$("#showMgs").append("<span>我:"+msg+"</span><br>");
				//this.msgs.push("<span>我:"+msg+"</span><br>");
			},
			
			//心跳机制
			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>

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java实现WebSocket消息广播可以使用Java-WebSocket库。下面是一个简单的示例代码: ```java import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.Collection; import java.util.concurrent.CopyOnWriteArrayList; import org.java_websocket.WebSocket; import org.java_websocket.handshake.ClientHandshake; import org.java_websocket.server.WebSocketServer; public class WebSocketBroadcastServer extends WebSocketServer { private Collection<WebSocket> clients; public WebSocketBroadcastServer(int port) throws UnknownHostException { super(new InetSocketAddress(port)); clients = new CopyOnWriteArrayList<>(); } @Override public void onOpen(WebSocket conn, ClientHandshake handshake) { clients.add(conn); System.out.println("New connection from " + conn.getRemoteSocketAddress().getAddress().getHostAddress()); } @Override public void onClose(WebSocket conn, int code, String reason, boolean remote) { clients.remove(conn); System.out.println("Closed connection to " + conn.getRemoteSocketAddress().getAddress().getHostAddress()); } @Override public void onMessage(WebSocket conn, String message) { System.out.println("Received message: " + message); broadcast(message); } @Override public void onError(WebSocket conn, Exception ex) { System.err.println("Error occurred on connection " + conn.getRemoteSocketAddress().getAddress().getHostAddress() + ": " + ex); } private void broadcast(String message) { for (WebSocket client : clients) { client.send(message); } System.out.println("Message broadcasted to " + clients.size() + " clients"); } public static void main(String[] args) throws UnknownHostException { int port = 8080; WebSocketBroadcastServer server = new WebSocketBroadcastServer(port); server.start(); System.out.println("WebSocket server started on port " + port); } } ``` 这个示例代码创建了一个WebSocket服务器,监听指定端口(8080)。当有新连接加入时,它将追踪所有连接的客户端,并在收到消息时将消息广播给所有客户端。 你可以根据自己的需求修改和扩展这个示例代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值