由于在网上看到有人用tomcat7+java7+extjs 实现了一个webscoket聊天室功能,我就想用tomcat8重写一遍。
于是就有了一下代码:
package com.ibcio;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import net.sf.json.JSONObject;
/**接收所有来自客户端的连接请求,并定义各种事件的回调
* @author leter
* @date 2016年3月8日
* @remark
*/
@ServerEndpoint(value = "/chat/{user}",configurator=MyConfigurator.class)
public class WebSocketImpl{
//建立连接的触发的事件
@OnOpen
public void start(Session session,@PathParam(value = "user") String user) {
session.getUserProperties().put("user", user);
// 触发连接事件,在连接池中添加连接
JSONObject result = new JSONObject();
result.element("type", "user_join");
result.element("user", user);
//向所有在线用户推送当前用户上线的消息
WebSocketSessionPool.sendMessage(result.toString(),session);
result = new JSONObject();
result.element("type", "get_online_user");
result.element("list", WebSocketSessionPool.getOnlineUser());
//向连接池添加当前的连接对象
WebSocketSessionPool.addMessageInbound(session,user);
//向当前连接发送当前在线用户的列表
WebSocketSessionPool.sendMessageToUser(user, result.toString());
}
@OnClose
public void end(Session session) {
// 触发关闭事件,在连接池中移除连接
String user = session.getUserProperties().get("user").toString();
WebSocketSessionPool.removeMessageInbound(user);
JSONObject result = new JSONObject();
result.element("type", "user_leave");
result.element("user", user);
//向在线用户发送当前用户退出的消息
WebSocketSessionPool.sendMessage(result.toString(),session);
}
//客户端发送消息到服务器时触发事件
@OnMessage
public void prcess(String message, Session session){
//向所有在线用户发送消息
WebSocketSessionPool.sendMessage(message.toString(),session);
}
}
package com.ibcio;
import javax.websocket.server.ServerEndpointConfig.Configurator;
/**因为每次来一个链接都会生成一个新的WebSocketImpl,这样比较耗费资源。
* 统一配置只使用一个WebSocketImpl
* @author leter
* @date 2016年3月8日
* @remark
*/
public class MyConfigurator extends Configurator {
private WebSocketImpl webSocket = new WebSocketImpl();
@Override
public <T> T getEndpointInstance(Class<T> clazz)
throws InstantiationException {
return (T) webSocket;
}
}
package com.ibcio;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.websocket.Session;
/**存储所有在线用户的信息,执行上线下线发送消息时需要执行的方法。
* @author leter
* @date 2016年3月8日
* @remark
*/
public class WebSocketSessionPool {
// 保存连接的MAP容器
private static final Map<String, Session> connections = Collections.synchronizedMap(new HashMap<String, Session>());
// 向连接池中添加连接
public static void addMessageInbound(Session session, String user) {
// 添加连接
System.out.println("user : " + user + " join..");
connections.put(user, session);
}
// 获取所有的在线用户
public static Set<String> getOnlineUser() {
return connections.keySet();
}
public static void removeMessageInbound(String user) {
// 移除连接
System.out.println("user : " + user + " exit..");
connections.remove(user);
}
public static void sendMessageToUser(String user, String message) {
try {
// 向特定的用户发送数据
System.out.println("send message to user : " + user+ " ,message content : " + message);
Session session = connections.get(user);
if (session != null) {
session.getBasicRemote().sendText(message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 向所有的用户发送消息
public static void sendMessage(String message, Session session) {
try {
for (String key : connections.keySet()) {
connections.get(key).getBasicRemote().sendText(message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
前台index.jsp代码
<%@ page language="java" pageEncoding="UTF-8" import="java.util.UUID"%>
<%
String user = "user"+UUID.randomUUID();
pageContext.setAttribute("user", user);
%>
<html>
<head>
<title>WebSocket 聊天室</title>
<!-- 引入CSS文件 -->
<link rel="stylesheet" type="text/css" href="ext4/resources/css/ext-all.css">
<link rel="stylesheet" type="text/css" href="ext4/shared/example.css" />
<link rel="stylesheet" type="text/css" href="css/websocket.css" />
<!-- 映入Ext的JS开发包,及自己实现的webscoket. -->
<script type="text/javascript" src="ext4/ext-all-debug.js"></script>
<script type="text/javascript" src="websocket.js"></script>
<script type="text/javascript">
var user = "${user}";
</script>
</head>
<body>
<h1>WebSocket聊天室</h1>
<p>通过HTML5标准提供的API与Ext富客户端框架相结合起来,实现聊天室,有以下特点:</p>
<ul class="feature-list" style="padding-left: 10px;">
<li>实时获取数据,由服务器推送,实现即时通讯</li>
<li>利用WebSocket完成数据通讯,区别于轮询,长连接等技术,节省服务器资源</li>
<li>结合Ext进行页面展示</li>
<li>用户上线下线通知</li>
</ul>
<div id="websocket_button"></div>
</body>
</html>
websokect.js
//用于展示用户的聊天信息 Ext.define('MessageContainer', { extend : 'Ext.view.View', trackOver : true, multiSelect : false, itemCls : 'l-im-message', itemSelector : 'div.l-im-message', overItemCls : 'l-im-message-over', selectedItemCls : 'l-im-message-selected', style : { overflow : 'auto', backgroundColor : '#fff' }, tpl : [ '<div class="l-im-message-warn">交谈中请勿轻信汇款、中奖信息、陌生电话。 请遵守相关法律法规。</div>', '<tpl for=".">', '<div class="l-im-message">', '<div class="l-im-message-header l-im-message-header-{source}">{from} {timestamp}</div>', '<div class="l-im-message-body">{content}</div>', '</div>', '</tpl>'], messages : [], initComponent : function() { var me = this; me.messageModel = Ext.define('Leetop.im.MessageModel', { extend : 'Ext.data.Model', fields : ['from', 'timestamp', 'content', 'source'] }); me.store = Ext.create('Ext.data.Store', { model : 'Leetop.im.MessageModel', data : me.messages }); me.callParent(); }, //将服务器推送的信息展示到页面中 receive : function(message) { var me = this; message['timestamp'] = Ext.Date.format(new Date(message['timestamp']), 'H:i:s'); if(message.from == user){ message.source = 'self'; }else{ message.source = 'remote'; } me.store.add(message); if (me.el.dom) { me.el.dom.scrollTop = me.el.dom.scrollHeight; } } }); Ext.onReady(function() { //创建用户输入框 var input = Ext.create('Ext.form.field.HtmlEditor', { region : 'south', height : 120, enableFont : false, enableSourceEdit : false, enableAlignments : false, listeners : { initialize : function() { Ext.EventManager.on(me.input.getDoc(), { keyup : function(e) { if (e.ctrlKey === true && e.keyCode == 13) { e.preventDefault(); e.stopPropagation(); send(); } } }); } } }); //创建消息展示容器 var output = Ext.create('MessageContainer', { region : 'center' }); var dialog = Ext.create('Ext.panel.Panel', { region : 'center', layout : 'border', items : [input, output], buttons : [{ text : '发送', handler : send }] }); var websocket; //初始话WebSocket function initWebSocket() { if (window.WebSocket) { websocket = new WebSocket(encodeURI('ws://localhost/webscoket/chat/'+user)); websocket.onopen = function() { //连接成功 win.setTitle(title + ' (已连接)'); } websocket.onerror = function() { //连接失败 win.setTitle(title + ' (连接发生错误)'); } websocket.onclose = function() { //连接断开 win.setTitle(title + ' (已经断开连接)'); } //消息接收 websocket.onmessage = function(message) { var message = JSON.parse(message.data); //接收用户发送的消息 if (message.type == 'message') { output.receive(message); } else if (message.type == 'get_online_user') { //获取在线用户列表 var root = onlineUser.getRootNode(); Ext.each(message.list,function(user){ var node = root.createNode({ id : user, text : user, iconCls : 'user', leaf : true }); root.appendChild(node); }); } else if (message.type == 'user_join') { //用户上线 var root = onlineUser.getRootNode(); var user = message.user; var node = root.createNode({ id : user, text : user, iconCls : 'user', leaf : true }); root.appendChild(node); } else if (message.type == 'user_leave') { //用户下线 var root = onlineUser.getRootNode(); var user = message.user; var node = root.findChild('id',user); root.removeChild(node); } } } }; //在线用户树 var onlineUser = Ext.create('Ext.tree.Panel', { title : '在线用户', rootVisible : false, region : 'east', width : 150, lines : false, useArrows : true, autoScroll : true, split : true, iconCls : 'user-online', store : Ext.create('Ext.data.TreeStore', { root : { text : '在线用户', expanded : true, children : [] } }) }); var title = '欢迎您:' + user; //展示窗口 var win = Ext.create('Ext.window.Window', { title : title + ' (未连接)', layout : 'border', iconCls : 'user-win', minWidth : 650, minHeight : 460, width : 650, animateTarget : 'websocket_button', height : 460, items : [dialog,onlineUser], border : false, listeners : { render : function() { initWebSocket(); } } }); win.show(); //发送消息 function send() { var message = {}; if (websocket != null) { if (input.getValue()) { Ext.apply(message, { from : user, content : input.getValue(), timestamp : new Date().getTime(), type : 'message' }); websocket.send(JSON.stringify(message)); //output.receive(message); input.setValue(''); } } else { Ext.Msg.alert('提示', '您已经掉线,无法发送消息!'); } } });
我使用的开发环境是eclipse kepler + tomcat8.0.32+jdk7.x
以下是tomat7实现和tomcat8实现的源码。