您的位置:首页 > 运维架构 > Tomcat

HTML5和Tomcat7 WebSocketServlet easyui的聊天室简单实现

2013-12-26 11:25 876 查看
需要的jar包

commons-beanutils-1.7.0.jar

commons-collections-3.1.jar

commons-io-1.3.2.jar

commons-lang-2.3.jar

commons-logging-1.0.4.jar

commons-logging-api-1.1.jar

ezmorph-1.0.4.jar

jackson-core-asl-1.8.7.jar

jackson-mapper-asl-1.8.7.jar

json-lib-2.2.2-jdk15.jar

另外在eclipse中开发时需要将tomcat7引入进来(myeclipse中未成功)



不多说了,看下代码

index.jsp

<%@ page language="java" pageEncoding="UTF-8" import="com.ibcio.WebSocketMessageServlet"%>
<%
String path = request.getContextPath();
String WsBasePath = "ws://" + request.getServerName() + ":"
+ request.getServerPort() + path + "/";
String user = request.getParameter("username");
if(user == null){
user = (String)session.getAttribute("user");
if(user == null){
//为用户生成昵称
user = "游客" + WebSocketMessageServlet.ONLINE_USER_COUNT;
WebSocketMessageServlet.ONLINE_USER_COUNT ++;
session.setAttribute("user", user);
}
}else{
session.setAttribute("user", user);
}
pageContext.setAttribute("user", user);
%>
<!DOCTYPE html >
<html>
<head>
<title>WebSocket 聊天室</title>
<!-- 引入CSS文件 -->
<link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath }/css/websocket.css" />
<link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath }/easyui1.3.5/themes/default/easyui.css"/>
<link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath }/easyui1.3.5/themes/icon.css"/>
<!-- 映入easyui的JS开发包,及自己实现的webscoket. -->
<script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery-1.10.2.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath }/easyui1.3.5/jquery.easyui.min.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath }/js/websocket.js"></script>
<script type="text/javascript">
var user = "${user}";
var wsPath = "<%=WsBasePath%>"+"message";

</script>
</head>

<body >
<h1>WebSocket聊天室</h1>
<p>通过HTML5标准提供的API与EasyUI富客户端框架相结合起来,实现聊天室,有以下特点:</p>
<ul class="feature-list" style="padding-left: 10px;">
<li>实时获取数据,由服务器推送,实现即时通讯</li>
<li>利用WebSocket完成数据通讯,区别于轮询,长连接等技术,节省服务器资源</li>
<li>结合EasyUI进行页面展示</li>
<li>用户上线下线通知</li>
</ul>
<div>
<a id="btn" href="javascript:void(0)" class="easyui-linkbutton" onclick="$('#initWin').window('open')">打开聊天窗口</a>
</div>
<div id="initWin" class="easyui-window" data-options="collapsible:false,minimizable:false,maximizable:false,iconCls:'user-win'" >
<div id="mainLayout" class="easyui-layout" style="width:642px;height:452px;">
<div data-options="region:'east',title:'在线用户',iconCls:'user-online'" style="width:150px;">
<!-- <div id="userOnlineTree" class="easyui-tree"></div> -->
<div id="userOnlineTree" class="easyui-accordion" data-options="height:400" style="border: none;"></div>
</div>
<div id="mainLayoutCenter" data-options="region:'center',iconCls:'user-win'">
<div class="easyui-layout" style="width:491px;height:423px;">
<div data-options="region:'south'" style="height: 150px;border: none;border-top: 1px solid #95B8E7;">
<div class="easyui-layout" style="width:489px;height:148px;">
<div data-options="region:'south'" style="height: 30px;text-align: right;border: none;border-top: 1px solid #95B8E7;">
<a id="btnSend" href="javascript:void(0);" class="easyui-linkbutton" style="margin-top: 5px;">发送</a>
</div>
<div data-options="region:'center'" style="height:100px;overflow-x: hidden;border: none;">
<textarea style="width: 485px; height: 112px; resize: none;border: none;" id="textArea" onkeypress="keyPress(event);"></textarea>
</div>
</div>
</div>
<div id="messageShowCenter" data-options="region:'center'" style="overflow: auto;border: none;overflow-x: hidden;backgroundColor : '#fff'" >

