Java中Spring Boot+Socket实现与html页面的长连接实例详解

Spring Boot+Socket实现与html页面的长连接,客户端给服务器端发消息,服务器给客户端轮询发送消息,附案例源码

功能介绍

客户端给所有在线用户发送消息客户端给指定在线用户发送消息服务器给客户端发送消息(轮询方式)

注意:socket只是实现一些简单的功能,具体的还需根据自身情况,代码稍微改造下

项目搭建

项目结构图

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.3.2.RELEASE</version>
  <relativePath/> <!-- lookup parent from repository -->
 </parent>
 <groupId>com.cyb</groupId>
 <artifactId>socket_test</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <name>socket_test</name>
 <description>Demo project for Spring Boot</description>

 <properties>
  <java.version>1.8</java.version>
 </properties>

 <dependencies>
  <!-- springboot websocket -->
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-websocket</artifactId>
  </dependency>
  <!--guava依赖-->
  <dependency>
   <groupId>com.google.guava</groupId>
   <artifactId>guava</artifactId>
   <version>18.0</version>
  </dependency>
  <!--fastjson依赖-->
  <dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>fastjson</artifactId>
   <version>1.2.46</version>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
  </dependency>

  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <scope>test</scope>
   <exclusions>
    <exclusion>
     <groupId>org.junit.vintage</groupId>
     <artifactId>junit-vintage-engine</artifactId>
    </exclusion>
   </exclusions>
  </dependency>
 </dependencies>

 <build>
  <plugins>
   <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
   </plugin>
  </plugins>
 </build>

</project>

appliccation.properties

SocketTestApplication.java(Spring Boot启动类)

WebSocketStompConfig.java

package com.cyb.socket.websocket;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketStompConfig {
 //这个bean的注册,用于扫描带有@ServerEndpoint的注解成为websocket ,如果你使用外置的tomcat就不需要该配置文件
 @Bean
 public ServerEndpointExporter serverEndpointExporter()
 {
  return new ServerEndpointExporter();
 }
}

WebSocket.java(Socket核心类)

package com.cyb.socket.websocket;

import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * @Author:陈彦斌
 * @Description:Socket核心类
 * @Date: 2020-07-26
 */

@Component
@ServerEndpoint(value = "/connectWebSocket/{userId}")
public class WebSocket {

 private Logger logger = LoggerFactory.getLogger(this.getClass());
 /**
  * 在线人数
  */
 public static int onlineNumber = 0;
 /**
  * 以用户的姓名为key,WebSocket为对象保存起来
  */
 private static Map<String, WebSocket> clients = new ConcurrentHashMap<String, WebSocket>();
 /**
  * 会话
  */
 private Session session;
 /**
  * 用户名称
  */
 private String userId;

 /**
  * 建立连接
  *
  * @param session
  */
 @OnOpen
 public void onOpen(@PathParam("userId") String userId, Session session) {
  onlineNumber++;
  System.out.println("现在来连接的客户id:" + session.getId() + "用户名:" + userId);
  //logger.info("现在来连接的客户id:"+session.getId()+"用户名:"+userId);
  this.userId = userId;
  this.session = session;
  System.out.println("有新连接加入! 当前在线人数" + onlineNumber);
  // logger.info("有新连接加入! 当前在线人数" + onlineNumber);
  try {
   //messageType 1代表上线 2代表下线 3代表在线名单 4代表普通消息
   //先给所有人发送通知,说我上线了
   Map<String, Object> map1 = Maps.newHashMap();
   map1.put("messageType", 1);
   map1.put("userId", userId);
   sendMessageAll(JSON.toJSONString(map1), userId);

   //把自己的信息加入到map当中去
   clients.put(userId, this);
   System.out.println("有连接关闭! 当前在线人数" + onlineNumber);
   //logger.info("有连接关闭! 当前在线人数" + clients.size());
   //给自己发一条消息:告诉自己现在都有谁在线
   Map<String, Object> map2 = Maps.newHashMap();
   map2.put("messageType", 3);
   //移除掉自己
   Set<String> set = clients.keySet();
   map2.put("onlineUsers", set);
   sendMessageTo(JSON.toJSONString(map2), userId);
  } catch (IOException e) {
   System.out.println(userId + "上线的时候通知所有人发生了错误");
   //logger.info(userId+"上线的时候通知所有人发生了错误");
  }
 }

 @OnError
 public void onError(Session session, Throwable error) {
  //logger.info("服务端发生了错误"+error.getMessage());
  //error.printStackTrace();
  System.out.println("服务端发生了错误:" + error.getMessage());
 }

 /**
  * 连接关闭
  */
 @OnClose
 public void onClose() {
  onlineNumber--;
  //webSockets.remove(this);
  clients.remove(userId);
  try {
   //messageType 1代表上线 2代表下线 3代表在线名单 4代表普通消息
   Map<String, Object> map1 = Maps.newHashMap();
   map1.put("messageType", 2);
   map1.put("onlineUsers", clients.keySet());
   map1.put("userId", userId);
   sendMessageAll(JSON.toJSONString(map1), userId);
  } catch (IOException e) {
   System.out.println(userId + "下线的时候通知所有人发生了错误");
   //logger.info(userId+"下线的时候通知所有人发生了错误");
  }
  //logger.info("有连接关闭! 当前在线人数" + onlineNumber);
  //logger.info("有连接关闭! 当前在线人数" + clients.size());
  System.out.println("有连接关闭! 当前在线人数" + onlineNumber);
 }

 /**
  * 收到客户端的消息
  *
  * @param message 消息
  * @param session 会话
  */
 @OnMessage
 public void onMessage(String message, Session session) {
  try {
   //logger.info("来自客户端消息:" + message+"客户端的id是:"+session.getId());
   System.out.println("来自客户端消息:" + message + " | 客户端的id是:" + session.getId());
   JSONObject jsonObject = JSON.parseObject(message);
   String textMessage = jsonObject.getString("message");
   String fromuserId = jsonObject.getString("userId");
   String touserId = jsonObject.getString("to");
   //如果不是发给所有,那么就发给某一个人
   //messageType 1代表上线 2代表下线 3代表在线名单 4代表普通消息
   Map<String, Object> map1 = Maps.newHashMap();
   map1.put("messageType", 4);
   map1.put("textMessage", textMessage);
   map1.put("fromuserId", fromuserId);
   if (touserId.equals("All")) {
    map1.put("touserId", "所有人");
    sendMessageAll(JSON.toJSONString(map1), fromuserId);
   } else {
    map1.put("touserId", touserId);
    System.out.println("开始推送消息给" + touserId);
    sendMessageTo(JSON.toJSONString(map1), touserId);
   }
  } catch (Exception e) {
   e.printStackTrace();
   //logger.info("发生了错误了");
  }

 }

 /**
  * 给指定的用户发送消息
  *
  * @param message
  * @param TouserId
  * @throws IOException
  */
 public void sendMessageTo(String message, String TouserId) throws IOException {
  for (WebSocket item : clients.values()) {
   System.out.println("给指定的在线用户发送消息,在线人员名单:【" + item.userId.toString() + "】发送消息:" + message);
   if (item.userId.equals(TouserId)) {
    item.session.getAsyncRemote().sendText(message);
    break;
   }
  }
 }

 /**
  * 给所有用户发送消息
  *
  * @param message 数据
  * @param FromuserId
  * @throws IOException
  */
 public void sendMessageAll(String message, String FromuserId) throws IOException {
  for (WebSocket item : clients.values()) {
   System.out.println("给所有在线用户发送给消息,在线人员名单:【" + item.userId.toString() + "】发送消息:" + message);
   item.session.getAsyncRemote().sendText(message);
  }
 }

 /**
  * 给所有在线用户发送消息
  *
  * @param message 数据
  * @throws IOException
  */
 public void sendMessageAll(String message) throws IOException {
  for (WebSocket item : clients.values()) {
   System.out.println("服务器给所有在线用户发送消息,当前在线人员为【" + item.userId.toString() + "】发送消息:" + message);
   item.session.getAsyncRemote().sendText(message);
  }
 }

 /**
  * 获取在线用户数
  *
  * @return
  */
 public static synchronized int getOnlineCount() {
  return onlineNumber;
 }
}

TestController.java(前端控制器)

package com.cyb.socket.websocket;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;

@Controller
@RequestMapping("testMethod")
public class TestController {
 @Autowired
 private WebSocket webSocket;

 /**
  * 给指定的在线用户发送消息
  * @param userId
  * @param msg
  * @return
  * @throws IOException
  */
 @ResponseBody
 @GetMapping("/sendTo")
 public String sendTo(@RequestParam("userId") String userId,@RequestParam("msg") String msg) throws IOException {
  webSocket.sendMessageTo(msg,userId);
  return "推送成功";
 }

 /**
  * 给所有在线用户发送消息
  * @param msg
  * @return
  * @throws IOException
  * @throws IOException
  */
 @ResponseBody
 @PostMapping("/sendAll")
 public String sendAll(@RequestBody String msg) throws IOException, IOException {
  webSocket.sendMessageAll(msg);
  return "推送成功";
 }
}

SocketTask.java(轮询调度往客户端推送消息)

package com.cyb.socket.schedule;

import com.cyb.socket.websocket.WebSocket;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

@Component
public class SocketTask {
 @Autowired
 private WebSocket webSocket;
 private SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS" );
 //5秒轮询一次
 @Scheduled(fixedRate = 5000)
 public void sendClientData() throws IOException {
  String msg="{\"message\":\"你好\",\"userId\":\"002\",\"to\":\"All\"}";
  webSocket.sendMessageAll(msg);
  System.out.println("消息推送时间:"+ sdf.format(new Date()));
 }
}

测试网页

index.html

<!DOCTYPE HTML>
<html>
<head>
 <title>Test My WebSocket</title>
</head>

<body>
TestWebSocket
<input id="text" type="text" style="width:500px"/>
<button onclick="send()">SEND MESSAGE</button>
<button onclick="closeWebSocket()">CLOSE</button>
<div id="message"></div>
</body>

<script type="text/javascript">
 var websocket = null;

 //判断当前浏览器是否支持WebSocket
 if('WebSocket' in window){
  //连接WebSocket节点
  websocket = new WebSocket("ws://localhost:8083/connectWebSocket/001");
 }
 else{
  alert('Not support websocket')
 }

 //连接发生错误的回调方法
 websocket.onerror = function(){
  setMessageInnerHTML("error");
 };

 //连接成功建立的回调方法
 websocket.onopen = function(event){
  setMessageInnerHTML("open");
 }

 //接收到消息的回调方法
 websocket.onmessage = function(event){
  setMessageInnerHTML(event.data);
 }

 //连接关闭的回调方法
 websocket.onclose = function(){
  setMessageInnerHTML("close");
 }

 //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
 window.onbeforeunload = function(){
  websocket.close();
 }

 //将消息显示在网页上
 function setMessageInnerHTML(innerHTML){
  document.getElementById('message').innerHTML += innerHTML + '<br/>';
 }

 //关闭连接
 function closeWebSocket(){
  websocket.close();
 }

 //发送消息
 function send(){
  var message = document.getElementById('text').value;
  websocket.send(message);
 }
</script>
</html>

index2.html

<!DOCTYPE HTML>
<html>
<head>
 <title>Test My WebSocket</title>
</head>

<body>
TestWebSocket
<input id="text" type="text" style="width:500px" />
<button onclick="send()">SEND MESSAGE</button>
<button onclick="closeWebSocket()">CLOSE</button>
<div id="message"></div>
</body>

<script type="text/javascript">
 var websocket = null;

 //判断当前浏览器是否支持WebSocket
 if('WebSocket' in window){
  //连接WebSocket节点
  websocket = new WebSocket("ws://localhost:8083/connectWebSocket/002");
 }
 else{
  alert('Not support websocket')
 }

 //连接发生错误的回调方法
 websocket.onerror = function(){
  setMessageInnerHTML("error");
 };

 //连接成功建立的回调方法
 websocket.onopen = function(event){
  setMessageInnerHTML("open");
 }

 //接收到消息的回调方法
 websocket.onmessage = function(event){
  setMessageInnerHTML(event.data);
 }

 //连接关闭的回调方法
 websocket.onclose = function(){
  setMessageInnerHTML("close");
 }

 //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
 window.onbeforeunload = function(){
  websocket.close();
 }

 //将消息显示在网页上
 function setMessageInnerHTML(innerHTML){
  document.getElementById('message').innerHTML += innerHTML + '<br/>';
 }

 //关闭连接
 function closeWebSocket(){
  websocket.close();
 }

 //发送消息
 function send(){
  var message = document.getElementById('text').value;
  websocket.send(message);
 }
</script>
</html>

项目地址

链接:https://pan.baidu.com/s/1yiAXTkCjHac-F3S1HFyNJQ 提取码:53tp

功能演示

客户端给所有在线用户发消息

客户端给指定在线用户发送消息

服务器给客户端发送消息(轮询方式)

注意需要加上这些注解

演示

通过前端控制器给指定用户发送消息

演示

到此这篇关于Java中Spring Boot+Socket实现与html页面的长连接实例详解的文章就介绍到这了,更多相关Java Spring Boot+Socket实现与html页面的长连接内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • java socket长连接中解决read阻塞的3个办法

    解决的方法有3个 : 1 约定发送的数据长度,比如 http的 keepAlive 就是必须依赖这个的 Content-Length 2 设置超时的时间,根据我的经验,只有在Socket级别设置才有效. 复制代码 代码如下: Socket socket = new Socket(host,port); socket.setSoTimeout(100); // 如果超过100毫秒还没有数据,则抛出 SocketTimeoutException 3 让发送端发送完数据后,关闭连接. 这个在Http的

  • Java Web项目中使用Socket通信多线程、长连接的方法

    很多时候在javaweb项目中我们需要用到Socket通信来实现功能,在web中使用Socket我们需要建立一个监听程序,在程序启动时,启动socket监听.我们的应用场景是在java项目中,需要外接如一个硬件设备,通过tcp通信,获取设备传上来的数据,并对数据做回应. 先看一下web的监听代码: import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class

  • JAVA实现长连接(含心跳检测Demo)

    实现原理:        长连接的维持,是要客户端程序,定时向服务端程序,发送一个维持连接包的.        如果,长时间未发送维持连接包,服务端程序将断开连接. 客户端:        Client通过持有Socket的对象,可以随时(使用sendObject方法)发送Massage Object(消息)给服务端.        如果keepAliveDelay毫秒(程序中是2秒)内未发送任何数据,则自动发送一个KeepAlive Object(心跳)给服务端,用于维持连接.       

  • java与微信小程序实现websocket长连接

    本文实例为大家分享了java与微信小程序实现websocket长连接的具体代码,供大家参考,具体内容如下 背景: 需要在小程序实现地图固定坐标下实时查看消息 java环境 :tomcat7 jdk1.7 1.java websocket 类 package com.qs.util; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.

  • Java中Spring Boot+Socket实现与html页面的长连接实例详解

    Spring Boot+Socket实现与html页面的长连接,客户端给服务器端发消息,服务器给客户端轮询发送消息,附案例源码 功能介绍 客户端给所有在线用户发送消息客户端给指定在线用户发送消息服务器给客户端发送消息(轮询方式) 注意:socket只是实现一些简单的功能,具体的还需根据自身情况,代码稍微改造下 项目搭建 项目结构图 pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xml

  • Spring boot jpa 删除数据和事务管理的问题实例详解

    今天我们介绍的是jpa删除和事务的一些坑,接下来看看具体内容. 业务场景(这是一个在线考试系统)和代码:根据问题的id删除答案 repository层: int deleteByQuestionId(Integer questionId); service 层: public void deleteChoiceAnswerByQuestionId(Integer questionId) { choiceAnswerRepository.deleteByQuestionId(questionId)

  • Spring Boot 定制与优化内置的Tomcat容器实例详解

    1.Spring Boot 定制与优化内置Tomcat容器. > 内置的容器有三个分别是Undertow.Jetty.Tomcat,Spring Boot 对这三个容器分别进行了实现,它们上层接口都是EmbeddedServletContainerFactory,该接口也是本文的主要核心. 对于内置容器的定制与优化主要有两种方式,第一种方式是通过配置文件来配置,另外一种是通过码代码的方式.接下来主要对上述两种方式进行实现. 2.通过配置文件来定制与优化Tomcat > 配置的核心内容参考org

  • spring boot+vue 的前后端分离与合并方案实例详解

    springboot和vue结合的方案网络上的主要有以下两种: 1. [不推荐]在html中直接使用script标签引入vue和一些常用的组件,这种方式和以前传统的开发是一样的,只是可以很爽的使用vue的双向数据绑定,这种方式只适合于普通的全栈开发. 2.[推荐]使用vue官方的脚手架创建单独的前端工程项目,做到和后端完全独立开发和部署,后端单独部署一个纯restful的服务,而前端直接采用nginx来部署,这种称为完全的前后端分离架构开发模式,但是在分离中有很多api权限的问题需要解决,包括部

  • Java中的Unsafe在安全领域的使用总结和复现(实例详解)

    目录 0前言 1基本使用 1.1内存级别修改值 1.2创建对象 1.3创建VMAnonymousClass 2利用姿势 2.1修改值以关闭RASP等防御措施 2.2创建NativeLibrary对象实现webshell 2.3匿名的内存马 2.4shellcode和instrumentation对象构建 3总结 参考: 总结并复现了一下Unsafe在安全领域的一些应用 0 前言 unsafe里面有很多好用的方法,比如allocateInstance可以直接创建实例对象,defineAnonymo

  • Java中Spring Boot支付宝扫码支付及支付回调的实现代码

    前言:最近开发支付宝支付功能,总结一下做个分享 官方文档:https://opendocs.alipay.com/apis 支付宝沙箱地址: https://openhome.alipay.com/platform/appDaily.htm?tab=info 支付宝支付流程: 准备工作:获取支付宝沙箱数据(APPID,支付宝网关,RSA2秘,沙箱支付账号等) 集成SpringBoot,使用Java代码发起支付请求 支付宝收到支付请求后,返回HTML代码片段,用于前端展示二维码 扫码支付成功后,支

  • Android中判断网络是否连接实例详解

    Android中判断网络是否连接实例详解 在android中,如何监测网络的状态呢,这个有的时候也是十分重要的,方法如下: public class ConnectionDetector { private Context _context; public ConnectionDetector(Context context){ this._context = context; } public boolean isConnectingToInternet(){ ConnectivityMana

  • 浅谈Java中Spring Boot的优势

    Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置. Spring Boot 简化了基于 Spring 的应用开发,通过少量的代码就能创建一个独立的.产品级别的 Spring 应用. 作为一名 Java 程序员如果你已经厌恶了传统的开发模式,希望有一个全新的框架可以提供快速开发,简单集成的编程体验,强烈建议你学习了解 Spring Boot .

  • Spring boot工具类静态属性注入及多环境配置详解

    由于需要访问MongoDB,但是本地开发环境不能直接连接MongoDB,需要通过SecureCRT使用127.0.0.2本地IP代理.但是程序部署到线上生产环境后,是可以直接访问MongoDB的,因此开发好程序后,总是要修改一下MongoDB服务器的IP才能提交代码,这样很是不方便. private static final String PUBCHAT_HOST = "127.0.0.2"; // private static final String PUBCHAT_HOST =

  • Spring Boot 通过 Mvc 扩展方便进行货币单位转换的代码详解

    由于公司是支付平台,所以很多项目都涉及到金额,业务方转递过来的金额是单位是元,而我们数据库保存的金额单位是分.一般金额的流向有以下几个方向: 外部业务方请求我们服务,传递过来的金额单位是元,需要把元转换成分.比如:下单接口. 内部系统之间的流转,不管是向下传递还是向上传递系统间的流程都是分,不需要扭转.比如:调用支付引擎(向下传递),支付引擎回调收单业务(向上传递). 向业务方返回数据,这个时候需要把分转换成元.比如:商户调用查询订单接口. 内部系统的展示,这个时候需要把分转换成元.比如:显示收

随机推荐