网站即时通讯(网页即时聊天)

基于html5的Websocket网页即时通讯技术,前端开发采用ExtJS前端框架

JavaEE框架:Mybatis、SpringMVC

先去官网下载ExtJS框架的资料文件:

https://www.sencha.com/products/extjs/evaluate/

可以参考中文翻译过来的官网查看API:

http://extjs-doc-cn.github.io/ext4api/

下载集成的jar:


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 ) !important;}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556

websocket.js:

var websocket;var isCreatw = false;var title=\"\";var win;var input;var isQj = true;var toUser=\"\";function toUserMsg(toU){ if((!isQj && toUser == toU) || toU == user){ win.setTitle(title + \" (已连接) 【现在全局对话】\"); isQj = true; toUser = \"\"; }else{ win.setTitle(title + \" (已连接) 【现在单独与\"+toU+\"对话】\"); isQj = false; toUser = toU; }} function creatw() { if(isCreatw){ alert(\"已经启动\"); return; }else{ isCreatw = true; } //创建用户输入框 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 }] }); //初始话WebSocket function initWebSocket() { if (window.WebSocket) { websocket = new WebSocket(encodeURI('ws://'+wimadress)); websocket.onopen = function() { //连接成功 win.setTitle(title + ' (已连接) 【现在全局对话】'); websocket.send('admin'+user); } 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 : [] } }) }); title = '欢迎您:' + user; //展示窗口 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(); win.on(\"close\",function(){ websocket.send('LeaveAdmin'); isCreatw = false; }); //发送消息 function send() { var content = input.getValue(); if(toUser != \"\"){content = \"admin886\"+toUser+\"admin888\" + content;} var message = {}; if (websocket != null) { if (input.getValue()) { Ext.apply(message, { from : user, content : content, timestamp : new Date().getTime(), type : 'message' }); websocket.send(JSON.stringify(message)); //output.receive(message); input.setValue(''); } } else { Ext.Msg.alert('提示', '您已经掉线,无法发送消息!'); } }};//用于展示用户的聊天信息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; } }});123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254

业务代码编写:

ChatServer.java