</div>
</div>
</div>
</div>
</div>
</body>
</html>


websocket.js
var websocket;
//聊天窗口
var win;
//聊天窗口标题
var title = '欢迎您: ';
//输入框
var input;
//在线用户
var onlineUser;

$(function() {
title += user;
win = $("#initWin");
input = $("#textArea");
//在线用户
onlineUser = $("#userOnlineTree");
onlineUser.accordion({
animate:false
});
//我的好友分组
onlineUser.accordion('add', {
title: "我的好友",
content: "<div id='onlineUserContent'></div>",
});
//	onlineUser.tree({
//		onBeforeSelect:function(node){
//			var select = onlineUser.tree("getSelected");
//			if(select != null && select.id == node.id){
//				//取消选中
//				$("#"+node.domId).removeClass("tree-node-selected");
//				//cancel this select action
//				return false;
//			}
//		}
//	});
initWebSocket();
//发送信息
$("#btnSend").click(function(e){
e.preventDefault();
send();
});
//消息展示
$("#messageShowCenter").append('<div class="l-im-message-warn">​交谈中请勿轻信汇款、中奖信息、陌生电话。 请遵守相关法律法规。</div>');
});

//初始话WebSocket
function initWebSocket() {
if (window.WebSocket) {
websocket = new WebSocket(encodeURI(wsPath));
websocket.onopen = function() {
//连接成功
win.panel("setTitle",title + '(已连接)');
};
websocket.onerror = function() {
//连接失败
win.panel("setTitle",title + '(连接发生错误)');
};
websocket.onclose = function() {
//连接断开
win.panel("setTitle",title + '(已经断开连接)');
};
//消息接收
websocket.onmessage = function(message) {
var message = JSON.parse(message.data);
//接收用户发送的消息
if (message.type == 'message') {
receive(message);
} else if (message.type == 'get_online_user') {
//获取在线用户列表
$.each(message.list,function(i){
var user = message.list[i];
var node = {
data: [{
id: user,
text: user,
iconCls : 'user',
leaf : true
}]
};
//onlineUser.tree('append',node);
var context = '<div id="'+user+'" class="tree-node"><span class="tree-indent"></span><span class="user"></span><span class="tree-title" >'+user+'</span></div>';
$("#onlineUserContent").append(context);
$("#"+user).mouseover(function(){
$(this).addClass("tree-node-hover");
}).mouseout(function(){
$(this).removeClass("tree-node-hover");
}).click(function(){
if($(this).hasClass("tree-node-selected")){
$("#userOnlineTree .tree-node-selected").removeClass("tree-node-selected");
}else{
$("#userOnlineTree .tree-node-selected").removeClass("tree-node-selected");
$(this).addClass("tree-node-selected");
}
});
});
} else if (message.type == 'user_join') {
//用户上线
var user = message.user;
var node = {
data: [{
id: user,
text: user,
iconCls : 'user',
leaf : true
}]
};
//onlineUser.tree('append',node);
var context = '<div id="'+user+'" class="tree-node"><span class="tree-indent"></span><span class="user"></span><span class="tree-title" >'+user+'</span></div>';
$("#onlineUserContent").append(context);
$("#"+user).mouseover(function(){
$(this).addClass("tree-node-hover");
}).mouseout(function(){
$(this).removeClass("tree-node-hover");
}).click(function(){
if($(this).hasClass("tree-node-selected")){
$("#userOnlineTree .tree-node-selected").removeClass("tree-node-selected");
}else{
$("#userOnlineTree .tree-node-selected").removeClass("tree-node-selected");
$(this).addClass("tree-node-selected");
}
});
} else if (message.type == 'user_leave') {
//用户下线
var user = message.user;
//var node = onlineUser.tree('find', user);
//onlineUser.tree('remove',node.target);
$("#"+user).remove();
}
$("#userOnlineTree div span").removeClass("tree-file").removeClass("tree-icon");
};
}
}

