This article author taro, original title “taro way Spring Boot WebSocket introduction”, this time have revision and change.

One, the introduction

WebSocket is now widely used in web-based instant messaging applications, not only for traditional PC Web pages, but also by many mobile developers for HTML5-based hybrid apps. WebSocket is almost a must for adding IM, push, and other real-time communication capabilities to web-based applications.

This article will implement a logically simple entry level IM application based on the Tomcat and Spring frameworks. For instant messaging beginners, it is obviously more meaningful to find a simple, direct and smooth running example code, which is exactly what this article does. Hope to give you inspiration for IM development and learning.

Note: The source code is available as an attachment at the beginning of sections 4 and 5 of this article.

Learning and communication:

  • Im/Push Technology Development Exchange 5 groups: 215477170 [Recommended]
  • Introduction to Mobile IM Development: One Entry is Enough: Developing Mobile IM from Zero
  • Open source IM framework source: https://github.com/JackJiang2011/MobileIMSDK

(synchronous published in this article: http://www.52im.net/thread-3483-1-1.html)

Two, knowledge preparation

If you don’t know anything about Instant messaging on the Web, be sure to read: “Getting Started: The Most Comprehensive Explanation of The Principles of Instant Messaging on the Web”, “Instant Messaging On the Web: Short polling, Comet, Websocket, SSE”.

Due to space limitations, this article will not delved into WebSocket technology theory. If you are interested, please learn from the basics:

  • Quick Start: WebSocket Tutorial
  • WebSocket from Beginner to Master in half an hour!
  • WebSocket Protocol: Quick Answers to WebSocket Hot Questions
  • “WebSocket in detail (a) : a preliminary understanding of WebSocket technology”
  • WebSocket in detail (2) : Technical principles, code demonstration and application cases
  • WebSocket In Detail (3) : Deep WebSocket Communication Protocol details
  • WebSocket in Detail (4) : Probing into the Relationship between HTTP and WebSocket (Part 1)
  • WebSocket in Detail (5) : The Relationship between HTTP and WebSocket (Part 2)
  • WebSocket Details (6) : Probing into the Relationship between WebSocket and Socket

If you want to be a bit more hardcore, here are a few:

  • WebSocket Core: 200 Lines of Code
  • Web Im Practice Tips: How to get Your WebSocket to reconnect faster?
  • “Theory to Practice: Understanding WebSocket Communication Principle, Protocol Format, security from Zero”

Iii. Content overview

Compared to HTTP, the WebSocket protocol is relatively new to most backend developers.

Relatively speaking: WebSocket protocol focus is to provide the server side to send data actively to the client, so that we can complete the real-time demand. For example: Chat IM messaging features, subscription messaging services, web games, etc.

Also: Because WebSocket uses TCP for communication, you can avoid repeated connection creation and improve communication quality and efficiency. For example, meituan-Dianping’s Long connection service. For details, see “Meituan-Dianping’s Mobile Network Optimization Practices: Greatly Improving the Success rate and Speed of Connection”.

Tips:

There is a misconception that webSockets, compared to normal sockets, only use HTTP to complete the handshake and create a connection. All subsequent communications are independent of the HTTP protocol.

At this point, you’d think we’d be beeping the WebSocket concept again. If you can’t do this, you can read the chapter “2. Knowledge Preparation” in this article.

To use WebSocket, there are several solutions:

  • 1) Solution 1: Spring WebSocket;
  • 2) Solution 2: Tomcat WebSocket;
  • 3) Solution 3: Netty WebSocket.

At present, the author has a project involving IM communication, using scheme 3.

The main reason is: we are relatively familiar with the Netty framework in practice, principle and source code, so we consider it. And, in addition to the need to support the WebSocket protocol, we also want to provide the native Socket protocol.

If only WebSocket protocol support is provided, you can consider solution one or two, in use, the two solutions are relatively close. In contrast, solution one, Spring WebSocket, has built-in support for STOMP.

However, this article uses scenario 2, “Tomcat WebSocket,” as an example to get started. Ahem, for no particular reason, except that I spent two hours writing an example with it before I started this article. I’m a little lazy. I don’t want to change. If I could do it again, I would choose Li Bai

Of course, don’t panic, the implementation code for solution one and solution two is really no different.

Before we start building an example for getting started with Tomcat WebSocket, let’s take a look at the JSR-356 specification, which defines the Java API for WebSockets: Javax WebSocket. The specification is the big brother and will not provide an implementation, so the same is true of JSR-356. Currently, jSR-356 implementations are available in mainstream Web containers such as Tomcat, Jetty, Undertow, and so on.

Four, Tomcat WebSocket actual combat entry

4.1 Basic Introduction

Sample code download:

