tomcat websocket 实现网页在线即时聊天
背景介绍
近一个月完成了公司的一个项目,负责即时聊天部分
寻找了一下,决定使用websocket,要问原因的话,因为tomcat 自带相关消息收发的API,用起来方便
闲话少叙,进入实现步骤
使用工具 java 1.6 tomcat 7.0.27以上版本(以下版本不支持websocket),本人使用的是 7.0.42版本
先概括说一下:(看着迷糊没关系,下面有提供完整源码,可以下载后运行 ,结合效果自行分析)
先写一个,消息处理类,继承 tomcat的catalina.jar的MessageInbound类,并创建一个构造方法获取登录名
继承onPen,onClose等方法对用户上线下线进行监听,重写onTextMessage(CharBuffer msg)方法,
实现消息的处理,接收和发送
再写一个网页应用必须的servlet类接收界面请求,继承WebSocketServlet类,实现登陆监控
既然是即时聊天,则需要知道在线用户有哪些,则需要一个在线用户容器类
另外,消息send(),参数只有一个,所以里面会包含消息发送者和消息接收者,需要解析,所以写了一个解析类
那么开始详细代码解释
- package socket;
- import java.io.IOException;
- import java.nio.ByteBuffer;
- import java.nio.CharBuffer;
- import java.util.HashMap;
- import org.apache.catalina.websocket.MessageInbound;
- import org.apache.catalina.websocket.WsOutbound;
- import util.MessageUtil;
- public class MyMessageInbound extends MessageInbound {
- // 用户姓名
- private String name;
- public MyMessageInbound() {
- super();
- }
- public MyMessageInbound(String name) {
- super();
- this.name = name;
- }
- @Override
- protected void onBinaryMessage(ByteBuffer arg0) throws IOException {
- }
- @Override
- protected void onTextMessage(CharBuffer msg) throws IOException {
- // 用户所发消息处理后的map
- HashMap<String,String> messageMap = MessageUtil.getMessage(msg); //处理消息类
- // 在线用户集合类map
- HashMap<String, MessageInbound> userMsgMap = InitServlet.getSocketMap();
- String fromName = messageMap.get("fromName"); // 消息来源
- String toName = messageMap.get("toName"); // 消息接收人
- //获取该用户
- MessageInbound messageInbound = userMsgMap.get(toName); //在容器中获得接收人的MessageInbound
- MessageInbound messageFromInbound = userMsgMap.get(fromName); //在容器中获得发送人的MessageInbound
- if(messageInbound!=null && messageFromInbound!=null){ // 如果发往人 存在进行操作
- WsOutbound outbound = messageInbound.getWsOutbound();
- WsOutbound outFromBound = messageFromInbound.getWsOutbound();
- String content = messageMap.get("content"); // 消息内容
- String msgContentString = fromName + "说: " + content; // 构造消息体
- //发出去内容
- CharBuffer toMsg = CharBuffer.wrap(msgContentString.toCharArray());
- CharBuffer fromMsg = CharBuffer.wrap(msgContentString.toCharArray());
- // 消息发送到前端
- outFromBound.writeTextMessage(fromMsg);
- outbound.writeTextMessage(toMsg); //
- outFromBound.flush();
- outbound.flush();
- }
- }
- @Override
- protected void onClose(int status) {
- // 用户离线后 在线用户容器 去除该用户
- InitServlet.getSocketMap().remove(this);
- super.onClose(status);
- }
- @Override
- protected void onOpen(WsOutbound outbound) {
- super.onOpen(outbound);
- //
- if(name!=null){
- // 用户登录后,存入在线用户容器
- InitServlet.getSocketMap().put(name, this);
- }
- }
- @Override
- public int getReadTimeout() {
- return 0;
- }
- }
然后是 请求接收类
- package socket;
- import javax.servlet.http.HttpServletRequest;
- import org.apache.catalina.websocket.StreamInbound;
- import org.apache.catalina.websocket.WebSocketServlet;
- public class MyWebSocketServlet extends WebSocketServlet {
- /**
- *
- */
- private static final long serialVersionUID = -6488889268352650321L;
- public String getUser(HttpServletRequest request){
- String userName = (String) request.getSession().getAttribute("user");
- if(userName==null){
- return null;
- }
- return userName;
- }
- @Override
- protected StreamInbound createWebSocketInbound(String arg0,
- HttpServletRequest request) {
- System.out.println("用户" + request.getSession().getAttribute("user") + "登录");
- return new MyMessageInbound(this.getUser(request));
- }
- }
在线用户容器初始化类
- package socket;
- import java.util.HashMap;
- import javax.servlet.ServletConfig;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import org.apache.catalina.websocket.MessageInbound;
- /**
- * 应用启动时加载此类
- * 初始化servlet
- * 在应用启动后 在线用户的容器就准备好了!
- * @author masan
- *
- */
- public class InitServlet extends HttpServlet {
- private static final long serialVersionUID = 5772968684237694231L;
- // 在线用户容器 (存储在线用户 键值对 name:MessageInbound)
- private static HashMap<String,MessageInbound> socketMap;
- public void init(ServletConfig config) throws ServletException {
- InitServlet.socketMap = new HashMap<String,MessageInbound>();
- super.init(config);
- System.out.println("Server Started!");
- }
- /**
- * 获取在线用户容器的方法
- * @return
- */
- public static HashMap<String,MessageInbound> getSocketMap() {
- return InitServlet.socketMap;
- }
- }
然后是 消息体的解析类,我是按照逗号分隔的 前两处分别为发送者和接收者姓名,后一处为消息内容
- package util;
- import java.nio.CharBuffer;
- import java.util.HashMap;
- public class MessageUtil {
- public static HashMap<String,String> getMessage(CharBuffer msg) {
- HashMap<String,String> map = new HashMap<String,String>();
- String msgString = msg.toString();
- String m[] = msgString.split(",");
- map.put("fromName", m[0]);
- map.put("toName", m[1]);
- map.put("content", m[2]);
- return map;
- }
- }
那么附上工程目录结构:
附上三个JSP的内容
- <%@ page language="java" contentType="text/html; charset=UTF-8"
- pageEncoding="UTF-8"%>
- <!DOCTYPE html>
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <title>Index</title>
- <script type="text/javascript" src="js/jquery-1.7.2.min.js"></script>
- <%session.setAttribute("user", "小化");%>
- <script type="text/javascript">
- var ws = null;
- function startWebSocket() {
- if ('WebSocket' in window)
- ws = new WebSocket("ws://localhost:8080/WebSocketUser/websocket.do");
- else if ('MozWebSocket' in window)
- ws = new MozWebSocket("ws://localhost:8080/WebSocketUser/websocket.do");
- else
- alert("not support");
- ws.onmessage = function(evt) {
- // alert(evt.data);
- console.log(evt);
- // $("#xiaoxi").val(evt.data);
- setMessageInnerHTML(evt.data);
- };
- function setMessageInnerHTML(innerHTML){
- document.getElementById('message').innerHTML += innerHTML + '<br/>';
- }
- ws.onclose = function(evt) {
- //alert("close");
- document.getElementById('denglu').innerHTML="离线";
- };
- ws.onopen = function(evt) {
- //alert("open");
- document.getElementById('denglu').innerHTML="在线";
- document.getElementById('userName').innerHTML='小化';
- };
- }
- function sendMsg() {
- var fromName = "小化";
- var toName = document.getElementById('name').value; //发给谁
- var content = document.getElementById('writeMsg').value; //发送内容
- ws.send(fromName+","+toName+","+content);
- }
- </script>
- </head>
- <body onload="startWebSocket();">
- <p>聊天功能实现</p>
- 登录状态:
- <span id="denglu" style="color:red;">正在登录</span>
- <br>
- 登录人:
- <span id="userName"></span>
- <br>
- <br>
- <br>
- 发送给谁:<input type="text" id="name" value="小明"></input>
- <br>
- 发送内容:<input type="text" id="writeMsg"></input>
- <br>
- 聊天框:<div id="message" style="height: 250px;width: 280px;border: 1px solid; overflow: auto;"></div>
- <br>
- <input type="button" value="send" onclick="sendMsg()"></input>
- </body>
- </html>
- <%@ page language="java" contentType="text/html; charset=UTF-8"
- pageEncoding="UTF-8"%>
- <!DOCTYPE html>
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <title>Index</title>
- <script type="text/javascript" src="js/jquery-1.7.2.min.js"></script>
- <%session.setAttribute("user", "小明");%>
- <script type="text/javascript">
- var ws = null;
- function startWebSocket() {
- if ('WebSocket' in window)
- ws = new WebSocket("ws://localhost:8080/WebSocketUser/websocket.do");
- else if ('MozWebSocket' in window)
- ws = new MozWebSocket("ws://localhost:8080/WebSocketUser/websocket.do");
- else
- alert("not support");
- ws.onmessage = function(evt) {
- console.log(evt);
- //alert(evt.data);
- //$("#xiaoxi").val(evt.data);
- setMessageInnerHTML(evt.data);
- };
- function setMessageInnerHTML(innerHTML){
- document.getElementById('message').innerHTML += innerHTML + '<br/>';
- }
- ws.onclose = function(evt) {
- //alert("close");
- document.getElementById('denglu').innerHTML="离线";
- };
- ws.onopen = function(evt) {
- //alert("open");
- document.getElementById('denglu').innerHTML="在线";
- document.getElementById('userName').innerHTML="小明";
- };
- }
- function sendMsg() {
- var fromName = "小明";
- var toName = document.getElementById('name').value; //发给谁
- var content = document.getElementById('writeMsg').value; //发送内容
- ws.send(fromName+","+toName+","+content);
- }
- </script>
- </head>
- <body onload="startWebSocket();">
- <p>聊天功能实现</p>
- 登录状态:
- <span id="denglu" style="color:red;">正在登录</span>
- <br>
- 登录人:
- <span id="userName"></span>
- <br>
- <br>
- <br>
- 发送给谁:<input type="text" id="name" value="小化"></input>
- <br>
- 发送内容:<input type="text" id="writeMsg"></input>
- <br>
- 聊天框:<div id="message" style="height: 250px;width: 280px;border: 1px solid; overflow: auto;"></div>
- <br>
- <input type="button" value="send" onclick="sendMsg()"></input>
- </body>
- </html>
附上两个JSP,可自行增加,
另:附上web.xml ,相当重要,里面进行了servlet配置,并且设置了要求
- initServlet在应用启动时就要执行
- <?xml version="1.0" encoding="UTF-8"?>
- <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
- http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
- <servlet>
- <servlet-name>websocket</servlet-name>
- <servlet-class>socket.MyWebSocketServlet</servlet-class>
- </servlet>
- <servlet-mapping>
- <servlet-name>websocket</servlet-name>
- <url-pattern>*.do</url-pattern>
- </servlet-mapping>
- <servlet>
- <servlet-name>initServlet</servlet-name>
- <servlet-class>socket.InitServlet</servlet-class>
- <load-on-startup>1</load-on-startup>
- </servlet>
- <welcome-file-list>
- <welcome-file>index.jsp</welcome-file>
- </welcome-file-list>
- </web-app>
注意事项:1.js代码内的websocket 构造参数里的IP 要和最终浏览器访问地址一致 要么都是localhost要么都是IP 否则跨域
2.这个demo内是根据用户名识别用户的,明显不能做到唯一,所以只能是个demo,要用到项目里,请考虑下然后稍作修改即可
(我生产项目里用的mysql数据库,最后使用的用户ID来做区别)
3.消息发送参数只有一个,那么发送者和接收者的信息一并发送,后台有个解析类,如果需要传递其他参数,可自行修改解析类的解析方案
完整代码下载地址
想免积分下载的,但是CSDN最新修改,没有零分的了,最低1分
http://download.csdn.net/download/u012580998/9941412
增加百度云盘下载地址
https://pan.baidu.com/s/1o7W6yW6
tomcat websocket 实现网页在线即时聊天的更多相关文章
- 前端开发之旅-zopim在线即时聊天客服
一.与潜在客户实时聊天的神奇-zopim Zopim是一款高效的可嵌入网页中去的即使通讯与网站访客信息追踪的的Web软件.知道谁在访问您的网站吗?想和他们实时交流吗?想更有效的把握商机吗?使用Zopi ...
- Asp.Net Mvc基于Fleck开发的多人网页版即时聊天室
一.项目的核心说明 1.Fleck这个是实现websocket一个比较简单第三方组件,它不需要安装额外的容器.本身也就几个接口可供调用. 2.项目是基于.net framework 4.7.2 ,在v ...
- 使用 Django WebSocket Redis 搭建在线即时通讯工具
话不多说先上效果图演示 项目:http://112.74.164.107:9990/ 1.安装组建 redis: yum install redis/apt install redis 2.创建虚拟化 ...
- openfire搭建spackweb在线即时聊天
1.首先去openFire官网下载openFire以及spackweb,以下地址可以2样东西一次打包下载.http://download.csdn.net/detail/a315157973/8048 ...
- 分享一个基于长连接+长轮询+原生的JS及AJAX实现的多人在线即时交流聊天室
实现网页版的在线聊天室的方法有很多,在没有来到HTML5之前,常见的有:定时轮询.长连接+长轮询.基于第三方插件(如FLASH的Socket),而如果是HTML5,则比较简单,可以直接使用WebSoc ...
- Java和WebSocket开发网页聊天室
小编心语:咳咳咳,今天又是聊天室,到现在为止小编已经分享了不下两个了,这一次跟之前的又不大相同,这一次是网页聊天室,具体怎么着,还请各位看官往下看~ Java和WebSocket开发网页聊天室 一.项 ...
- java ssm 后台框架平台 项目源码 websocket即时聊天发图片文字 好友群组 SSM源码
官网 http://www.fhadmin.org/D 集成安全权限框架shiro Shiro 是一个用 Java 语言实现的框架,通过一个简单易用的 API 提供身份验证和授权,更安全,更可靠E ...
- Activiti6.0 工作流引擎 websocket即时聊天发图片文字 好友群组 SSM源码
即时通讯:支持好友,群组,发图片.文件,消息声音提醒,离线消息,保留聊天记录 (即时聊天功能支持手机端,详情下面有截图) 工作流模块---------------------------------- ...
- java Activiti6 工作流引擎 websocket 即时聊天 SSM源码 支持手机即时通讯聊天
即时通讯:支持好友,群组,发图片.文件,消息声音提醒,离线消息,保留聊天记录 (即时聊天功能支持手机端,详情下面有截图) 工作流模块---------------------------------- ...
随机推荐
- FAT文件系统学习和思考
FAT(File Allocation Table)文件系统 前两天面试,导师说我基础差,要赶紧补起来了.今天晚上看了FAT32文件系统,基本的信息都是百度百科中"FAT文件系统" ...
- Chrome浏览器扩展开发系列之二:Google Chrome浏览器扩展的调试
1) 查看扩展程序的详细信息和ID 通过Chrome 浏览器的“ 工具->更多工具->扩展程序”,打开chrome://extensions页面,选中右上角的“开发者模式”,可以 ...
- (cljs/run-at (JSVM. :all) "一次说白DataType、Record和Protocol")
前言 在项目中我们一般会为实际问题域定义领域数据模型,譬如开发VDOM时自然而言就会定义个VNode数据类型,用于打包存储.操作相关数据.clj/cljs不单内置了List.Vector.Set和M ...
- CSS3-loading动画(五)
CSS3-loading加载动画 在线示例demo:http://liyunpei.xyz/loading.html 之前发了四篇,二十二个效果,今天再分享六个效果,总计二十八个效果. 二十三.效果二 ...
- 关于Lumen / Laravel .env 文件中的环境变量是如何生效的
.env 文件包含默认环境变量,我们还可自定义其他任何有效的变量,并可通过 调用 env() 或 $_SERVER 或 $_ENV 来获取该变量.那么env()是如何加载到这些变量的呢?在Lume ...
- spring boot 读取配置文件信息
1.读取application.properties @Component @ConfigurationProperties(prefix="images.product.column&qu ...
- Sass学习笔记(补充)
阅读目录 1. Sass和SCSS的区别 2. @while循环 3. @at-root 4. @content 5. 凸显注释 6. CSS输出样式 7. 重置浏览器样式 8. Sass调试和@de ...
- Html table 合并单元格
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Akka(15): 持久化模式:AtLeastOnceDelivery-消息保证送达模式
消息保证送达是指消息发送方保证在任何情况下都会至少一次确定的消息送达.AtleastOnceDelivery是一个独立的trait,主要作用是对不确定已送达的消息进行补发,这是一种自动的操作,无需用户 ...
- DNS,TCP,IP,HTTP,socket,Servlet概念整理
DNS,TCP,IP,HTTP,socket,Servlet概念整理 常见的协议虽然很容易理解,但是看了之后过一段时间不看还是容易忘,笔记如下,比较零碎,勉强供各位复习.如有错误欢迎指正. D ...