基于 WebSocket 的聊天室
原文链接: crossoverjie.top
前言
不知大家在平时的需求中有没有遇到需要实时处理信息的情况,如站内信,订阅,聊天之类的。在这之前我们通常想到的方法一般都是采用轮训的方式每隔一定的时间向服务器发送请求从而获得最新的数据,但这样会浪费掉很多的资源并且也不是实时的,于是随着
HTML5
的推出带来了
websocket
可以根本的解决以上问题实现真正的实时传输。
WebSocket 是什么?
至于
websocket
是什么、有什么用这样的问题一 Google 一大把,这里我就简要的说些
websocket
再本次实例中的作用吧。
由于在本次实例中需要实现的是一个聊天室,一个实时的聊天室。如下图:
采用
websocket
之后可以让前端和和后端像 C/S 模式一样实时通信,不再需要每次单独发送请求。由于是基于 H5 的所以对于老的浏览器如 IE7、IE8 之类的就没办法了,不过 H5 是大势所趋这点不用担心。
后端
既然推出了
websocket
,作为现在主流的 Java 肯定也有相应的支持,所以在
JavaEE7
之后也对
websocket
做出了规范,所以本次的代码理论上是要运行在
Java1.7
+ 和
Tomcat7.0+
之上的。
看过我前面几篇文章的朋友应该都知道本次实例也是运行在之前的
SSM
之上的,所以这里就不再赘述了。
首先第一步需要加入
websocket
的依赖:
``` < dependency> < groupid>javax.websocket< /groupid> < artifactid>javax.websocket-api< /artifactid> < version>1.1< /version> < /dependency>
< dependency> < groupid>org.springframework< /groupid> < artifactid>spring-websocket < version>${spring.version}< /version> < /dependency> ```
以上就是使用
websocket
所需要用到的包。
spring-websocket
这个主要是在之后需要在
websocket
的后端注入
service
所需要的。
之后再看一下后端的核心代码
MyWebSocket.java
``` package com.crossoverJie.controller;
/* * Created by Administrator on 2016/8/7. / import com.crossoverJie.pojo.Content; import com.crossoverJie.service.ContentService; import org.apache.camel.BeanInject; import org.apache.camel.EndpointInject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.stereotype.Controller; import org.springframework.web.context.support.SpringBeanAutowiringSupport; import org.springframework.web.socket.server.standard.SpringConfigurator;
import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.CopyOnWriteArraySet;
import javax.annotation.PostConstruct; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint;
//该注解用来指定一个URI,客户端可以通过这个URI来连接到WebSocket。 /* 类似Servlet的注解mapping。无需在web.xml中配置。 * configurator = SpringConfigurator.class是为了使该类可以通过Spring注入。 / @ServerEndpoint(value = "/websocket",configurator = SpringConfigurator.class) public class MyWebSocket { //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。 private static int onlineCount = 0;
public MyWebSocket() {
}
@Autowired
private ContentService contentService ;
//concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
// 若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
private static CopyOnWriteArraySet webSocketSet = new CopyOnWriteArraySet();
//与客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
/**
* 连接建立成功调用的方法
* @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
@OnOpen
public void onOpen(Session session){
this.session = session;
webSocketSet.add(this); //加入set中
addOnlineCount(); //在线数加1
System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose(){
webSocketSet.remove(this); //从set中删除
subOnlineCount(); //在线数减1
System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
* @param message 客户端发送过来的消息
* @param session 可选的参数
*/
@OnMessage
public void onMessage(String message, Session session) {
System.out.println("来自客户端的消息:" + message);
//群发消息
for(MyWebSocket item: webSocketSet){
try {
item.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
continue;
}
}
}
/**
* 发生错误时调用
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error){
System.out.println("发生错误");
error.printStackTrace();
}
/**
* 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。
* @param message
* @throws IOException
*/
public void sendMessage(String message) throws IOException{
//保存数据到数据库
Content content = new Content() ;
content.setContent(message);
SimpleDateFormat sm = new SimpleDateFormat("yyyy-MM-dd HH:mm:dd") ;
content.setCreateDate(sm.format(new Date()));
contentService.insertSelective(content) ;
this.session.getBasicRemote().sendText(message);
//this.session.getAsyncRemote().sendText(message);
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
MyWebSocket.onlineCount++;
}
public static synchronized void subOnlineCount() {
MyWebSocket.onlineCount--;
}
} ```
这就是整个
websocket
的后端代码。看起来也比较简单主要就是使用那几个注解。每当有一个客户端连入、关闭、发送消息都会调用各自注解的方法。这里我讲一下
sendMessage()
这个方法。
WebSocket 绕坑
在
sendMessage()
方法中我只想实现一个简单的功能,就是将每次的聊天记录都存到数据库中。看似一个简单的功能硬是花了我半天的时间。
我先是按照以前的惯性思维只需要在这个类中注入
service
即可。但是无论怎么弄每次都注入不进来都是
null
。
最后没办法只有 Google 了,最后终于在神级社区
StackOverFlow
中找到了答案,就是前边所说的需要添加的第二个
maven
依赖,然后加入
@ServerEndpoint(value = "/websocket",configurator = SpringConfigurator.class)
这个注解即可利用
Spring
注入了。接着就可以做消息的保存了。
前端
前端我采用了 Bootstrap 做的,不太清楚 Bootstrap 的童鞋建议先看下 官方文档 也比较简单。还是先贴一下代码:
``` <%@ page language="java" import="java.util.*" pageEncoding="UTF-8" %> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> < base href="http://crossoverjie.top/2016/09/04/SSM5/<%=basePath%>"> < title>聊天室< /title> < input id="text" type="text"> < button>发送< /button> < button>关闭连接< /button> 聊天室 在线人数1人 < p> < button>发送< /button> < /p>
```
其实其中重要的就是那几个 JS 方法,都写有注释。需要注意的是这里
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
websocket = new WebSocket("ws://192.168.0.102:8080/ssm/websocket");
}
else {
alert("对不起!你的浏览器不支持webSocket")
}
当项目跑起来之后需要将这里的地址改为你项目的地址即可。
哦对了,我在这里采用了百度的一个
Ueditor
的富文本编辑器(虽然百度搜索我现在很少用了,但是这个编辑器确实还不错),这个编辑器也比较简单只需要个性化的配置一下个人的需求即可。
Ueditor 相关配置
直接使用我项目运行的童鞋就不需要重新下载了,我将资源放在了 webapp 目录下的 ueditor 文件夹下面的。
值得注意的是我们首先需要将
jsp-->lib
下的 jar 包加入到项目中。加好之后会出现一个想下的箭头表示已经引入成功。
,之后修改该目录下的
config.json
文件,主要修改以下内容即可:
``` "imageAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], / 上传图片格式显示 / "imageCompressEnable": true, / 是否压缩图片,默认是true / "imageCompressBorder": 1600, / 图片压缩最长边限制 / "imageInsertAlign": "none", / 插入的图片浮动方式 / "imageUrlPrefix": "http://192.168.0.102:8080/ssm", / 图片访问路径前缀 / "imagePathFormat": "/ueditor/jsp/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}",
```
这里主要是要修改
imageUrlPrefix
为你自己的项目地址就可以了。
ueditor
一个我认为很不错的就是他支持图片、多图、截图上传,而且都不需要手动编写后端接口,所有上传的文件、图片都会保存到项目发布出去的
jsp-->upload
文件夹下一看就明白了。更多关于
ueditor
的配置可以查看
官网
。
其中值得注意一点的是,由于项目采用了
Spring MVC
并拦截了所有的请求,导致静态资源不能访问,如果是需要用到上传txt
文件之类的需求可以参照web.xml
中修改,如下:
< servlet-mapping> < servlet-name>default< /servlet-name> < url-pattern>*.txt< /url-pattern> < /servlet-mapping>
这样就可以访问 txt 文件了,如果还需要上传 PPT 之类的就以此类推。
总结
这样一个简单的基于
websocket
的聊天室就算完成了,感兴趣的朋友可以将项目部署到外网服务器上这样好基友之间就可以愉快的聊(zhuang)天(bi)了。
当然这只是一个简单的项目,感兴趣的朋友再这基础之上加入实时在线人数,用户名和 IP 之类的。
参考文献
- 面向学生的商业信息公布和话题讨论平台(吉林大学·张振中)
- 基于Struts+JSP的SNS网站系统的设计与实现(吉林大学·王雷)
- WEB QQ——基于JMS技术并集成在服务器端的即时通讯系统(成都理工大学·李健)
- 手机软酷网即时通讯软件的设计与实现(电子科技大学·齐迎旭)
- 基于SSH架构的个人空间交友网站的设计与实现(北京邮电大学·隋昕航)
- NetAccount宽带计费系统中基于Web的系统管理子系统及用户功能子系统的设计与实现(苏州大学·王锋)
- 基于JSP的网上聊天室系统的设计与实现(电子科技大学·任飞)
- 基层部队网站的设计与建设(昆明理工大学·罗顺先)
- 齐齐哈尔合众商务科技有限公司门户网站设计(吉林大学·孟云飞)
- 基于WebSocket协议的实时网页通信的研究与实现(江苏科技大学·包文祥)
- “Things-Cloud-People”:一个“Web of Things”实现方案(华东师范大学·汤承刚)
- 基于SSH的大学生联谊交友管理系统设计与实现(华中科技大学·王海波)
- 基于J2EE的辽油通信小灵通服务下载系统的设计与实现(电子科技大学·吴文哲)
- 基于.NET平台的游戏门户系统设计与实现(电子科技大学·余胜鹏)
- 一种基于UNIX的互联网站搭建方案的关键技术与WEB2.0(中国水利水电科学研究院·罗皓)
本文内容包括但不限于文字、数据、图表及超链接等)均来源于该信息及资料的相关主题。发布者:源码码头网 ,原文地址:https://bishedaima.com/yuanma/35820.html