//发送消息
function send() {
var message = {};
var to = onlineUser.tree('getSelected');
if(null == to){
to = "";
}else{
to = to.id;
}
if (websocket != null) {
if (input.val()) {
$.extend(message, {
from : user,
to : to,
content : input.val(),
timestamp : new Date().getTime(),
type : 'message'
});
if(websocket.readyState == 1){
message = JSON.stringify(message);
websocket.send(message);
//message保存到本地
var key = user+"to"+to;
if(localStorage.getItem(key)){
localStorage.setItem(key, localStorage.getItem(key)+"\n"+message);
}else{
localStorage.setItem(key,message);
}
input.val('');
}else{
$.messager.alert('提示', '您已经掉线,无法发送消息!','warning');
}
//output.receive(message);
}
} else {
$.messager.alert('提示', '您已经掉线,无法发送消息!','warning');
}
}
//接受消息
function receive(message){
if(message.from == user){
message.source = 'self';
}else{
message.source = 'remote';
}
message['timestamp'] = new Date(message['timestamp']).format("H:m:s");
var msg = '<div class="l-im-message">'+
'<div class="l-im-message-header l-im-message-header-'+message.source+'">'+message.from+'  '+message.timestamp+'</div>'+
'<div class="l-im-message-body">'+message.content+'</div>'+
'</div>';

$("#messageShowCenter").append(msg);
var msc = document.getElementById("messageShowCenter");
if (msc) {
msc.scrollTop = msc.scrollHeight;
}
}
//enter事件
function keyPress(e){
var keynum="";

if (window.event) // IE
{
keynum = e.keyCode;
} else if (e.which) // Netscape/Firefox/Opera
{
keynum = e.which;
}
if (e.ctrlKey !== true && keynum == 13) {
//only enter
e.preventDefault();
e.stopPropagation();
send();
}else if(e.ctrlKey === true && (keynum == 13 || keynum == 10)){
//ctrl+enter
input.val(input.val()+"​\n");
}
}

// 时间格式化
Date.prototype.format = function(format) {
var o = {
"M+" : this.getMonth() + 1, //month
"d+" : this.getDate(), //day
"H+" : this.getHours(), //hour
"m+" : this.getMinutes(), //minute
"s+" : this.getSeconds(), //second
"q+" : Math.floor((this.getMonth() + 3) / 3), //quarter
"S" : this.getMilliseconds()
//millisecond
};

if (/(y+)/.test(format)) {
format = format.replace(RegExp.$1, (this.getFullYear() + "")
.substr(4 - RegExp.$1.length));
}

for ( var k in o) {
if (new RegExp("(" + k + ")").test(format)) {
format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k]
: ("00" + o[k]).substr(("" + o[k]).length));
}
}
return format;
};


websocket.css
@CHARSET "UTF-8";
.l-im-message-warn {
font-family: "微软雅黑";
cursor: default;
width: 100%;
padding: 5px 0px 5px 25px;
-webkit-user-select : none;
background: url("../images/information.png") no-repeat 5;
}

.l-im-message {
font-family: "微软雅黑";
cursor: default;
width: 100%;
}

.l-im-message-over {
background-color: rgba(233, 233, 233, 0.5);
}

.l-im-message-selected {
background-color: rgba(250, 218, 90, 0.5);
}

.l-im-message-header {
font-size: 12px;
padding: 5px 0px 5px 10px;
}

.l-im-message-header-self {
color: green;
}
.l-im-message-header-remote {
color: blue;
}

.l-im-message-body {
font-size: 12px;
padding: 2px 0px 2px 20px;
}

.user-win {
background-image: url( ../images/user_win.png )
!important;
}

.user-online {
background-image: url( ../images/group.png )
!important;
}

.user {
background-image: url( ../images/user.gif );
width:16px;
height:16px;
display:inline-block;
}


JsonUtil.java
package com.ibcio;

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

import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.ObjectMapper;