(Because the attachment cannot be uploaded here, please download it from the synchronization link:
http://www.52im.net/thread-3483-1-1.html)

The code directory looks like this:

In this section, we’ll build a WebSocket example using Tomcat WebSocket.

Supports the following message functions:

  • 1) Identity authentication request;
  • 2) Private chat messages;
  • 3) Group chat messages.

To make the example easier to understand, let’s make it global and have only one large chat room, that is, all connections to the WebSocket are automatically entered into the chat room.

Next, start roaming the WebSocket fish pond…

4.2. Importing dependencies

In the POM.xml file, introduce dependencies.

<? The XML version = “1.0” encoding = “utf-8”? >


The < project XMLNS = “http://maven.apache.org/POM/4.0.0”


         xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”


Xsi: schemaLocation = “http://maven.apache.org/POM/4.0.0 [url =
http://maven.apache.org/xsd/m…”>


    <parent>


        <groupId>org.springframework.boot</groupId>


        <artifactId>spring-boot-starter-parent</artifactId>


< version > 2.1.10. RELEASE < / version >


<relativePath/> <! — lookup parent from repository –>


    </parent>


The < modelVersion > 4.0.0 < / modelVersion >


    <artifactId>lab-25-01</artifactId>


    <dependencies>


<! — Implement the introduction of WebSocket dependency, convenient ~ –>


        <dependency>


            <groupId>org.springframework.boot</groupId>


            <artifactId>spring-boot-starter-websocket</artifactId>


        </dependency>


<! — Introduce Fastjson to serialize JSON, because we will use it to parse the message later –>


        <dependency>


            <groupId>com.alibaba</groupId>


            <artifactId>fastjson</artifactId>


The < version > 1.2.62 < / version >


        </dependency>


    </dependencies>


</project>


Read the comments for yourself to see what each dependency does.

4.3, WebsocketServerEndpoint

In cn. Iocoder. Springboot. Lab25. Springwebsocket. Websocket package path, create WebsocketServerEndpoint class, define the websocket service EndPoint (the EndPoint).

The code is as follows:

// WebsocketServerEndpoint.java


@Controller


@ServerEndpoint(“/”)


public class WebsocketServerEndpoint {


    private Logger logger = LoggerFactory.getLogger(getClass());


    @OnOpen


    public void onOpen(Session session, EndpointConfig config) {


        logger.info(“onOpen”, session);


    }


    @OnMessage


    public void onMessage(Session session, String message) {


logger.info(“onOpen”, session, message); // In a production environment, set the level to debug


    }


    @OnClose


    public void onClose(Session session, CloseReason closeReason) {


        logger.info(“onClose”, session, closeReason);


    }


    @OnError


    public void onError(Session session, Throwable throwable) {


        logger.info(“onClose”, session, throwable);


    }


}

The code looks like this:

  • 1) Add the @Controller annotation to the class to ensure that a WebsocketServerEndpoint Bean is created;
  • 2) Add @serverEndpoint as defined by JSR-356 to the class, marking it as a WebSocket EndPoint with a/path;
  • 3) WebSocket has four events, corresponding to @onOpen, @onMessage, @onClose, and @onError annotations defined by JSR-356.

This is the simplest version of the WebsocketServerEndpoint code. We will complete the code later.

4.4, WebSocketConfiguration

In cn. Iocoder. Springboot. Lab24. Springwebsocket. Config directory, create a WebsocketServerEndpoint configuration class.

The code is as follows:

// WebSocketConfiguration.java


@Configuration


// @enableWebSocket // There is no need to add this annotation because we are not using Spring WebSocket


public class WebSocketConfiguration {


    @Bean


    public ServerEndpointExporter serverEndpointExporter() {


        return new ServerEndpointExporter();


    }


}

PS: In #serverEndpointExporter(), create the serverEndpointExporter Bean. The function of this Bean is to scan for beans that have the @ServerEndpoint annotation added.

4.5, Application

Create the application. Java class and configure the @SpringBootApplication annotation.

The code is as follows:

// Application.java


@SpringBootApplication


public class Application {


    public static void main(String[] args) {


        SpringApplication.run(Application.class, args);


    }


}

Execute Application to start the sample project.

Considering that you may not be able or willing to write front-end code, we went straight to the WebSocket online testing tool to test WebSocket connections.

The diagram below:

At this point, the skeleton of one of the simplest WebSocket projects has been built. Next, we begin to transform and complete the corresponding logic.

4.6, message,

In THE HTTP protocol, is based on Request/Response Request Response synchronization model, interaction. In Websocket protocol, is based on the Message Message asynchronous model, interaction. This is a big difference, and will be more obvious when you look at the specific message classes.

