Preface
HTML5 WebSocket implements two-way communication between server and browser. Two-way communication makes the development of server message push simpler. The most common ones are instant communication and applications that require high-quality information real-time. Most of the previous server message pushes used "polling" and "long connection" technologies, both of which would incur considerable overhead on the server, and the real-time performance was not particularly high. WebSocket technology will only incur small overhead and is particularly high in real time. Let’s start by explaining how to use WebSocket technology to develop chat rooms. In this example, the Tomcat7 server is used. Each server has different implementations of WebSocket, so this instance can only be run in the Tomcat server. However, Spring has launched the WebSocket API, which is compatible with the implementation of each server. You can consult the relevant information to understand. I won’t introduce it here. The following picture is the rendering of the chat room:
In this example, real-time push of messages is implemented, and online and offline notifications of chat users are also implemented. Let’s start explaining in detail how to implement it.
Backend processing
Tomcat mainly implements WebSocket by relying on the org.apache.catalina.websocket.MessageInbound class. This class is in {TOMCAT_HOME}/lib/catalina.jar, so when you develop, you need to introduce catalina.jar and tomcat-coyote.jar. The following code is exposed to the Servlet address of the client:
package com.ibcio; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; import org.apache.catalina.websocket.StreamInbound; @WebServlet(urlPatterns = { "/message"}) //If you want to receive requests from the browser's ws:// protocol, you must implement the WebSocketServlet class 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"); } // Unlike ordinary Servlets, createWebSocketInbound needs to be implemented, and the custom WebSocket connection object is initialized here @Override protected StreamInbound createWebSocketInbound(String subProtocol,HttpServletRequest request) { return new WebSocketMessageInbound(this.getUser(request)); } } This Servlet is somewhat different from ordinary Servlets. It inherits the WebSocketServlet class and needs to override the createWebSocketInbound method. The user attribute in Session in this class is set when the user enters index.jsp, and records the current user's nickname. The following is the code of the WebSocketMessageInbound class implemented by yourself:
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 { //The user name of the current connection is private final String user; public WebSocketMessageInbound(String user) { this.user = user; } public String getUser() { return this.user; } // Event triggered by establishing a connection @Override protected void onOpen(WsOutbound outbound) { // Trigger the connection event and add the connection in the connection pool JSONObject result = new JSONObject(); result.element("type", "user_join"); result.element("user", this.user); //Press the message that the current user is online to all online users WebSocketMessageInboundPool.sendMessage(result.toString()); result = new JSONObject(); result.element("type", "get_online_user"); result.element("list", WebSocketMessageInboundPool.getOnlineUser()); //Add the current connection object to the connection pool WebSocketMessageInboundPool.addMessageInbound(this); //Send the current online user list to the current connection WebSocketMessageInboundPool.sendMessageToUser(this.user, result.toString()); } @Override protected void onClose(int status) { // Trigger the closing event and remove the connection from the connection pool WebSocketMessageInboundPool.removeMessageInbound(this); JSONObject result = new JSONObject(); result.element("type", "user_leave"); result.element("user", this.user); //Send the message to the online user that the current user exits WebSocketMessageInboundPool.sendMessage(result.toString()); } @Override protected void onBinaryMessage(ByteBuffer message) throws IOException { throw new UnsupportedOperationException("Binary message not supported."); } //The event is triggered when the client sends a message to the server @Override protected void onTextMessage(CharBuffer message) throws IOException { //Send message to all online users WebSocketMessageInboundPool.sendMessage(message.toString()); } } The code mainly implements onOpen, onClose, and onTextMessage methods, which handle users' online, offline, and send messages respectively. There is a WebSocketMessageInboundPool connection pool class in this class. This class is used to manage the connections of currently online users. The following is the code of this class:
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 { //Save the MAP container for connection private static final Map<String,WebSocketMessageInbound> connections = new HashMap<String,WebSocketMessageInbound>(); //Add a connection to the connection pool public static void addMessageInbound(WebSocketMessageInbound inbound){ //Add connection System.out.println("user : " + inbound.getUser() + " join.."); connections.put(inbound.getUser(), inbound); } //Get all online users public static Set<String> getOnlineUser(){ return connections.keySet(); } public static void removeMessageInbound(WebSocketMessageInbound inbound){ //Remove connection System.out.println("user : " + inbound.getUser() + " exit.."); connections.remove(inbound.getUser()); } public static void sendMessageToUser(String user,String message){ try { //Send data to specific users 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(); } } //Send a message to all users public static void sendMessage(String message){ try { 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)); } } } catch (IOException e) { e.printStackTrace(); } } } Front desk display
The above code is the code of the chat room backend, which is mainly composed of 3 objects, Servlet, connection object, and connection pool. The following is the code of the front desk. The code of the front desk mainly implements connection with the server and displays user list and information list. The display of the front desk uses the Ext framework. Students who are not familiar with Ext can have a preliminary understanding of Ext. The following is the code of index.jsp:
<%@ page language="java" pageEncoding="UTF-8" import="com.ibcio.WebSocketMessageServlet"%> <% String user = (String)session.getAttribute("user"); if(user == null){ //Generate a nickname for the user user = "Guest" + WebSocketMessageServlet.ONLINE_USER_COUNT; WebSocketMessageServlet.ONLINE_USER_COUNT ++; session.setAttribute("user", user); } pageContext.setAttribute("user", user); %> <html> <head> <title>WebSocket Chat Room</title> <!-- Introduce CSS files--> <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" /> <!-- Input Ext's JS development package and its own implemented 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 Chat Room</h1> <p>The API provided by the HTML5 standard is combined with the Ext rich client framework to implement the chat room, which has the following characteristics: </p> <ul style="padding-left: 10px;"> <li>Retrieve data in real time, push it by the server, realizing instant communication</li> <li>Use WebSocket to complete data communication, which is different from technologies such as polling and long connection, and saves server resources</li> <li>Combined with Ext for page display</li> <li>User online and offline notification</li> </ul> <div id="websocket_button"></div> </body> </html> The display of the page is mainly controlled in websocket.js. The following is the code of websocket.jsd:
// Used to display the user's chat information 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>Do not trust remittances, winning information, or unfamiliar phone calls during conversation. Please abide by relevant laws and regulations. </div>', '<tpl for=".">', '<div>', '<div>{from} {timestamp}</div>', '<div>{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(); }, //Show the information pushed by the server to the page 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; } } }); This code mainly implements the container that displays the message. The following is the code that starts executing after the page is loaded:
Ext.onReady(function() { //Create the user input box 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(); } } }); } } }); //Create a message display container var output = Ext.create('MessageContainer', { region : 'center' }); var dialog = Ext.create('Ext.panel.Panel', { region : 'center', layout : 'border', items : [input, output], buttons : [{ text : 'send', handler : send }] }); var websocket; //Initial WebSocket function initWebSocket() { if (window.WebSocket) { websocket = new WebSocket(encodeURI('ws://localhost:8080/WebSocket/message')); websocket.onopen = function() { //Connection successful win.setTitle(title + ' (connected)'); } websocket.onerror = function() { //Connection failed win.setTitle(title + ' (connected error occurred)'); } websocket.onclose = function() { //Connection disconnected win.setTitle(title + ' (disconnected)'); } //Message reception websocket.onmessage = function(message) { var message = JSON.parse(message.data); //Receive messages sent by the user if (message.type == 'message') { output.receive(message); } else if (message.type == 'get_online_user') { //Get online user list 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') { //The user goes online 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') { //The user goes offline var root = onlineUser.getRootNode(); var user = message.user; var node = root.findChild('id',user); root.removeChild(node); } } } } }; //Online User Tree var onlineUser = Ext.create('Ext.tree.Panel', { title : 'Online', 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 : 'Online User', expanded : true, children : [] } }) }); var title = 'Welcome: ' + user; //Display window var win = Ext.create('Ext.window.Window', { title : title + ' (not connected)', 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(); //Send message 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('Tip', 'You have been disconnected and cannot send messages!'); } } } });The above code is the code that automatically connects to the server after the page is loaded and creates the display interface.
Notice
Two points to note: after the deployment is completed, catalina.jar and tomcat-coyote.jar in the lib directory in the tomcat application directory need to be deleted. For example, the project's lib directory is D:/workspace/WebSocket/WebRoot/WEB-INF/lib, and the deployed application lib directory is D:/tools/apache-tomcat-7.0.32/webapps/WebSocket/WEB-INF/lib. Just delete the lib directory of the deployment directory and connect two jars. Otherwise, the error may not be initialize. Remember.
If the connection is still not possible, please download the latest tomcat. Forgot that version of tomcatcreateWebSocketInbound does not have a request parameter. The current code has this parameter. The 7.0.3XX versions all come with this parameter, remember.
Summarize
Using WebSocket to develop server push is very convenient. This is a simple application. In fact, it can also combine WebRTC to realize video chat and voice chat.
Example download
Download address: demo
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.