网易首页 > 网易号 > 正文 申请入驻

【181期】Spring + WebSocket + Quartz 实现聊天框,定时推送消息

0
分享至

点击上方“Java精选”,选择“设为星标”

别问别人为什么,多问自己凭什么!

下方有惊喜,留言必回,有问必答!

每天 08:35 更新文章,每天进步一点点...

websocket

简单的说,websocket是真正实现了全双工通信的服务器向客户端推的互联网技术。

全双工与单工、半双工的区别?

  • 全双工:简单地说,就是可以同时进行信号的双向传输(A->B且B->A),是瞬时同步的。

  • 单工、半双工:一个时间段内只有一个动作发生。

推送和拉取的区别?

  • 推:由服务器主动发消息给客户端,就像广播。优势在于,信息的主动性和及时性。

  • 拉:由客户端主动请求所需要的数据。

实现消息通信的几种方式?

  • 传统的http协议实现方式:。

  • 传统的socket技术。

  • websocket协议实现方式。

接下来我们主要讲第三种,使用websocket协议,来实现服务端定时向客户端推送消息。

  • 开发环境:jdk1.8、tomcat7

  • 后台:springmvc、websocket、quartz

  • 前台:html5中新增的API

  • 开发工具:IDEA、maven

实现步骤 一、环境搭建 1)导入相关约束:

在pom文件中加入需要的约束,spring相关的约束,请各位自己导入,这里我就不贴出来了。


org.quartz-schedulergroupId>
quartzartifactId>
2.3.0version>
dependency>


org.springframeworkgroupId>
spring-context-supportartifactId>
5.1.1.RELEASEversion>
dependency>


javax.websocketgroupId>
javax.websocket-apiartifactId>
1.1version>
providedscope>
dependency>

web.xml中就配置前端控制器,大家自行配置。然后,加载springmvc的配置文件。

springmvc.xml文件中



bean>


text/html;charset=UTF-8value>
application/jsonvalue>
list>
property>
WriteMapNullValuevalue>
QuoteFieldNamesvalue>
list>
property>
bean>
mvc:message-converters>
mvc:annotation-driven>

到此,环境就基本搭建完成了。

这里我就直接贴出代码了,上面有相关的注释。

首先,完成websocket的实现类。

package com.socket.web.socket;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
* @Author: 清风一阵吹我心
* @ProjectName: socket
* @Package: com.socket.web.socket
* @ClassName: WebSocketServer
* @Description:
* @Version: 1.0
**/
//ServerEndpoint它的功能主要是将目前的类定义成一个websocket服务器端。注解的值将被用于监听用户连接的终端访问URL地址。
@ServerEndpoint(value = "/socket/{ip}")
@Component
public class WebSocketServer {

//使用slf4j打日志
private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketServer.class);

//用来记录当前在线连接数
private static int onLineCount = 0;

//用来存放每个客户端对应的WebSocketServer对象
private static ConcurrentHashMap webSocketMap = new ConcurrentHashMap();

//某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;

//客户端的ip地址
private String ip;

/**
* 连接建立成功,调用的方法,与前台页面的onOpen相对应
* @param ip ip地址
* @param session 会话
*/
@OnOpen
public void onOpen(@PathParam("ip")String ip,Session session){
//根据业务,自定义逻辑实现
this.session = session;
this.ip = ip;
webSocketMap.put(ip,this); //将当前对象放入map中
addOnLineCount(); //在线人数加一
LOGGER.info("有新的连接加入,ip:{}!当前在线人数:{}",ip,getOnLineCount());
}

/**
* 连接关闭调用的方法,与前台页面的onClose相对应
* @param ip
*/
@OnClose
public void onClose(@PathParam("ip")String ip){
webSocketMap.remove(ip); //根据ip(key)移除WebSocketServer对象
subOnLineCount();
LOGGER.info("WebSocket关闭,ip:{},当前在线人数:{}",ip,getOnLineCount());
}

/**
* 当服务器接收到客户端发送的消息时所调用的方法,与前台页面的onMessage相对应
* @param message
* @param session
*/
@OnMessage
public void onMessage(String message,Session session){
//根据业务,自定义逻辑实现
LOGGER.info("收到客户端的消息:{}",message);
}

/**
* 发生错误时调用,与前台页面的onError相对应
* @param session
* @param error
*/
@OnError
public void onError(Session session,Throwable error){
LOGGER.error("WebSocket发生错误");
error.printStackTrace();
}

/**
* 给当前用户发送消息
* @param message
*/
public void sendMessage(String message){
try{
//getBasicRemote()是同步发送消息,这里我就用这个了,推荐大家使用getAsyncRemote()异步
this.session.getBasicRemote().sendText(message);
}catch (IOException e){
e.printStackTrace();
LOGGER.info("发送数据错误:,ip:{},message:{}",ip,message);
}
}