The WebSocket protocol, unlike HTTP, does not have urIs to distinguish between API requests, so we need to add the WebSocket Message to identify the Message type. In this case, we use the Type field.

So in this example, the Message is encoded in JSON format.

The format is as follows:

{


Type: “”, // Message type


Body: {} // Body of the message


}

To explain:

  • 1) Type field, message type. From this field, we know which MessageHandler to use (we will parse MessageHandler in detail in the next section);
  • 2) The body field of the message. Different message types have different message bodies.
  • 3) Message is encoded in JSON format, mainly considering convenience. In actual projects, Protobuf and other more efficient and traffic saving encoding formats can also be considered.

In fact: In our example, the body field corresponds to the message-related interface and class, whose name is unexpected. All the Message, we are all in cn iocoder. Springboot. Lab25. Springwebsocket. The Message path.

4.6.1 Message

Create the Message interface, the base Message body, which all Message bodies will implement.

The code is as follows:

// Message.java


publicinterfaceMessage {


}

Currently as a tag interface, no operations are defined.

4.6.2 Authentication-related messages

Create the AuthRequest class where the user authenticates the request.

The code is as follows:

// AuthRequest.java


public class AuthRequest implements Message {


    public static final String TYPE = “AUTH_REQUEST”;


/ * *


* the authentication Token


* /


    private String accessToken;


/ /… Omit the set/get method


}

To explain:

  • 1) TYPE Static property. The message TYPE is AUTH_REQUEST.
  • 2) The accessToken attribute authenticates the Token.

For point 2), in the WebSocket protocol, we also need to authenticate the current connection and what the user identity is. In general, we use the accessToken accessToken that the user calls the HTTP login interface and returns after the successful login. Let’s not expand on this, but check out the article “WebSocket Connections based on Token Authentication”.

However, the WebSocket protocol is based on the Message model for interaction. However, this does not mean that its operations do not need to respond to results. For example, a user authentication request requires a user authentication response. So, we create the AuthResponse class as the user authentication response.

The code is as follows:

// AuthResponse.java


public class AuthResponse implements Message {


    public static final String TYPE = “AUTH_RESPONSE”;


/ * *


* Response status code


* /


    private Integer code;


/ * *


* Response prompt


* /


    private String message;


/ /… Omit the set/get method


}

To explain:

  • 1) TYPE Static property, the message TYPE is AUTH_REQUEST;
  • 2) Code attribute, response status code;
  • 3) Message property, response prompt.

For point 1), we actually added the TYPE static property to each Message implementation class as the Message TYPE. Now, we won’t repeat it.

In this example, after a user is successfully authenticated, a notification Message about the user joining the group chat is broadcast, using UserJoinNoticeRequest.

The code is as follows:

// UserJoinNoticeRequest.java


public class UserJoinNoticeRequest implements Message {


    public static final String TYPE = “USER_JOIN_NOTICE_REQUEST”;


/ * *


* nicknames


* /


    private String nickname;


/ /… Omit the set/get method


}

In fact, we can extend Message where we need to use the Request/Response model:

  • 1) Request abstract class, add requestId field, indicating the Request number;
  • 2) Response abstract class, add requestId field, and each Request mapping (at the same time, the uniform definition of code and message properties, indicating the Response status code and Response prompt).

Thus, in a business scenario using the synchronous model, the Message implementation class uses Request/Reponse as the suffix. For example, a user authenticates a request, deletes a friend request, and so on.

In business scenarios where the asynchronous model is used, the Message implementation class continues as Message. For example, a message can be sent, and after the user has finished, there is no need to block and wait for the result

4.6.3 Sending Messages Related messages

Create the SendToOneRequest class to send the Message of the private chat Message to the specified person.

The code is as follows:

// SendToOneRequest.java


public class SendToOneRequest implements Message {


    public static final String TYPE = “SEND_TO_ONE_REQUEST”;


/ * *


* To the user


* /


    private String toUser;


/ * *


* Message number


* /


    private String msgId;


/ * *


* content


* /


    private String content;


/ /… Omit the set/get method


}

Read the comments for each field.

Create the SendToAllRequest class to send the Message of the group chat Message to all the people.

The code is as follows:

// SendToAllRequest.java


public class SendToAllRequest implements Message {


    public static final String TYPE = “SEND_TO_ALL_REQUEST”;


/ * *


* Message number


* /


    private String msgId;


/ * *


* content


* /


    private String content;


/ /… Omit the set/get method


}

Read the comments for each field.

The server receives a request to send a message and needs to respond asynchronously to whether the message is sent successfully. So, create the SendResponse class to send a Message in response to the result.

The code is as follows:

// SendResponse.java


public class SendResponse implements Message {


    public static final String TYPE = “SEND_RESPONSE”;


/ * *


* Message number


* /


    private String msgId;


/ * *


* Response status code


* /


    private Integer code;


/ * *


* Response prompt


* /


    private String message;


/ /… Omit the set/get method


}

Focus on the msgId field: message number. When the client sends a message, it uses the UUID algorithm to generate a globally unique message number (for the generation technology of unique ID, see section “_5, Unique ID Technical Solution _” in “From novice to Expert: How to design a Distributed IM System with billions of Messages”). In this way, the server responds with a SendResponse message and does the mapping through the msgId.

The server receives a request to send a message and needs to forward the message to the corresponding person. So, create the SendToUserRequest class to send a Message to a user’s Message.

The code is as follows:

// SendResponse.java


public class SendToUserRequest implements Message {


    public static final String TYPE = “SEND_TO_USER_REQUEST”;


/ * *


* Message number


* /


    private String msgId;


/ * *


* content


* /


    private String content;


/ /… Omit the set/get method


}

There is one less toUser field than SendToOneRequest. Because we already know who to send it to through a WebSocket connection.

4.7 message Processor

For each Message type that the client initiates, we declare the corresponding MessageHandler. This is similar to SpringMVC, where each API corresponds to a Controller Method.

All the MessageHandler, we are all in cn. Iocoder. Springboot. Lab25. Springwebsocket. Handler package under the path.

4.7.1 MessageHandler

Create the MessageHandler interface.

The code is as follows:

// MessageHandler.java


public interface MessageHandler<T extends Message> {


/ * *


* Execute the processing message


     *


* @Param session


* @param message


* /


    void execute(Session session, T message);


/ * *


* @return Message TYPE, which is the TYPE static field on each Message implementation class


* /


    String getType();


}

To explain:


  • is defined as a generic

    class.
  • 2) define two interface methods, see the comments.

4.7.2 AuthMessageHandler

Create the AuthMessageHandler class to process the AuthRequest message.

The code is as follows:

// AuthMessageHandler.java


@Component


public class AuthMessageHandler implements MessageHandler<AuthRequest> {


    @Override


    public void execute(Session session, AuthRequest message) {


// If accessToken is not passed


        if(StringUtils.isEmpty(message.getAccessToken())) {


            WebSocketUtil.send(session, AuthResponse.TYPE,


New AuthResponse().setCode(1).setMessage(” authenticated accessToken not passed “));


            return;


        }


// Add to WebSocketUtil


WebSocketUtil.addSession(session, message.getAccessToken()); // To simplify the code, let’s use the accessToken User directly


// Check whether the authentication is successful. Here, pretend direct success


        WebSocketUtil.send(session, AuthResponse.TYPE,newAuthResponse().setCode(0));


// Notify everyone that someone has joined. This is optional logic, just for demonstration purposes


        WebSocketUtil.broadcast(UserJoinNoticeRequest.TYPE,


newUserJoinNoticeRequest().setNickname(message.getAccessToken())); // To simplify the code, let’s use the accessToken User directly


    }


    @Override


    public String getType() {


        return AuthRequest.TYPE;


    }


}

The code is relatively simple, follow the code to read.

We’ll look at the WebSocketUtil classes in more detail in section 5.8, WebSocketUtil.

4.7.3 SendToOneRequest

Create the SendToOneHandler class to process the SendToOneRequest message.

The code is as follows:

// SendToOneRequest.java


@Component


public class SendToOneHandler implements MessageHandler<SendToOneRequest> {


    @Override


    public void execute(Session session, SendToOneRequest message) {


// Here, pretend direct success


        SendResponse sendResponse = newSendResponse().setMsgId(message.getMsgId()).setCode(0);


        WebSocketUtil.send(session, SendResponse.TYPE, sendResponse);


// Create the forwarded message


        SendToUserRequest sendToUserRequest = newSendToUserRequest().setMsgId(message.getMsgId())


                .setContent(message.getContent());


// Broadcast transmission


        WebSocketUtil.send(message.getToUser(), SendToUserRequest.TYPE, sendToUserRequest);


    }


    @Override


    public String getType() {


        return SendToOneRequest.TYPE;


    }


}

The code is relatively simple, follow the code to read.

4.7.4 SendToAllHandler

Create the SendToAllHandler class to process the SendToAllRequest message.

The code is as follows:

// SendToAllRequest.java


@Component


public class SendToAllHandler implements MessageHandler<SendToAllRequest> {


    @Override


    public void execute(Session session, SendToAllRequest message) {


// Here, pretend direct success


        SendResponse sendResponse = newSendResponse().setMsgId(message.getMsgId()).setCode(0);


        WebSocketUtil.send(session, SendResponse.TYPE, sendResponse);


// Create the forwarded message


        SendToUserRequest sendToUserRequest = newSendToUserRequest().setMsgId(message.getMsgId())


                .setContent(message.getContent());


// Broadcast transmission


        WebSocketUtil.broadcast(SendToUserRequest.TYPE, sendToUserRequest);


    }


    @Override


    public String getType() {


        return SendToAllRequest.TYPE;


    }


}

The code is relatively simple, follow the code to read.

4.8, WebSocketUtil

Code in cn. Iocoder. Springboot. Lab25. Springwebsocket. The util package path.

Create a WebSocketUtil utility class that provides two main functions:

  • 1) Session management;
  • 2) Multiple ways to send messages.

The overall code is simple, take a look at it yourself.

The code is in the following location in the directory:

4.9. Improve WebsocketServerEndpoint

In this section, we will modify the WebsocketServerEndpoint code to improve its functionality.

4.9.1 Initializing the MessageHandler set

Implement the InitializingBean interface and, in the #afterPropertiesSet() method, scan all MessageHandler beans and add them to the MessageHandler collection.

The code is as follows:

// WebsocketServerEndpoint.java


/ * *


* Mapping message types to MessageHandler


 *


* Note that this is set to a static variable. Although WebsocketServerEndpoint is a singleton, Spring Boot creates a WebsocketServerEndpoint Bean for each WebSocket.


* /


private static final Map<String, MessageHandler> HANDLERS = newHashMap<>();


@Autowired


private ApplicationContext applicationContext;


@Override


public void afterPropertiesSet() throws Exception {


// Get all MessageHandler beans from ApplicationContext


ApplicationContext. GetBeansOfType (MessageHandler. The class). The values () / / get all the MessageHandler Bean. The forEach (MessageHandler – > HANDLERS.put(messageHandler.getType(), messageHandler)); // Add to the handlers


    logger.info(“afterPropertiesSet”, HANDLERS.size());


}

In this way, you can avoid manually configuring MessageHandler to message type mappings.

4.9.2 onOpen

Re-implement the #onOpen(Session Session, EndpointConfig Config) method to implement the accessToken parameter for user authentication when connecting.

The code is as follows:

// WebsocketServerEndpoint.java


@OnOpen


public void onOpen(Session session, EndpointConfig config) {


    logger.info(“onOpen”, session);


// <1> Parse accessToken


    List<String> accessTokenValues = session.getRequestParameterMap().get(“accessToken”);


String accessToken = ! CollectionUtils.isEmpty(accessTokenValues) ? accessTokenValues.get(0) : null;


// <2> Create the AuthRequest message type


    AuthRequest authRequest = newAuthRequest().setAccessToken(accessToken);


// <3> Get the message handler


    MessageHandler<AuthRequest> messageHandler = HANDLERS.get(AuthRequest.TYPE);


    if(messageHandler == null) {


        logger.error(“onOpen”);


        return;


    }


    messageHandler.execute(session, authRequest);


}

The code looks like this:

  • At <1> : parse the request parameter of the accessToken on the WS :// address. For example: ws://127.0.0.1:8080? AccessToken = 999999;
  • <2> : Create the AuthRequest message type and set the accessToken attribute;
  • <3> : obtain the MessageHandler message handler corresponding to the AuthRequest message type, and then call the MessageHandler#execute(session, message) method to process the user authentication request.

Open the three browsers to create, and set the service address as follows:

  • 1) the ws: / / 127.0.0.1:8080 /? AccessToken = taro;
  • 2) the ws: / / 127.0.0.1:8080 /? AccessToken = tomatoes;
  • 3) the ws: / / 127.0.0.1:8080 /? AccessToken = potato.

Then, click the “Open Connection” button one by one to make WebSocket connections.

The final effect is as follows:

As the picture above shows:

  • 1) In the red circle, you can see the AuthResponse message;
  • 2) In the yellow circle, you can see the message from UserJoinNoticeRequest.

4.9.3 onMessage

The _#onMessage(Session Session, String message)_ method is reimplemented to realize the different message, forward to different MessageHandler message handler.

The code is as follows:

// WebsocketServerEndpoint.java


@OnMessage


public void onMessage(Session session, String message) {


logger.info(“onOpen”, session, message); // In a production environment, set the level to debug


    try{


// <1> Get the message type


        JSONObject jsonMessage = JSON.parseObject(message);


        String messageType = jsonMessage.getString(“type”);


// <2> Get the message handler


        MessageHandler messageHandler = HANDLERS.get(messageType);


        if(messageHandler == null) {


            logger.error(“onMessage”, messageType);


            return;


        }


// <3> Parse the message


        Class<? extendsMessage> messageClass = this.getMessageClass(messageHandler);


// <4> Process the message


        Message messageObj = JSON.parseObject(jsonMessage.getString(“body”), messageClass);


        messageHandler.execute(session, messageObj);


    } catch(Throwable throwable) {


        logger.info(“onMessage”, session, throwable);


    }


}

In the code:

  • <1>, get the message type from the “type” field;
  • <2>, the MessageHandler corresponding to the message type is obtained;
  • <3>, call #getMessageClass(MessageHandler handler) method, through the MessageHandler, by parsing the generics on its Class, to get the corresponding Class of the message type.

The code is as follows:

// WebsocketServerEndpoint.java


private Class<? extends Message> getMessageClass(MessageHandler handler) {


// Get the Class name of the Bean. Because it is possible to be proxyed by AOP.


Class<? > targetClass = AopProxyUtils.ultimateTargetClass(handler);


// Get an array of types for the interface


    Type[] interfaces = targetClass.getGenericInterfaces();


Class<? > superclass = targetClass.getSuperclass();


While ((Objects. IsNull (interfaces) | | 0 = = interfaces. The length) && Objects. NonNull (superclass)) {/ / here, is the parent class interface shall prevail


        interfaces = superclass.getGenericInterfaces();


        superclass = targetClass.getSuperclass();


    }


    if(Objects.nonNull(interfaces)) {


// Iterate over the interfaces array


        for(Type type : interfaces) {


// Requires that type be a generic parameter


            if(type instanceof ParameterizedType) {


                ParameterizedType parameterizedType = (ParameterizedType) type;


// MessageHandler interface is required


                if(Objects.equals(parameterizedType.getRawType(), MessageHandler.class)) {


                    Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();


// Take the first element


                    if(Objects.nonNull(actualTypeArguments) && actualTypeArguments.length > 0) {


                        return(Class<Message>) actualTypeArguments[0];


                    } else{


ThrownewIllegalStateException (String format (” type (% s) can’t get the message type “, handler));


                    }


                }


            }


        }


    }


Throw new IllegalStateException(string.format (” type (%s) cannot get message type “, handler));


}

This is a reference rocketmq – spring project DefaultRocketMQListenerContainer# getMessageType () method, slightly modified.

If you’re not familiar with Java’s generics mechanics, you might be a little hardcore. You can skip it for a moment and just know the intent.

<4>, call the MessageHandler#execute(session, message) method to execute the processing request.

In addition, the try-catch code is added to prevent exceptions from occurring throughout the execution. If an exception occurs during the processing of the onMessage event, the Session Session corresponding to the message is automatically closed. Obviously, this does not meet our requirements. For example, in the process of MessageHandler processing messages, some exceptions are inevitable.

Continuing with the three browsers created above, let’s first click on the “Clear Message” button to clear the next Message and clean up the received Message shown in the last test. WebSocket connections, of course, do not need to be disconnected.

In the first browser, send two separate chat messages.

A private SendToOneRequest message:

{


    type: “SEND_TO_ONE_REQUEST”,


    body: {


ToUser: tomato,


        msgId: “eaef4a3c-35dd-46ee-b548-f9c4eb6396fe”,


Content: “I’m a single chat message”


    }


}

A SendToAllHandler group chat message:

{


    type: “SEND_TO_ALL_REQUEST”,


    body: {


        msgId: “838e97e1-6ae9-40f9-99c3-f7127ed64747”,


Content: “I’m a group message”


    }


}

The final result is as follows:

As the picture above shows:

  • 1) In the red circle, you can see a SendToUserRequest message, which is only received by the second browser (Tomato);
  • 2) In the yellow circle, you can see three SendToUserRequest messages, which are received by all browsers.

4.9.4 onClose

#onClose(Session Session, CloseReason CloseReason)_

The code is as follows:

// WebsocketServerEndpoint.java


@OnClose


public void onClose(Session session, CloseReason closeReason) {


    logger.info(“onClose”, session, closeReason);


    WebSocketUtil.removeSession(session);


}

4.9.5 onError

#onError(Session Session, Throwable Throwable);

The code is as follows:

// WebsocketServerEndpoint.java


@OnError


public void onError(Session session, Throwable throwable) {


    logger.info(“onClose”, session, throwable);


}

Five, Spring WebSocket combat entry

5.0. Basic Introduction

Sample code download:

(Because the attachment cannot be uploaded here, please download it from the synchronization link:
http://www.52im.net/thread-3483-1-1.html)

Carefully a subtle, tiger body shock, or provide a Spring WebSocket quick start example.

On the basis of the example of _lab-websocket-25-01_ in the previous chapter “A Practical Introduction to Tomcat WebSocket”, we copy the lab-Websocket-25-02 project and transform it.

The modified code directory looks like this:

5.1, WebSocketUtil

Because Tomcat WebSocket uses Session as a Session and Spring WebSocket uses WebSocketSession as a Session, we need to modify the WebSocketUtil utility class slightly. This is a very slight change. Click on webSocketutil. Java to check it out.

There are two main points:

  • 1) Adjust the WebSocketSession class wherever the Session class is used;
  • 2) Change the sending message from Session to WebSocketSession.