/**
*
* @author Administrator
*
*/
@SuppressWarnings({"unchecked","rawtypes"})
public class JsonUtil {

private ObjectMapper mapper;
private static JsonUtil jsonUtil;

public static JsonUtil getInstance() {
if (null == jsonUtil) {
jsonUtil = new JsonUtil();
}
return jsonUtil;

}

private JsonUtil() {
this.mapper = new ObjectMapper();
}

public ObjectMapper getMapper() {
return mapper;
}

public static String pathValue(String jsonNode, String path) {
String result = "";
ObjectMapper obm = JsonUtil.getInstance().getMapper();
JsonNode rootNode;
try {
rootNode = obm.readTree(jsonNode);
result = rootNode.path(path).getValueAsText();
} catch (JsonProcessingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

return result;
}

public static String pathValue(JsonNode jsonNode, String path) {
String result = null;
result = jsonNode.path(path).getValueAsText();
return result;
}

public static JsonNode readNode(String jsonNode) {
ObjectMapper obm = JsonUtil.getInstance().getMapper();
JsonNode rootNode = null;
try {
rootNode = obm.readTree(jsonNode);
} catch (JsonProcessingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return rootNode;
}

/**
* json转map
* @param jsonStr
* @return
*/
public static Map readMap(String jsonStr) {
ObjectMapper om = JsonUtil.getInstance().getMapper();
Map map = null;
try {
map = om.readValue(jsonStr, Map.class);
} catch (Exception e) {
e.printStackTrace();
}
return map;
}

/**
* json 转 Object
* @param jsonStr
* @param clazz
* @return
*/
public static Object readObj(String jsonStr, Class clazz) {
ObjectMapper om = JsonUtil.getInstance().getMapper();
try {
return om.readValue(jsonStr, clazz);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

/**
* object转json
* @param obj
* @return
*/
public static String writeAsString(Object obj) {
ObjectMapper obm = JsonUtil.getInstance().getMapper();
String reuslt = "";
try {
reuslt = obm.writeValueAsString(obj);
} catch (JsonProcessingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return reuslt;
}

public static String nodeText(JsonNode node, String nodeName) {
String text = "";
if (node != null) {
JsonNode tmp = node.path(nodeName);
if (tmp != null) {
text = tmp.getValueAsText();
}
}
if (text == null) {
text = "";
}
return text;
}

}


MsgModel.java
package com.ibcio;

/**
* 消息模型
*
* @author Administrator
*
*/
public class MsgModel {

// msg
private String from;
private String to;
private String content;
private String timestamp;
private String type;
// user join leave
private String user;
private String list;

public String getFrom() {
return from;
}

public void setFrom(String from) {
this.from = from;
}

public String getTo() {
return to;
}

public void setTo(String to) {
this.to = to;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}

public String getTimestamp() {
return timestamp;
}

public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}

public String getType() {
return type;
}

public void setType(String type) {
this.type = type;
}

public String getUser() {
return user;
}

public void setUser(String user) {
this.user = user;
}

public String getList() {
return list;
}

public void setList(String list) {
this.list = list;
}

}


WebSocketMessageInbound.java
package com.ibcio;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;

import net.sf.json.JSONObject;

import org.apache.catalina.websocket.MessageInbound;
import org.apache.catalina.websocket.WsOutbound;

public class WebSocketMessageInbound extends MessageInbound {

//当前连接的用户名称
private final String user;

public WebSocketMessageInbound(String user) {
this.user = user;
}

public String getUser() {
return this.user;
}

//建立连接的触发的事件
@Override
protected void onOpen(WsOutbound outbound) {
// 触发连接事件,在连接池中添加连接
JSONObject result = new JSONObject();
result.element("type", "user_join");
result.element("user", this.user);
//向所有在线用户推送当前用户上线的消息
WebSocketMessageInboundPool.sendMessage(result.toString());

result = new JSONObject();
result.element("type", "get_online_user");
result.element("list", WebSocketMessageInboundPool.getOnlineUser());
//向连接池添加当前的连接对象
WebSocketMessageInboundPool.addMessageInbound(this);
//向当前连接发送当前在线用户的列表
WebSocketMessageInboundPool.sendMessageToUser(this.user, result.toString());
}

@Override
protected void onClose(int status) {
// 触发关闭事件,在连接池中移除连接
WebSocketMessageInboundPool.removeMessageInbound(this);
JSONObject result = new JSONObject();
result.element("type", "user_leave");
result.element("user", this.user);
//向在线用户发送当前用户退出的消息
WebSocketMessageInboundPool.sendMessage(result.toString());
}

@Override
protected void onBinaryMessage(ByteBuffer message) throws IOException {
throw new UnsupportedOperationException("Binary message not supported.");
}

//客户端发送消息到服务器时触发事件
@Override
protected void onTextMessage(CharBuffer message) throws IOException {
//向所有在线用户发送消息
WebSocketMessageInboundPool.sendMessage(message.toString());
}
}


WebSocketMessageInboundPool.java
package com.ibcio;

import java.io.IOException;
import java.nio.CharBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class WebSocketMessageInboundPool {

//保存连接的MAP容器
private static final Map<String,WebSocketMessageInbound > connections = new HashMap<String,WebSocketMessageInbound>();

//向连接池中添加连接
public static void addMessageInbound(WebSocketMessageInbound inbound){
//添加连接
System.out.println("user : " + inbound.getUser() + " join..");
connections.put(inbound.getUser(), inbound);
}

//取得connection
public static Map<String,WebSocketMessageInbound > getOnlineMap(){
return connections;
}
//获取所有的在线用户
public static Set<String> getOnlineUser(){
return connections.keySet();
}

public static void removeMessageInbound(WebSocketMessageInbound inbound){
//移除连接
System.out.println("user : " + inbound.getUser() + " exit..");
connections.remove(inbound.getUser());
}

public static void sendMessageToUser(String user,String message){
try {
//向特定的用户发送数据
System.out.println("send message to user : " + user + " ,message content : " + message);
WebSocketMessageInbound inbound = connections.get(user);
if(inbound != null){
inbound.getWsOutbound().writeTextMessage(CharBuffer.wrap(message));
}
} catch (IOException e) {
e.printStackTrace();
}
}

//向所有的用户发送消息
public static void sendMessage(String message){
try {
MsgModel mm = null;
try {
mm = (MsgModel)JsonUtil.readObj(message, MsgModel.class);
} catch (Exception e) {
e.printStackTrace();
}
if(null == mm || mm.getTo() == null || "".equals(mm.getTo())){
Set<String> keySet = connections.keySet();
for (String key : keySet) {
WebSocketMessageInbound inbound = connections.get(key);
if(inbound != null){
System.out.println("send message to user : " + key + " ,message content : " + message);
inbound.getWsOutbound().writeTextMessage(CharBuffer.wrap(message));
}
}
}else{
WebSocketMessageInbound inbound = connections.get(mm.getFrom());
inbound.getWsOutbound().writeTextMessage(CharBuffer.wrap(message));
inbound = connections.get(mm.getTo());
inbound.getWsOutbound().writeTextMessage(CharBuffer.wrap(message));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}


WebSocketMessageServlet.java

package com.ibcio;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;

import org.apache.catalina.websocket.StreamInbound;

@WebServlet(urlPatterns = { "/message"})
//如果要接收浏览器的ws://协议的请求就必须实现WebSocketServlet这个类
public class WebSocketMessageServlet extends org.apache.catalina.websocket.WebSocketServlet {

private static final long serialVersionUID = 1L;

public static int ONLINE_USER_COUNT	= 1;

public String getUser(HttpServletRequest request){
return (String) request.getSession().getAttribute("user");
}

//跟平常Servlet不同的是,需要实现createWebSocketInbound,在这里初始化自定义的WebSocket连接对象
@Override
protected StreamInbound createWebSocketInbound(String subProtocol,HttpServletRequest request) {

return new WebSocketMessageInbound(this.getUser(request));
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: