Socket.IO notes
Instant messaging is based on TCP long connection. After the connection is established, the client / server can send data to the opposite end at any time for unlimited times, so as to realize the immediacy of server data transmission
HTTP
HTTP is a short link, which is designed to reduce the pressure on the server
HTTP pseudo instant messaging
- Polling emmet
- Long polling
Application scenario
- Chat function
- Push the following order online and immediately to the merchant
Realize instant messaging
- Build your own server
- Select the supported protocol websocket xmpp
- Use some mature frameworks, socketio xmppframework
- Self encapsulating socket
- Use mature third-party solutions
- Rongyun Huanxin
Websocket
Both websocket and HTTP are based on Tcp. The difference is that HTTP establishes a short link, while websocket establishes a long link
The disadvantage is that websocket can only support some platforms
Socket.IO
Socket.io is a mature solution based on WebSocket protocol, which can realize real-time, two-way and event based communication between browsers and servers. SocketIO encapsulates WebSocket, AJAX and other communication methods into a unified communication interface, that is, we don't have to worry about compatibility when using SocketIO, The bottom layer will automatically select the best communication mode.
advantage
- Ease of use
- Cross platform
- self-adaption
shortcoming
- The transmitted data does not fully follow the webSocket protocol, which requires that both the client and server must use socket IO solutions
SpringBoot integrates SocketIO to realize real-time push
The directory structure is as follows
1. pom.xml
<dependency> <groupId>com.corundumstudio.socketio</groupId> <artifactId>netty-socketio</artifactId> <version>1.7.11</version> </dependency>
2.application.properties
server.port=8080 server.servlet.context-path=/ spring.mvc.view.prefix=/templates spring.mvc.view.suffix=.html #============================================================================ # netty socket io setting #============================================================================ # host can be set to localhost or local IP in local test, and can be changed to server IP in Linux server socketio.host=localhost socketio.port=9092 # Set the maximum length of data processed per frame to prevent others from using big data to attack the server socketio.maxFramePayloadLength=1048576 # Set the maximum content length of http interaction socketio.maxHttpContentLength=1048576 # Number and size of socket connections (if only one port is monitored, the boss thread group is 1) socketio.bossCount=1 socketio.workCount=100 socketio.allowCustomRequests=true # Protocol upgrade timeout (MS), 10 seconds by default. Timeout for upgrading HTTP handshake to ws protocol socketio.upgradeTimeout=1000000 # The timeout time (MS) of Ping message is 60 seconds by default. If no heartbeat message is received within this time interval, a timeout event will be sent socketio.pingTimeout=6000000 # Ping message interval (MS), 25 seconds by default. Interval between clients sending a heartbeat message to the server socketio.pingInterval=25000
3. Create auxiliary class
3.1 create channel connection cache class
package com.erha.socketio.cache; import com.corundumstudio.socketio.SocketIOClient; import org.springframework.context.annotation.ComponentScan; import org.springframework.stereotype.Component; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; /** * @ClassNameClientCache * @Description TODO Cache user - page sessionId - channel connection * @Author DELL * @Date 2022/1/2113:55 * @Version 1.0 **/ @Component public class ClientCache { /** * @Author Old man erha * @Description //TODO User information cache * @Date 14:00 2022/1/21 * @Param * @return **/ private static Map<String, HashMap<UUID, SocketIOClient>> concurrentHashMap = new ConcurrentHashMap<>(); /** * @Author Old man erha * @Description //TODO userId-User ID | sessionId - page sessionid | socketoclient - channel connection corresponding to page * @Date 14:03 2022/1/21 * @Param [userId, sessionId, socketIOClient] * @return void **/ public void saveClient(String userId,UUID sessionId,SocketIOClient socketIOClient){ HashMap<UUID, SocketIOClient> sessionIdClientCache = concurrentHashMap.get(userId); if(sessionIdClientCache == null){ sessionIdClientCache = new HashMap<>(); } sessionIdClientCache.put(sessionId,socketIOClient); concurrentHashMap.put(userId,sessionIdClientCache); } /** * @Author Old man erha * @Description //TODO Get the user's page channel information * @Date 14:12 2022/1/21 * @Param [userId] * @return java.util.HashMap<java.util.UUID,com.corundumstudio.socketio.SocketIOClient> **/ public HashMap<UUID,SocketIOClient> getUserClient(String userId){ return concurrentHashMap.get(userId); } /** * @Author Old man erha * @Description //TODO Delete page channel connection according to user Id and page sessionID * @Date 14:14 2022/1/21 * @Param [userId, sessionId] * @return void **/ public void deleteSessionClientByUserId(String userId,UUID sessionId){ concurrentHashMap.get(userId).remove(sessionId); } /** * @Author Old man erha * @Description //TODO Delete the user channel connection cache according to the user Id, and the cache is not used * @Date 14:19 2022/1/21 * @Param [userId] * @return void **/ public void deleteUserCacheByUserId(String userId){ concurrentHashMap.remove(userId); } }
3.2 create message class
package com.erha.socketio.pojo; import org.springframework.stereotype.Component; /** * @ClassNameMessageInfo * @Description TODO * @Author DELL * @Date 2022/1/2114:36 * @Version 1.0 **/ @Component public class MessageInfo { private String userID; private String userName; private String message; public MessageInfo() { } public MessageInfo(String userID, String userName, String message) { this.userID = userID; this.userName = userName; this.message = message; } public MessageInfo(String userName, String message) { this.userName = userName; this.message = message; } @Override public String toString() { return "MessageInfo{" + "userID='" + userID + '\'' + ", userName='" + userName + '\'' + ", message='" + message + '\'' + '}'; } public String getUserID() { return userID; } public void setUserID(String userID) { this.userID = userID; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
4. Create SocketIO configuration class
package com.erha.socketio.config; import com.corundumstudio.socketio.SocketConfig; import com.corundumstudio.socketio.SocketIOServer; import com.erha.socketio.handler.SocketIOHandler; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import javax.annotation.Resource; /** * @ClassNameSocketIOConfig * @Description TODO * @Author DELL * @Date 2022/1/2114:19 * @Version 1.0 **/ @Configuration public class SocketIOConfig implements InitializingBean { @Resource private SocketIOHandler socketIOHandler; @Value("${socketio.host}") private String host; @Value("${socketio.port}") private Integer port; @Value("${socketio.bossCount}") private int bossCount; @Value("${socketio.workCount}") private int workCount; @Value("${socketio.allowCustomRequests}") private boolean allowCustomRequests; @Value("${socketio.upgradeTimeout}") private int upgradeTimeout; @Value("${socketio.pingTimeout}") private int pingTimeout; @Value("${socketio.pingInterval}") private int pingInterval; @Override public void afterPropertiesSet() throws Exception { SocketConfig socketConfig = new SocketConfig(); socketConfig.setReuseAddress(true); socketConfig.setTcpNoDelay(true); socketConfig.setSoLinger(0); com.corundumstudio.socketio.Configuration configuration = new com.corundumstudio.socketio.Configuration(); configuration.setSocketConfig(socketConfig); // host can be set to localhost or local IP in local test, and can be changed to server IP in Linux server configuration.setHostname(host); configuration.setPort(port); // Number and size of socket connections (if only one port is monitored, the boss thread group is 1) configuration.setBossThreads(bossCount); configuration.setWorkerThreads(workCount); configuration.setAllowCustomRequests(allowCustomRequests); // Protocol upgrade timeout (MS), 10 seconds by default. Timeout for upgrading HTTP handshake to ws protocol configuration.setUpgradeTimeout(upgradeTimeout); // The timeout time (MS) of Ping message is 60 seconds by default. If no heartbeat message is received within this time interval, a timeout event will be sent configuration.setPingTimeout(pingTimeout); // Ping message interval (MS), 25 seconds by default. Interval between clients sending a heartbeat message to the server configuration.setPingInterval(pingInterval); SocketIOServer socketIOServer = new SocketIOServer(configuration); //Add event listener socketIOServer.addListeners(socketIOHandler); //Start SocketIOServer socketIOServer.start(); System.out.println("SocketIO Start up complete"); } }
5.SocketIO event response class
package com.erha.socketio.handler; import com.corundumstudio.socketio.AckRequest; import com.corundumstudio.socketio.SocketIOClient; import com.corundumstudio.socketio.annotation.OnConnect; import com.corundumstudio.socketio.annotation.OnDisconnect; import com.corundumstudio.socketio.annotation.OnEvent; import com.erha.socketio.cache.ClientCache; import com.erha.socketio.pojo.MessageInfo; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.UUID; /** * @ClassNameSocketIOHandler * @Description TODO * @Author DELL * @Date 2022/1/2114:29 * @Version 1.0 **/ @Component public class SocketIOHandler { @Resource private ClientCache clientCache; /** * @Author Old man erha * @Description //TODO Triggered when the client connects, the front-end js triggers: socket = io connect(" http://localhost:9092 "); * @Date 12:12 2022/1/20 * @Param [client] * @return void **/ @OnConnect public void onConnect(SocketIOClient client){ String userId = client.getHandshakeData().getSingleUrlParam("userId"); UUID sessionId = client.getSessionId(); clientCache.saveClient(userId,sessionId, client); System.out.println("userId: "+userId+"Connection established successfully - "+sessionId); } /** * @Author Old man erha * @Description //TODO Trigger when the client closes the connection: front end js trigger: socket disconnect(); * @Date 12:14 2022/1/20 * @Param [client] * @return void **/ @OnDisconnect public void onDisconnect(SocketIOClient client){ String userId = client.getHandshakeData().getSingleUrlParam("userId"); UUID sessionId = client.getSessionId(); clientCache.deleteSessionClientByUserId(userId,sessionId); System.out.println("userId: "+userId+"Connection closed successfully - "+sessionId); } /** * @Author Old man erha * @Description * //TODO Custom message event, triggered by client js: socket emit('messageevent', {msgContent: msg}); This method is triggered when * //TODO Front end js socket The emit ("event name", "parameter data") method is used when triggering back-end custom message events * //TODO Front end js socket On ("event name", anonymous function (data sent by the server to the client)) is to listen to events on the server * @Date 13:51 2022/1/20 * @Param [client, request, data] * @return void **/ @OnEvent("chatevent") public void chatEvent(SocketIOClient client, AckRequest ackRequest, MessageInfo message){ HashMap<UUID, SocketIOClient> userClient = clientCache.getUserClient("79"); Iterator<Map.Entry<UUID, SocketIOClient>> iterator = userClient.entrySet().iterator(); while(iterator.hasNext()){ Map.Entry<UUID, SocketIOClient> next = iterator.next(); next.getValue().sendEvent("chatevent", message); } System.out.println(message); } }
6. Control layer Controller
6.1 page Jump to basic class
@Controller public class baseController { @RequestMapping(value = "/") public String index(){ return "index"; } }
6.2 the page sends information according to the channel connection test class
@RestController @RequestMapping("/push") public class PushController { @Resource private ClientCache clientCache; @GetMapping("/user/{userId}") public String pushTuUser(@PathVariable("userId") String userId){ HashMap<UUID, SocketIOClient> userClient = clientCache.getUserClient(userId); userClient.forEach((uuid, socketIOClient) -> { //Push message to client socketIOClient.sendEvent("chatevent",new MessageInfo("administrators","Messages sent to customer segments")); }); return "success"; } }
7. Front page
The front-end page needs to introduce socket io. js package of js
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Demo Chat</title> <link href="css/bootstrap.css" rel="stylesheet"> <style> body { padding:20px; } #console { height: 400px; overflow: auto; } .username-msg {color:orange;} .connect-msg {color:green;} .disconnect-msg {color:red;} .send-msg {color:#888} </style> <script src="js/socket.io/socket.io.js"></script> <script src="js/moment.min.js"></script> <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script> <script> var userName = 'user' + Math.floor((Math.random()*1000)+1); //Create channel connection var socket = io.connect('http://localhost:9092?userId=79'); socket.on('connect', function() { output('<span class="connect-msg">Client has connected to the server!</span>'); }); socket.on('chatevent', function(message) { output('<span class="username-msg">' + message.userName + ':</span> ' + message.message); }); socket.on('disconnect', function() { output('<span class="disconnect-msg">The client has disconnected!</span>'); }); //Closing a channel connection can be monitored by the @ OnDisconnect annotated method function sendDisconnect() { socket.disconnect(); } function sendMessage() { var message = $('#msg').val(); $('#msg').val(''); var jsonObject = { userName: userName, message: message }; //Method to send back-end @ OnEvent("chatevent") annotation socket.emit('chatevent', jsonObject); } function output(message) { var currentTime = "<span class='time'>" + moment().format('HH:mm:ss.SSS') + "</span>"; var element = $("<div>" + currentTime + " " + message + "</div>"); $('#console').prepend(element); } $(document).keydown(function(e){ if(e.keyCode == 13) { $('#send').click(); } }); </script> </head> <body> <h1>Netty-socketio Demo Chat</h1> <br/> <div id="console" class="well"> </div> <form class="well form-inline" onsubmit="return false;"> <input id="msg" class="input-xlarge" type="text" placeholder="Type something..."/> <button type="button" onClick="sendMessage()" class="btn" id="send">Send</button> <button type="button" onClick="sendDisconnect()" class="btn">Disconnect</button> </form> </body> </html>
Core code
Create a connection. The execution of this statement will call the method annotated by @ OnConnect in the corresponding class of SocketIO event on the server
//Create channel connection var socket = io.connect('http://localhost:9092? userId=79'); // --@ Method of onconnect annotation
Listening code
//The @ OnConnect annotation method of event listening server-side listener socket.on('connect', function() { output('<span class="connect-msg">Client has connected to the server!</span>'); }); //Method of @ OnDisconnect annotation of event listening server-side listener socket.on('disconnect', function() { output('<span class="disconnect-msg">The client has disconnected!</span>'); }); //The user-defined page listening event listens to the information sent by the back-end. The corresponding code of the back-end page is socketoclient sendEvent("chatevent",message); socket.on('chatevent', function(message) { output('<span class="username-msg">' + message.userName + ':</span> ' + message.message); }); //Custom event format socket.on("Custom event name",function(message){.....})
Function code
//Closing a channel connection can be monitored by the @ OnDisconnect annotated method socket.disconnect(); //The method of sending @ OnEvent("chatevent") annotation to the server socket.emit('chatevent', message);
Effect display
Enter integration link http://localhost:8080/ Enter the chat page
Background prompt:
Input http://localhost:8080/push/user/79 Send a message from the server first
Page information sending
Close link