5.2. Message Processor

Will _cn. Iocoder. Springboot. Lab25. Springwebsocket. Handler_ package under the path of the message handler, to use the Session class, adjust into WebSocketSession class.

5.3, DemoWebSocketShakeInterceptor

In _cn. Iocoder. Springboot. Lab25. Springwebsocket. Websocket_ package path, create DemoWebSocketShakeInterceptor interceptors. Because WebSocketSession cannot get the request parameter on the WS address, it has to get the accessToken request parameter through the interceptor and set it to attributes.

The code is as follows:

// DemoWebSocketShakeInterceptor.java


public class DemoWebSocketShakeInterceptor extends HttpSessionHandshakeInterceptor {


Override// Block Handshake


    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,WebSocketHandler wsHandler, Map<String, Object> attributes) throwsException {


/ / accessToken


        if(request instanceof ServletServerHttpRequest) {


            ServletServerHttpRequest serverRequest = (ServletServerHttpRequest) request;


            attributes.put(“accessToken”, serverRequest.getServletRequest().getParameter(“accessToken”));


        }


// Call the parent method to continue executing the logic


        return super.beforeHandshake(request, response, wsHandler, attributes);


    }


}

5.4, DemoWebSocketHandler

In _cn. Iocoder. Springboot. Lab25. Springwebsocket. Websocket_ package path, create DemoWebSocketHandler processor. The processor is coded by referring to section 5.9, Perfecting WebsocketServerEndpoint.

DemoWebSocketHandler. Java code is located at the following directory, specific content is not posted, have already read it:

The code is very similar, and it can be simply removed.

5.5, WebSocketConfiguration

Modify the WebSocketConfiguration configuration class with the following code:

// WebSocketConfiguration.java


@Configuration


@enableWebSocket // Enable Spring WebSocket


public class WebSocketConfiguration implements WebSocketConfigurer {


    @Override


    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {


Registry.addhandler (this.webSockethandler (), “/”) // Configures the handler


AddInterceptors (newDemoWebSocketShakeInterceptor ()) / / configure interceptor


.setAllowedOrigins(“*”); // Solve cross-domain problems


    }


    @Bean


    public DemoWebSocketHandler webSocketHandler() {


        return new DemoWebSocketHandler();


    }


    @Bean


    public DemoWebSocketShakeInterceptor webSocketShakeInterceptor() {


        return new DemoWebSocketShakeInterceptor();


    }


}

To explain:

  • 1) Add @enableWebSocket annotation to the class to enable Spring WebSocket;
  • 2) implement WebSocketConfigurer interface, custom WebSocket configuration (specific see # registerWebSocketHandlers (registry) method, the configuration WebSocket processor, interceptors, And allow cross-domain).

At this point, we have completed the Spring WebSocket example.

Next, we execute Application to start the project. The specific test, here will not repeat, you can use the WebSocket online testing tool to test under.

Write at the end

Although the WebSocket protocol is well supported in the major browsers, there are always some “outliers” that are not compatible. So there are libraries like SockJS and socket. IO. About their introduction and use, can see “SockJS brief introduction”, “The development of Web instant communication technology and WebSocket, socket. IO technology practice” article.

In the actual scenario, whether we are using a WebSocket or a native Socket, we need to consider “how to ensure that the message is delivered to the user?”

One thing you can certainly imagine is that messages persist to MySQL, MongoDB, etc., when the user is not online. This is correct and it has to be done.

Let’s consider the boundary scenario: The network environment of the client is poor, especially in the mobile terminal scenario. The network disconnection may occur. The connection may actually be disconnected, but the server may think the client is online. At this point, the server will send the message to the client, and the message will actually be sent to the “air”, resulting in loss.

To solve this problem, you need to introduce a client-side ACK message mechanism.

At present, there are two approaches in the mainstream.

The first:ACK number based on each message

The overall process is as follows:

  • 1) Whether the client is online or not, the server first persists the received message to the database. If the client is online, the server pushes the complete message to the client.
  • 2) After receiving the message, the client sends an ACK message number to the server to inform it that it has received the message. When receiving the ACK message number, the server marks that the message has been sent successfully.
  • 3) The server periodically polls the online client whether there is any message that has not been ACK for more than N seconds. If yes, resend the message to the corresponding client.