package com.appms.websocket;import java.io.IOException;import java.net.InetSocketAddress;import java.net.UnknownHostException;import java.util.Date;import net.sf.json.JSONObject;import org.java_websocket.WebSocket;import org.java_websocket.WebSocketImpl;import org.java_websocket.framing.Framedata;import org.java_websocket.handshake.ClientHandshake;import org.java_websocket.server.WebSocketServer;/** * 即时通讯 */public class ChatServer extends WebSocketServer{ public ChatServer(int port) throws UnknownHostException { super(new InetSocketAddress(port)); } public ChatServer(InetSocketAddress address) { super(address); } /** * 触发连接事件 */ @Override public void onOpen( WebSocket conn, ClientHandshake handshake ) { } /** * 触发关闭事件 */ @Override public void onClose( WebSocket conn, int code, String reason, boolean remote ) { userLeave(conn); } /** * 客户端发送消息到服务器时触发事件 */ @Override public void onMessage(WebSocket conn, String message){ message = message.toString(); if(null != message && message.startsWith(\"admin\")){ this.userjoin(message.replaceFirst(\"admin\", \"\"),conn); }if(null != message && message.startsWith(\"LeaveAdmin\")){ this.userLeave(conn); }if(null != message && message.contains(\"admin886\")){ String toUser = message.substring(message.indexOf(\"admin886\")+8, message.indexOf(\"admin888\")); message = message.substring(0, message.indexOf(\"admin886\")) +\"[私信] \"+ message.substring(message.indexOf(\"admin888\")+8, message.length()); ChatServerPool.sendMessageToUser(ChatServerPool.getWebSocketByUser(toUser),message);//向所某用户发送消息 ChatServerPool.sendMessageToUser(conn, message);//同时向本人发送消息 }else{ ChatServerPool.sendMessage(message.toString());//向所有在线用户发送消息 } } public void onFragment( WebSocket conn, Framedata fragment ) { } /** * 触发异常事件 */ @Override public void onError( WebSocket conn, Exception ex ) { ex.printStackTrace(); if( conn != null ) { //some errors like port binding failed may not be assignable to a specific websocket } } /** * 用户加入处理 * @param user */ public void userjoin(String user, WebSocket conn){ JSONObject result = new JSONObject(); result.element(\"type\", \"user_join\"); result.element(\"user\", \"<a onclick=\\"toUserMsg('\"+user+\"');\\">\"+user+\"</a>\"); ChatServerPool.sendMessage(result.toString()); //把当前用户加入到所有在线用户列表中 String joinMsg = \"{\\"from\\":\\"[系统]\\",\\"content\\":\\"\"+user+\"上线了\\",\\"timestamp\\":\"+new Date().getTime()+\",\\"type\\":\\"message\\"}\"; ChatServerPool.sendMessage(joinMsg); //向所有在线用户推送当前用户上线的消息 result = new JSONObject(); result.element(\"type\", \"get_online_user\"); ChatServerPool.addUser(user,conn); //向连接池添加当前的连接对象 result.element(\"list\", ChatServerPool.getOnlineUser()); ChatServerPool.sendMessageToUser(conn, result.toString()); //向当前连接发送当前在线用户的列表 } /** * 用户下线处理 * @param user */ public void userLeave(WebSocket conn){ String user = ChatServerPool.getUserByKey(conn); boolean b = ChatServerPool.removeUser(conn); //在连接池中移除连接 if(b){ JSONObject result = new JSONObject(); result.element(\"type\", \"user_leave\"); result.element(\"user\", \"<a onclick=\\"toUserMsg('\"+user+\"');\\">\"+user+\"</a>\"); ChatServerPool.sendMessage(result.toString()); //把当前用户从所有在线用户列表中删除 String joinMsg = \"{\\"from\\":\\"[系统]\\",\\"content\\":\\"\"+user+\"下线了\\",\\"timestamp\\":\"+new Date().getTime()+\",\\"type\\":\\"message\\"}\"; ChatServerPool.sendMessage(joinMsg); //向在线用户发送当前用户退出的消息 } } public static void main( String[] args ) throws InterruptedException , IOException { WebSocketImpl.DEBUG = false; int port = 8887; //端口 ChatServer s = new ChatServer(port); s.start(); System.out.println( \"服务器的端口\" + s.getPort() ); }}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124

ChatServerPool.java:

package com.appms.websocket;import java.util.ArrayList;import java.util.Collection;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.Set;import org.java_websocket.WebSocket;/** * 即时通讯 */public class ChatServerPool { private static final Map<WebSocket,String> userconnections = new HashMap<WebSocket,String>(); /** * 获取用户名 * @param session */ public static String getUserByKey(WebSocket conn){ return userconnections.get(conn); } /** * 获取WebSocket * @param user */ public static WebSocket getWebSocketByUser(String user){ Set<WebSocket> keySet = userconnections.keySet(); synchronized (keySet) { for (WebSocket conn : keySet) { String cuser = userconnections.get(conn); if(cuser.equals(user)){ return conn; } } } return null; } /** * 向连接池中添加连接 * @param inbound */ public static void addUser(String user, WebSocket conn){ userconnections.put(conn,user); //添加连接 } /** * 获取所有的在线用户 * @return */ public static Collection<String> getOnlineUser(){ List<String> setUsers = new ArrayList<String>(); Collection<String> setUser = userconnections.values(); for(String u:setUser){ setUsers.add(\"<a onclick=\\"toUserMsg('\"+u+\"');\\">\"+u+\"</a>\"); } return setUsers; } /** * 移除连接池中的连接 * @param inbound */ public static boolean removeUser(WebSocket conn){ if(userconnections.containsKey(conn)){ userconnections.remove(conn); //移除连接 return true; }else{ return false; } } /** * 向特定的用户发送数据 * @param user * @param message */ public static void sendMessageToUser(WebSocket conn,String message){ if(null != conn && null != userconnections.get(conn)){ conn.send(message); } } /** * 向所有的用户发送消息 * @param message */ public static void sendMessage(String message){ Set<WebSocket> keySet = userconnections.keySet(); synchronized (keySet) { for (WebSocket conn : keySet) { String user = userconnections.get(conn); if(user != null){ conn.send(message); } } } }}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105

写个过滤器,在项目执行时启动:

package com.appms.filter;import java.io.IOException;import java.net.UnknownHostException;import java.util.Calendar;import java.util.Date;import java.util.Timer;import java.util.TimerTask;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import org.java_websocket.WebSocketImpl;import com.appms.base.BaseController;import com.appms.base.Const;import com.appms.utils.Tools;import com.appms.websocket.ChatServer;import com.appms.websocket.OnlineChatServer;public class StartFilter extends BaseController implements Filter{ /** * 初始化 */ public void init(FilterConfig fc) throws ServletException { this.startWebsocketInstantMsg(); this.startWebsocketOnline(); } /** * 启动即时聊天服务 */ public void startWebsocketInstantMsg(){ WebSocketImpl.DEBUG = false; ChatServer s = null; try { String strWEBSOCKET = Tools.readTxtFile(Const.WEBSOCKET);//读取WEBSOCKET配置,获取端口配置 if(null != strWEBSOCKET && !\"\".equals(strWEBSOCKET)){ String strIW[] = strWEBSOCKET.split(\",fh,\"); if(strIW.length == 4){ s = new ChatServer(Integer.parseInt(strIW[1])); s.start(); } } System.out.println( \"websocket服务器启动,端口\" + s.getPort() ); } catch (UnknownHostException e) { e.printStackTrace(); } } /** * 启动在线管理服务 */ public void startWebsocketOnline(){ WebSocketImpl.DEBUG = false; OnlineChatServer s = null; try { String strWEBSOCKET = Tools.readTxtFile(Const.WEBSOCKET);//读取WEBSOCKET配置,获取端口配置 if(null != strWEBSOCKET && !\"\".equals(strWEBSOCKET)){ String strIW[] = strWEBSOCKET.split(\",fh,\"); if(strIW.length == 4){ s = new OnlineChatServer(Integer.parseInt(strIW[3])); s.start(); } } System.out.println( \"websocket服务器启动,端口\" + s.getPort() ); } catch (UnknownHostException e) { e.printStackTrace(); } } //计时器 public void timer() { Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.HOUR_OF_DAY, 9); // 控制时 calendar.set(Calendar.MINUTE, 0); // 控制分 calendar.set(Calendar.SECOND, 0); // 控制秒 Date time = calendar.getTime(); // 得出执行任务的时间 Timer timer = new Timer(); timer.scheduleAtFixedRate(new TimerTask() { public void run() { //PersonService personService = (PersonService)ApplicationContext.getBean(\"personService\"); } }, time, 1000*60*60*24);// 这里设定将延时每天固定执行 } public void destroy() { // TODO Auto-generated method stub } public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException { // TODO Auto-generated method stub }}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109

在web.xml里配置:

<filter> <filter-name>startFilter</filter-name> <filter-class>com.appms.filter.StartFilter</filter-class> </filter>1234

在jsp页面进行调用:

<script type=\"text/javascript\">var wimadress=\"127.0.0.1:8887\";</script> <script type=\"text/javascript\">var oladress=\"127.0.0.1:8889\";</script> <link rel=\"stylesheet\" type=\"text/css\" href=\"plugins/websocket/ext4/resources/css/ext-all.css\"> <link rel=\"stylesheet\" type=\"text/css\" href=\"plugins/websocket/css/websocket.css\" /> <script type=\"text/javascript\" src=\"plugins/websocket/ext4/ext-all-debug.js\"></script> <script type=\"text/javascript\" src=\"plugins/websocket/websocket.js\"></script> <!--引入属于此页面的js --> <script type=\"text/javascript\" src=\"source/js/jquery-1.8.3.js\"></script>123456789

点击li标签跳出聊天页面

ul class=\"am-avg-sm-1 am-avg-md-4 am-margin am-padding am-text-center admin-content-list \"> <li onclick=\"creatw();\"><a href=\"javascript:;\"><span class=\"am-icon-btn am-icon-file-text\"></span><br/>即时通讯<br/></a></li> <li><a href=\"fusioncharts/index.do\" class=\"am-text-warning\"><span class=\"am-icon-btn am-icon-briefcase\"></span><br/>图表统计<br/></a></li> <li><a href=\"#\" class=\"am-text-danger\"><span class=\"am-icon-btn am-icon-recycle\"></span><br/>昨日访问<br/>80082</a></li> <li><a href=\"#\" class=\"am-text-secondary\"><span class=\"am-icon-btn am-icon-user-md\"></span><br/>在线用户<br/>3000</a></li> </ul>123456

私聊:


群聊:


基于ExtJS前端框架的Websocket即时通讯系统