/**
* 给所有用户发消息
* @param message
*/
public static void sendMessageAll(final String message){
//使用entrySet而不是用keySet的原因是,entrySet体现了map的映射关系,遍历获取数据更快。
Set> entries = webSocketMap.entrySet();
for (Map.Entry entry : entries) {
final WebSocketServer webSocketServer = entry.getValue();
//这里使用线程来控制消息的发送,这样效率更高。
new Thread(new Runnable() {
public void run() {
webSocketServer.sendMessage(message);
}
}).start();
}
}

/**
* 获取当前的连接数
* @return
*/
public static synchronized int getOnLineCount(){
return WebSocketServer.onLineCount;
}

/**
* 有新的用户连接时,连接数自加1
*/
public static synchronized void addOnLineCount(){
WebSocketServer.onLineCount++;
}

/**
* 断开连接时,连接数自减1
*/
public static synchronized void subOnLineCount(){
WebSocketServer.onLineCount--;
}

public Session getSession(){
return session;
}
public void setSession(Session session){
this.session = session;
}

public static ConcurrentHashMap getWebSocketMap() {
return webSocketMap;
}

public static void setWebSocketMap(ConcurrentHashMap webSocketMap) {
WebSocketServer.webSocketMap = webSocketMap;
}
}

然后写我们的定时器(quartz),这里我就不详解定时器了。大家可以自行去了解。

这里我使用的是xml注解的方式,创建一个job类,此类不需要继承任何类和实现任何接口。

package com.socket.web.quartz;

import com.socket.web.socket.WebSocketServer;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* @Author: 清风一阵吹我心
* @ProjectName: socket
* @Package: com.socket.web.quartz
* @ClassName: TestJob
* @Description:
* @Version: 1.0
**/
public class TestJob {

public void task(){
//获取WebSocketServer对象的映射。
ConcurrentHashMap map = WebSocketServer.getWebSocketMap();
if (map.size() != 0){
for (Map.Entry entry : map.entrySet()) {
WebSocketServer webSocketServer = entry.getValue();
try {
//向客户端推送消息
webSocketServer.getSession().getBasicRemote().sendText("每隔两秒,向客户端推送一次数据");
}catch (IOException e){
e.printStackTrace();
}
}
}else {
System.out.println("WebSocket未连接");
}
}
}

定时器的实现类就完成了,我们还需要在springmvc.xml中进行配置

springmvc.xml配置:


bean>


property>


bean>


bean>


list>
property>
bean>

接下来是controller层的代码,就一个登录的功能。

package com.socket.web.controller;

import com.socket.domain.User;
import com.sun.org.apache.bcel.internal.generic.RETURN;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.UUID;

/**
* @Author: 清风一阵吹我心
* @ProjectName: socket
* @Package: com.socket.web
* @ClassName: ChatController
* @Description:
* @CreateDate: 2018/11/9 11:04
* @Version: 1.0
**/
@RequestMapping("socket")
@Controller
public class ChatController {

/**
* 跳转到登录页面
* @return
*/
@RequestMapping(value = "/login",method = RequestMethod.GET)
public String goLogin(){
return "login";
}

/**
* 跳转到聊天页面
* @param request
* @return
*/
@RequestMapping(value = "/home",method = RequestMethod.GET)
public String goMain(HttpServletRequest request){
HttpSession session = request.getSession();
if (null == session.getAttribute("USER_SESSION")){
return "login";
}
return "home";
}

@RequestMapping(value = "/login",method = RequestMethod.POST)
public String login(User user, HttpServletRequest request){
HttpSession session = request.getSession();
//将用户放入session
session.setAttribute("USER_SESSION",user);
return "redirect:home";
}

}

以上就是登录的代码了,基本上就是伪代码,只要输入用户名就可以了,后面的逻辑,大家可以根据自己的业务来实现。

最后就是前台页面的设计了,登录,login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
登录title>
head>

登录名:
form>
body>
html>

消息接收页面,home.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
聊天title></p>

2)配置xml文件

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
“姜萍连题目都看不懂”,北大硕士赵斌500万对赌,称愿承担后果

“姜萍连题目都看不懂”,北大硕士赵斌500万对赌,称愿承担后果

妍妍教育日记
2024-06-19 15:56:18
男子误机花6000从天津打车到西安:出租车司机路上一直问,“兄弟你确定?”

男子误机花6000从天津打车到西安:出租车司机路上一直问,“兄弟你确定?”

天津族
2024-06-20 07:39:06
要大结局了?仁爱礁菲舰出现6米大裂缝,坐滩人员已中断补给25天

要大结局了?仁爱礁菲舰出现6米大裂缝,坐滩人员已中断补给25天

胖福的小木屋
2024-06-17 23:57:30
外媒污蔑中国在英军帽徽中装跟踪器,知名大V:幸亏我们不和傻子生活在一个国家!