In this scheme, because the client ACK messages are numbered one by one, the interaction between the client and the server will be excessive. Of course, clients can asynchronously ACK multiple messages in batches, thus reducing the number of times.

However, because the server still needs to poll regularly, it can also cause a lot of pressure on the server. As a result, this kind of scheme has been basically abandoned.

The second:Sliding window BASED ACK

The overall process is as follows:

  • 1) Whether the client is online or not, the server first persists the received message to the database. If the client is online, the server pushes the message number to the client.
  • 2) After receiving the message number, the client compares it with the local message number. If the value is smaller than the local value, the message is received and is ignored. If it is larger than the local message number, the local message number is used to pull the message list that is larger than the local message number to the server, that is, the incremental message list. After the pull is complete, the largest message number in the update message list is the new local message number.
  • 3) When the server receives the incremental message list pulled by the client, it records the number of the request to the database, so as to know the local number of the client’s latest message at this time;
  • 4) Considering that the server pushes the message number to the client, there will be some loss, so the client will regularly pull the message list from the server every N seconds that is larger than the local message number.

This approach, known as a push-pull scheme in business, is often used to achieve real-time data synchronization in distributed message queues, configuration centers, and registries.

Moreover, in this case, the client and the server do not necessarily need to use the long connection, but can use the long polling instead.

For example, the client sends an HTTP request to the server with the message version number:

  • 1) If the server has a new message number than the client, the incremental message list is directly returned;
  • 2) If the server does not have a new message number than the client, it holds the request until a new message list can be returned, or the HTTP request times out;
  • 3) When the client receives the HTTP request timeout, it immediately resends the HTTP request with the message version number to the server. In this way, the message number is used as the incremental identifier to obtain the message in real time.

If you are interested in reliable delivery of information, check out the following:

  • Introduction to Zero-based IM Development iii: What is RELIABILITY for IM Systems?
  • Mobile IM Message Reliability and Delivery from the Client’s Perspective
  • IM Message Delivery Guarantee Mechanism (I) : Ensure the reliable delivery of online Real-time Messages
  • Implementation of IM Message Delivery Guarantee Mechanism (2) : Ensure reliable Delivery of Offline Messages
  • IM group chat messages are so complicated, how to ensure not to lose weight?
  • IM Development Dry goods Sharing: How to gracefully deliver a Large number of Offline Messages reliably
  • A set of IM Architecture technology for 100 million users (Part II) : Reliability, Order, weak network Optimization, etc.

After all, this is a little sketchy

Finally: If you want to learn more about IM development systematically, it’s recommended to read: Getting Started is Enough: Developing Mobile IM from Scratch. If you think you’ve hit the ground running, check out from Novice to Expert: How to Design a Distributed IM System for Billions of Messages in a production environment.

Limited by space, I will not continue here.

Appendix: More hands-on IM development articles

“Self development IM is so difficult? Hand to hand teach you self a Simple Andriod VERSION IM (with source code)” “an Android END IM intelligent heartbeat algorithm design and implementation discussion (including sample code)” hand to hand teach you to use Netty network communication program heartbeat mechanism, disconnect reconnect mechanism” “[lightweight instant messaging framework MobileIMSDK iOS source code (open source edition) [attachment download]] (http://www.52im.net/thread-35…” “[open source IM project” mushroom street TeamTalk “may not abridged version before 2015 complete code [attachment download]] (http://www.52im.net/thread-77…” “[introduction to the NIO framework (a) : the server based on UDP Netty4 two-way communication Demo presentation [attachment download]] (http://www.52im.net/thread-36…” “[introduction to the NIO framework (2) : the server based on UDP MINA2 two-way communication Demo presentation [attachment download]] (http://www.52im.net/thread-37…” “[introduction to the NIO framework (3) : iOS and MINA2 Netty4 cross-platform UDP two-way communication of actual combat [attachment download]] (http://www.52im.net/thread-37…” “[introduction to the NIO framework (4) : Android and MINA2 Netty4 cross-platform UDP two-way communication of actual combat [attachment download]] (http://www.52im.net/thread-38…” “[a WebSocket real-time chat Demo: based on the node. Js + socket. IO [attachment download]] (http://www.52im.net/thread-51…” “Suitable for beginners: from zero to develop an IM server (based on Netty, complete source code)” “pick up the keyboard is dry: with me to develop a distributed IM system” “correct understanding of IM long connection heartbeat and reconnection mechanism, and start to achieve (complete IM source code)” “suitable for beginners: Learn with the source code: Learn with the Source code: Build Web IM Chat with WebSocket

This post has been posted on the “Im Technosphere” official account.



▲ The link to this article on the official account is:Click here to enter. The synchronous publish link is:http://www.52im.net/thread-3483-1-1.html