外媒污蔑中国在英军帽徽中装跟踪器,知名大V:幸亏我们不和傻子生活在一个国家!

不掉线电波
2024-06-20 12:01:30
永远不要做压死骆驼的最后一根稻草

永远不要做压死骆驼的最后一根稻草

洞见
2024-06-18 21:48:38
社会上流行着“不欠祖国只欠父母”的思想,非常可怕

社会上流行着“不欠祖国只欠父母”的思想,非常可怕

雪莉故事汇
2024-06-18 08:56:23
抗日神剧八路军的伙食,不是海鲜就是法国菜,主打的就是一个上流

抗日神剧八路军的伙食,不是海鲜就是法国菜,主打的就是一个上流

附允历史观
2024-06-19 16:45:10
两性疑问:为什么男生更喜欢从后面来

两性疑问:为什么男生更喜欢从后面来

坟头长草
2024-05-30 16:33:38
黄一鸣“杀疯了”!直播间卖大葱养孩子,王思聪被整得完全没脾气

黄一鸣“杀疯了”!直播间卖大葱养孩子,王思聪被整得完全没脾气

萌神木木
2024-06-18 21:18:32
布林克INS发文:永远不会想到会发生在自己身上 但这会让我更强大

布林克INS发文:永远不会想到会发生在自己身上 但这会让我更强大

直播吧
2024-06-20 07:57:09
解放战争中,如果国民党获得胜利,今天的中国会是什么样

解放战争中,如果国民党获得胜利,今天的中国会是什么样

史诗长歌
2024-05-13 13:34:32
上海这一夜,由王中磊牵头的明星聚会,将华谊的落魄展现淋漓尽致

上海这一夜,由王中磊牵头的明星聚会,将华谊的落魄展现淋漓尽致

先人后记
2024-06-19 00:01:03
央国企开始疯狂卖资产!

央国企开始疯狂卖资产!

樱桃大房子
2024-06-20 21:46:53
50岁富婆夜间约会帅哥,凌晨对方哀求“歇一会吧”,竟还心怀叵胎

50岁富婆夜间约会帅哥,凌晨对方哀求“歇一会吧”,竟还心怀叵胎

四象八卦
2024-06-20 16:42:28
著名女优玩偶姐姐HongKongDoll,被爆料真实面目?

著名女优玩偶姐姐HongKongDoll,被爆料真实面目?

吃瓜党二号头目
2024-06-13 10:15:52
被保险人“呼吸心跳骤停”死亡,保险公司以“猝死”拒赔引争议

被保险人“呼吸心跳骤停”死亡,保险公司以“猝死”拒赔引争议

澎湃新闻
2024-06-20 19:56:35
1.8亿全是水分?皇马巨星被狠批:眼神防守,一碰就倒,全场0射门

1.8亿全是水分?皇马巨星被狠批:眼神防守,一碰就倒,全场0射门

我的护球最独特
2024-06-21 02:37:24
曝45岁伏明霞离婚,净身出户原因揭晓,71岁百亿丈夫只说6个字

曝45岁伏明霞离婚,净身出户原因揭晓,71岁百亿丈夫只说6个字

深度知局
2024-05-20 19:25:53
尴尬,国际乒联向王曼昱发来奥运单打邀请,但女单为孙颖莎、陈梦

尴尬,国际乒联向王曼昱发来奥运单打邀请,但女单为孙颖莎、陈梦

尘语者
2024-06-19 21:45:38
援手已到,中方90艘舰船就位,南海反包围开始,菲船后撤32海里

援手已到,中方90艘舰船就位,南海反包围开始,菲船后撤32海里

红心说娱乐
2024-06-19 18:52:43
2024-06-21 05:18:44
Java精选
Java精选
一场永远也演不完的戏
1551文章数 3855关注度
往期回顾 全部

科技要闻

小米SU7流量泼天,富贵却被蔚来接住了

头条要闻

欧洲杯:凯恩破门 英格兰1-1丹麦

头条要闻

欧洲杯:凯恩破门 英格兰1-1丹麦

体育要闻

千夫所指的关系户 成了拯救葡萄牙的英雄

娱乐要闻

叶舒华参加柯震东生日聚会,五毒俱全

财经要闻

楼市新“王炸”!释放何信号?

汽车要闻

售价11.79-14.39万元 新一代哈弗H6正式上市

态度原创

家居
艺术
游戏
旅游
教育

家居要闻

自然开放 实现灵动可变空间

艺术要闻

穿越时空的艺术:《马可·波罗》AI沉浸影片探索人类文明

魔兽世界怀旧服即将开服,正式服何时开放?官方早已透露玄机!

旅游要闻

铁路儿童票新规 已有超4900万小旅客免费出行

教育要闻

学霸的题目,当然要学霸来做了!学渣挤眉弄眼一个字都没写出来

无障碍浏览 进入关怀版