Learn along with the source code: hand in hand to teach you how to create web IM chat with websocket

Time:2021-4-21

The author of this article, taro, originally titled “Introduction to taro spring boot websocket”, has been revised and changed this time.

1、 Introduction

Websocket is now widely used in the application of instant messaging technology on the web. It is not only used in traditional PC web pages, but also used in HTML5 based hybrid apps by many mobile developers. To add IM, push and other real-time communication functions in web-based applications, websocket is almost a necessary technology.

Learn along with the source code: hand in hand to teach you how to create web IM chat with websocket

This paper will implement an entry-level IM application with simple logic based on Tomcat and spring framework. For instant messaging beginners, it is more significant to find a simple, direct and smooth example code, which is exactly the case in this paper. Hope to inspire your IM development and learning.

Note:The source code can be downloaded from the attachment at the beginning of the fourth and fifth sections of this article.

exchange of learning:

(this article is published at:http://www.52im.net/thread-3483-1-1.html

2、 Knowledge preparation

If you are confused about the knowledge of instant messaging on the web, be sure to read:《Beginner’s post: the most complete web instant messaging technology in history》、《Survey of instant messaging technology on Web: short polling, comet, websocket, SSE》。

Due to space limitation, this article will not go into the theory of websocket technology

If you want to be more hard core, you can read the following articles:

3、 Content overview

Compared with HTTP protocol, websocket protocol is unfamiliar to most back-end developers.

Relatively speaking:Websocket protocol focuses on providing the ability for the server to actively send data to the client, so that we can complete the high real-time requirements. For example: chat IM communication function, message subscription service, web games and so on.

At the same time:Because websocket uses TCP communication, it can avoid repeated connection creation and improve communication quality and efficiency. For example: meituan’s long connection service, you can see the details《Meituan’s mobile network optimization practice: greatly improve the connection success rate, speed, etc》 。

Tips:

There is a mistake here. Websocket, compared with ordinary socket, only uses HTTP protocol to complete handshake and create connection. All subsequent communications have nothing to do with HTTP protocol.

Seeing this, you must think that the concept of websocket will start again. Ha ha, I don’t want to read this article if you can’t help it“2. Knowledge preparation”This chapter.

To use websocket, there are several solutions as follows:

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

The main reasons are as followsWe are relatively familiar with the actual combat, principle and source code of netty framework, so we considered it. Besides supporting websocket protocol, we also want to provide native socket protocol.

If we only provide websocket protocol support, we can consider scheme 1 or scheme 2. In use, the two schemes are relatively close. In contrast, spring websocket has built-inSTOMPProtocol support.

however:In this paper, the second scheme “Tomcat websocket” is used as an example. Cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough, cough. I’m a little lazy and I don’t want to change it. If I can do it again, I’ll choose Li Bai, ha ha ha ha~

Of course, don’t panic. There is no difference between the implementation code of scheme 1 and scheme 2.

Before we start building the Tomcat websocket introductory example, let’s take a look at it  JSR-356The specification defines the Java API for websocket, namelyJavax WebSocket 。 The specification is the big brother, and will not provide implementation, so JSR – 356 is the same. At present, the mainstream web containers have provided JSR – 356, such as tomcat, jetty, undertow and so on.

4、 Introduction to Tomcat websocket

4.1 basic introduction

Sample code download:

(because the attachment can’t be uploaded here, please download it from the sync link:http://www.52im.net/thread-3483-1-1.html

The contents of the code directory are as follows: 

Learn along with the source code: hand in hand to teach you how to create web IM chat with websocket

In this section, we will use Tomcat websocket to build an example of websocket.

The following messages are supported:

  • 1) Authentication request;
  • 2) Private chat news;
  • 3) Group chat news.

In order to make the example easier to understand, let’s create a chat room with only one large chat room in the whole world, that is, the connection to websocket will automatically enter the chat room.

Now, let’s take a tour of websocket …

4.2 introduction of dependency

In pom.xml File, the introduction of related dependencies.

<?xml version=”1.0″encoding=”UTF-8″?>
<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>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>lab-25-01</artifactId>
    <dependencies>
        <!– The introduction of websocket dependency is convenient ~ –>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <!– Fastjson is introduced to realize the serialization of JSON, because we will use it to parse messages later –>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
    </dependencies>
</project>
For the role of each dependency, take a look at the notes carefully.

4.3、WebsocketServerEndpoint

In cn.iocoder.springboot .lab25. springwebsocket.websocket Under the package path, the websocketserverendpoint class is created to define the endpoint of the websocket service.

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); / / set to debug level in production environment
    }
    @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);
    }
}

As shown in the code:

  • 1) Add @ controller annotation to the class to ensure the creation of a websocketserverendpoint bean;
  • 2) On the class, add JSR – 356 defined  @ServerEndpointThis is a websocket endpoint with a path of /;
  • 3) There are four events in websocket, which use JSR respectively – 356 defined  @OnOpen@OnMessage@OnClose@OnErrorNotes.

This is the simplest version of websocketserverendpoint code. In the following, we will gradually complete the code.

4.4、WebSocketConfiguration

On cn . iocoder . springboot . lab24 . springwebsocket . Create websocketserverendpoint configuration class under config package path.

The code is as follows:

// WebSocketConfiguration.java
@Configuration
//@ enable websocket / / 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 () Method, create a serverendpoint exporter bean. The function of this bean is to scan and add @ Bean of serverendpoint annotation.

4.5、Application

establish Application.java Class, 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 or are not willing to write front-end code, we use it directlyWebsocket online testing toolTo test the websocket connection.

As shown in the figure below:

Learn along with the source code: hand in hand to teach you how to create web IM chat with websocket

So far, we have built the skeleton of the simplest websocket project. Next, we start to reform and complete the corresponding logic.

4.6 news

In the HTTP protocol, it is based on the synchronous model of request / response to interact. In websocket protocol, it is based on the asynchronous model of message to interact. This is very different. When you see the specific message class, you will feel more obvious.

Because websocket protocol, unlike HTTP protocol, has a URI to distinguish different API request operations, we need to add a message that can identify the message type in the message of websocket. Here we use the type field.

So in this example, the message we use is encoded in JSON format.

The format is as follows:

{
Type: ‘/ / message type
Body: {} / / message body
}

Explain:

  • 1) Type field, message type. Through this field, we know which message handler to use (for message handler, we will analyze it in detail in the next section);
  • 2) Body field, message body. Different message types have different message bodies;
  • 3) Message is encoded in JSON format, mainly considering convenience. In actual projects, it can also be consideredProtobufAnd other more efficient and traffic saving coding formats.

actually:In this example, we can’t think of the name of the message related interface and class corresponding to the body field. All the messages, we put them in the cn.iocoder.springboot .lab25. springwebsocket.message Package path.

4.6.1 Message

Create message interface, basic message body, all message bodies should implement this interface.

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 to authenticate the user request.

The code is as follows:

// AuthRequest.java
public class AuthRequest implements Message {
    public static final String TYPE = “AUTH_REQUEST”;
    /**
* authentication token
     */
    private String accessToken;
/ /… Omit the set / get method
}

Explain:

  • 1) Type static property, message type is auth_ REQUEST 。
  • 2) Accesstoken property, authentication token.

For point 2), in websocket protocol, we also need to authenticate the current connection and the user’s identity. In general, we use the user to call the HTTP login interface and return the accesstoken after successful login. Here, let’s not expand the lecture first, but look at it later《Websocket connection based on token authentication》Article.

Although the websocket protocol is based on the message model for interaction. However, this does not mean that its operation does not require a response result. 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
}

Explain:

  • 1) Type static property, message type is auth_ REQUEST ;
  • 2) Code attribute, response status code;
  • 3) Message property in response to the prompt.

For point 1), in fact, we add the type static attribute to each message implementation class as the message type. Next, we will not repeat it.

In this example, after the user successfully authenticates, the notification message of the user joining the group chat will be broadcast, and the user joinnoticerequest will be used.

The code is as follows:

// UserJoinNoticeRequest.java
public class UserJoinNoticeRequest implements Message {
    public static final String TYPE = “USER_JOIN_NOTICE_REQUEST”;
    /**
* nickname
     */
    private String nickname;
    // … Omit set / Get method
}

In fact, we can expand message where we need to use the request / response model

  • 1) Request abstract class, adding the requestid field to indicate the request number;
  • 2) Response abstract class, add the requestid field, and map each request (at the same time, it defines the code and message attributes to represent the response status code and response prompt).

In this way, in the business scenario of using the synchronization model, the message implementation class uses request / Reponse as a suffix. For example, user authentication request, delete a friend request and so on.

However, in the business scenario where the asynchronous model can be used, the message implementation class will continue to use message as the suffix. For example, after sending a message, the user does not need to block and wait for the result

4.6.3 sending message

Create the sendtoonerequest class to send the message of the private chat message to the designated person.

The code is as follows:

// SendToOneRequest.java
public class SendToOneRequest implements Message {
    public static final String TYPE = “SEND_TO_ONE_REQUEST”;
    /**
* users sent to
     */
    private String toUser;
    /**
     * Message number
     */
    private String msgId;
    /**
* content
     */
    private String content;
/ /… Omit the set / get method
}

For each field, look at the comments.

Create the sendtoallrequest class to send the group chat message to everyone.

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
}

For each field, look at the comments.

When the server receives a request to send a message, it needs to respond asynchronously to see if the message is sent successfully. Therefore, create the sendresponse class to send the message of the message response 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 sending a message, the client generates a globally unique message number by using UUID algorithm (for the generation technology of unique ID, see:《From novice to expert: how to design a distributed IM system with 100 million messages》”Of”_ 5. Technical solution of unique ID Chapter). In this way, the server responds through the sendresponse message and maps through msgid.

When the server receives the request to send a message, it needs to forward the message to the corresponding person. Therefore, 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
}

Compared with sendtoonerequest, there is one less touser field. Because we can connect through websocket and already know who to send it to.

4.7 message processor

For the message type initiated by each client, we will declare the corresponding message handler. This is similar to that in spring MVC, each API interface corresponds to a controller method.

All the message handlers, we put them in the cn.iocoder.springboot .lab25. springwebsocket.handler Package path.

4.7.1 MessageHandler

Create message handler interface and message processor interface.

The code is as follows:

// MessageHandler.java
public interface MessageHandler<T extends Message> {
    /**
* execution processing message
     *
* @ param session
* @ param message
     */
    void execute(Session session, T message);
    /**
     * @ Return message type, that is, the type static field on each message implementation class
     */
    String getType();
}

Explain:

  • 1) The generic < T > is defined, which needs to be the implementation class of message;
  • 2) Define the two interface methods, their own look at the comments ha.

4.7.2 AuthMessageHandler

Create the authmessage handler 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 the accesstoken is not passed
        if(StringUtils.isEmpty(message.getAccessToken())) {
            WebSocketUtil.send(session, AuthResponse.TYPE,
                     new AuthResponse (). setCode ( one ). setMessage (” Authentication accesstoken not passed in “));
            return;
        }
/ / add to websocketutil
         WebSocketUtil.addSession (session, message.getAccessToken ()); / / to simplify the code, let’s use accesstoken as user first
/ / judge whether the authentication is successful. Here, pretend direct success
        WebSocketUtil.send(session, AuthResponse.TYPE,newAuthResponse().setCode(0));
/ / inform everyone that someone has joined. This is optional logic, just for demonstration
        WebSocketUtil.broadcast(UserJoinNoticeRequest.TYPE,
                newUserJoinNoticeRequest().setNickname( message.getAccessToken ()); / / to simplify the code, let’s use accesstoken as user first
    }
    @Override
    public String getType() {
        return AuthRequest.TYPE;
    }
}

The code is relatively simple, just follow the code.

For the websocketutil class, let’s look at it in detail in the 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 a 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, just follow the code.

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 a 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, just follow the code.

4.8、WebSocketUtil

Code in cn.iocoder.springboot .lab25. springwebsocket.util Package path.

Creating websocketutil tool class mainly provides two functions:

  • 1) Session management;
  • 2) There are many ways to send messages.

The overall code is relatively simple, see for yourself.

The location of the code in the directory is as follows:

Learn along with the source code: hand in hand to teach you how to create web IM chat with websocket

4.9 improve websocketserverendpoint

In this section, we will modify the code of websocketserverendpoint to improve its function.

4.9.1 initialize the message handler collection

realization  InitializingBeanInterface. In the # afterpropertieset() method, scan all the message handler beans and add them to the message handler collection.

The code is as follows:

// WebsocketServerEndpoint.java
/**
* mapping between message type and message handler
 *
* note that this is set to a static variable. Although websocketserverendpoint is a singleton, spring boot will create 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 message handler beans through ApplicationContext
     applicationContext.getBeansOfType ( MessageHandler.class ). values() / / get all message handlers Bean.forEach (messageHandler -> HANDLERS.put ( messageHandler.getType (), message handler)); / / add to handlers
    logger.info(“afterPropertiesSet”, HANDLERS.size());
}

In this way, you can avoid manually configuring the mapping between message handler and message type.

4.9.2 onOpen

Reimplement#onOpen(Session session, EndpointConfig config)Method, when the connection is realized, the accesstoken parameter is used for user authentication.

The code is as follows:

// WebsocketServerEndpoint.java
@OnOpen
public void onOpen(Session session, EndpointConfig config) {
    logger.info(“onOpen”, session);
/ / < 1 > parsing accesstoken
    List<String> accessTokenValues = session.getRequestParameterMap().get(“accessToken”);
    String accessToken = !CollectionUtils.isEmpty(accessTokenValues) ? accessTokenValues.get(0) : null;
/ / < 2 > create authrequest message type
    AuthRequest authRequest = newAuthRequest().setAccessToken(accessToken);
/ / < 3 > get message processor
    MessageHandler<AuthRequest> messageHandler = HANDLERS.get(AuthRequest.TYPE);
    if(messageHandler == null) {
        logger.error(“onOpen”);
        return;
    }
    messageHandler.execute(session, authRequest);
}

As shown in the code:

  • <1> Address: resolve the request parameter of accesstoken on WS: / /. For example: WS: / / 127.0.0.1:8080? Accesstoken = 999999;
  • <2> Where: create the authrequest message type and set the accesstoken property;
  • < three > Location: get the MessageHandler message processor corresponding to the AuthRequest message type, and then call MessageHandler. # execute ( session , message ) Method to execute and process the user authentication request.

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

  • 1) Ws: / / 127.0.0.1:8080 /? Accesstoken = taro;
  • 2) Ws: / / 127.0.0.1:8080 /? Accesstoken = tomato;
  • 3) Ws: / / 127.0.0.1:8080 /? Accesstoken = potato.

Then, click the “open connection” button one by one to connect to websocket.

The final effect is as follows:

Learn along with the source code: hand in hand to teach you how to create web IM chat with websocket

As shown in the figure above:

  • 1) In the red circle, you can see the message of authresponse;
  • 2) In the yellow circle, you can see the message of userjoinnoticerequest.

4.9.3 onMessage

Reimplement_ #onMessage(Session session, String message)_ Method to implement different messages and forward them to different message handler message processors.

The code is as follows:

// WebsocketServerEndpoint.java
@OnMessage
public void onMessage(Session session, String message) {
     logger.info (“onopen”, session, message); / / set to debug level in production environment
    try{
/ / < 1 > get message type
        JSONObject jsonMessage = JSON.parseObject(message);
        String messageType = jsonMessage.getString(“type”);
        // < two > Get message processor
        MessageHandler messageHandler = HANDLERS.get(messageType);
        if(messageHandler == null) {
            logger.error(“onMessage”, messageType);
            return;
        }
/ / < 3 > parsing messages
        Class<? extendsMessage> messageClass = this.getMessageClass(messageHandler);
        // < four > Processing messages
        Message messageObj = JSON.parseObject(jsonMessage.getString(“body”), messageClass);
        messageHandler.execute(session, messageObj);
    } catch(Throwable throwable) {
        logger.info(“onMessage”, session, throwable);
    }
}

In the code:

  • < one > Get the message type from ” type ” Field;
  • < two > The message handler message processor corresponding to the message type is obtained;
  • <3> Then, call the # GetMessage class (message handler handler) method to get the class corresponding to the message type by parsing the generics on the class in the message handler.

The code is as follows:

// WebsocketServerEndpoint.java
private Class<? extends Message> getMessageClass(MessageHandler handler) {
    // Get the class name corresponding to the bean. Because it may have been represented by AOP.
    Class<?> targetClass = AopProxyUtils.ultimateTargetClass(handler);
/ / get the type array of the interface
    Type[] interfaces = targetClass.getGenericInterfaces();
    Class<?> superclass = targetClass.getSuperclass();
    while(( Objects.isNull (interfaces) || 0== interfaces.length ) && Objects.nonNull (superclass)) {/ / here, the interface of the parent class takes precedence
        interfaces = superclass.getGenericInterfaces();
        superclass = targetClass.getSuperclass();
    }
    if(Objects.nonNull(interfaces)) {
/ / traverse the interfaces array
        for(Type type : interfaces) {
            // Type is required to be a generic parameter
            if(type instanceof ParameterizedType) {
                ParameterizedType parameterizedType = (ParameterizedType) type;
/ / the message handler 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) cannot get message type”, handler));
                    }
                }
            }
        }
    }
    throw new IllegalStateException( String.format (“type (% s) cannot get message type”, handler));
}

This is slightly modified by referring to the defaultrocketmqlistenercontainer? GetMessage type() method of rocketmq spring project.

If you don’t know anything about Java’s generic mechanism, it may be a bit hard core. You can skip it for a while and know the intention.

<4>  Call the message handler # execute ( session , message ) Method to execute the processing request.

In addition:Try catch code is added here to avoid exception in the whole execution process. If an exception occurs during the processing of the onmessage event, the session corresponding to the message will be automatically closed. Obviously, this does not meet our requirements. For example, in the process of message handler processing messages, some exceptions are unavoidable.

To continue with the three browsers created above, we first click the “clear message” button to clear the next message and clean up the received message displayed in the last test. Of course, websocket doesn’t need to be disconnected.

In the first browser, two chat messages are sent respectively.

A private message of sendtoonerequest:

{
    type: “SEND_TO_ONE_REQUEST”,
    body: {
Touser: “tomato”,
        msgId: “eaef4a3c-35dd-46ee-b548-f9c4eb6396fe”,
Content: “I am 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 chat message “
    }
}

The final results are as follows:

Learn along with the source code: hand in hand to teach you how to create web IM chat with websocket

As shown in the figure above:

  • 1) In the red circle, you can see a send to user request message. Only the second browser (tomato) receives it;
  • 2) In the yellow circle, you can see three send to user request messages, which are received by all browsers.

4.9.4 onClose

Reimplement_ #onClose(Session session, CloseReason closeReason)_ Method to remove the closed session.

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)Method, remain unchanged.

The code is as follows:

// WebsocketServerEndpoint.java
@OnError
public void onError(Session session, Throwable throwable) {
    logger.info(“onClose”, session, throwable);
}

5、 Introduction to spring websocket

5.0 basic introduction

Sample code download:

(because the attachment can’t be uploaded here, please download it from the sync link:http://www.52im.net/thread-3483-1-1.html

If you are careful, you will be shocked by the tiger’s body, or you can provide an example of a quick start to spring websocket.

In the previous chapter “Tomcat websocket practical introduction”_ lab-websocket-25-01_ On the basis of the example, we copy the lab-websocket-25-02 project for transformation.

The contents of the code directory are as follows:

Learn along with the source code: hand in hand to teach you how to create web IM chat with websocket

5.1、WebSocketUtil

Because Tomcat websocket uses session as a session and spring websocket uses websocketsession as a session, we need to slightly modify the websocketutil tool class. The change is very slight. Click WebSocketUtil.java Check it out. I understand.

There are two main points

  • 1) Adjust all the places using session class to websocketsession class;
  • 2) Change the sending message from session to websocketsession.

5.2 message processor

take  _ cn . iocoder . springboot . lab25 . springwebsocket . handler_   The message processors in the packet path use the session class and adjust it to the websocketsession class.

5.3、DemoWebSocketShakeInterceptor

stay  _ cn . iocoder . springboot . lab25 . springwebsocket . websocket_   Under the package path, create the demowebsocketshakeinterceptor interceptor. Because websocketsession can’t get the request parameters on the WS address, we have to get the accesstoken request parameters through the interceptor and set them into attributes.

The code is as follows:

// DemoWebSocketShakeInterceptor.java
public class DemoWebSocketShakeInterceptor extends HttpSessionHandshakeInterceptor {
@ override / / intercept handshake event
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,WebSocketHandler wsHandler, Map<String, Object> attributes) throwsException {
/ / get 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_ Under the package path, create the demowebsockethandler processor. The processor refers to the5.9 improve websocketserverendpointAnd write its code.

DemoWebSocketHandler.java The code is located in the following directory, and the specific content will not be pasted out. Read it by yourself:

Learn along with the source code: hand in hand to teach you how to create web IM chat with websocket

The code is very similar, just roll it down.

5.5、WebSocketConfiguration

Modify the websocketconfiguration configuration class. The code is as follows:

// WebSocketConfiguration.java
@Configuration
@Enable websocket / / start spring websocket
public class WebSocketConfiguration implements WebSocketConfigurer {
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
         registry.addHandler ( this.webSocketHandler (), “/”) / / configure processor
. addinterceptors (newdemowebsocketshakeinterceptor()) / / configure interceptors
. setallowedorigins (“*”); / / solve cross domain problems
    }
    @Bean
    public DemoWebSocketHandler webSocketHandler() {
        return new DemoWebSocketHandler();
    }
    @Bean
    public DemoWebSocketShakeInterceptor webSocketShakeInterceptor() {
        return new DemoWebSocketShakeInterceptor();
    }
}

Explain:

  • 1) On the class, add the @ enablewebsocket annotation to enable the spring websocket function;
  • 2) Implement the websocketconfigurator interface and customize the configuration of websocket (for details, see the # registerwebsockethandlers (Registry) method, configure the websocket processor, interceptor, and allow cross domain).

So far, we have completed the spring websocket example.

Later, we execute application to start the project. Specific tests, here is not repeated, you can useWebsocket online testing toolLet’s test it.

7、 Write at the end

Although the websocket protocol has been well supported in mainstream browsers, there are always some “different” and incompatible. So sockjs was born Socket.io This kind of library. About their introduction and use, you can see《Brief introduction to sockjs》 、《The development of Web instant messaging technology and websocket Socket.io The technical practice of the project》Article.

In actual scenarios, whether we use websocket or native socket, we need to consider “how to ensure that messages are delivered to users?”

What you can certainly think of is: if the user is not online, the message will be persisted to MySQL, mongodb and other databases. This is right and must be done.

Let’s consider the lower boundary scenario together: the client’s network environment is poor, especially in the mobile scenario, when the network is disconnected, the connection may actually be disconnected, and the server thinks that the client is online. At this time, the server will send the message to the client, then the message is actually sent to the “air”, resulting in loss.

To solve this problem, we need to introduce the ACK message mechanism of the client.

At present, there are two mainstream approaches.

The first one is the first oneACK based on each message number

The overall process is as follows:

  • 1) No matter whether the client is online or not, the server will persist the received message to the database first. If the client is online, the server will push the complete message to the client;
  • 2) After receiving the message, the client sends the ACK message number to the server to inform it that the message has been received. When the server receives the ACK message number, it marks that the message has been sent successfully;
  • 3) The server polling regularly, the online client, whether there are more than N seconds without ACK message. If yes, resend the message to the corresponding client.

In this scheme, because the client number the ACK messages one by one, it will lead to too many interactions between the client and the server. Of course, the client can asynchronously batch ack multiple messages, thus reducing the number of times.

However, because the server still needs regular polling, it will also lead to greater pressure on the server. Therefore, this kind of scheme has been basically not adopted.

Second:Based on sliding window ACK

The overall process is as follows:

  • 1) No matter whether the client is online or not, the server will persist the received message to the database first. If the client is online, the server will push the message number to the client;
  • 2) After receiving the message number, the client compares it with the local message number. If it is smaller than the local one, it means that the message has been received and will not be processed; if it is larger than the local one, it uses the local message number to pull the incremental message list, which is larger than the local message number, from the server. After the pull is completed, 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 request number in the database to know the latest local message number of the client at this time;
  • 4) Considering that the server will push the message number to the client, there will be loss, so the client will pull the message list larger than the local message number from the server every N seconds.

This method, known as push-pull combination in business, is often used to achieve real-time data synchronization in distributed message queue, configuration center and registration center.

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

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

  • 1) If the server has a new message number than the client, the incremental message list will be returned directly;
  • 2) If the server does not have a new message number than the client, hold 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 relaunches the HTTP request with the message version number to the server. In this way, the message number is used as the incremental identification to obtain the message in real time.

If you are interested in reliable message delivery, you can take a look at the following articles:

After all, this article is a bit brief ~

last:If you want to systematically learn the knowledge of IM development, it is recommended to read:《A novice is enough: developing mobile IM from scratch》。 If you think you are a little bit of a maverick, you can take a look at the knowledge of IM system architecture design with large number of users in the production environment《From novice to expert: how to design a distributed IM system with 100 million messages》。

Limited by space, it will not continue here.

Appendix: more practical articles on IM development

Is it that difficult to develop Im by yourself? Hand in hand to teach you how to make a simple Android im ( Source code )
Design and implementation of an Android terminal im intelligent heartbeat algorithm (including sample code)
Hand in hand teach you how to use netty to realize the heartbeat mechanism and disconnection reconnection mechanism of network communication program
[IOS source code of mobile IM SDK (open source version) [attachment Download]](http://www.52im.net/thread-35…
《 [ Open source im project “mushroom street teamtalk” before May 2015 [ Attachment Download ]](http://www.52im.net/thread-77…
Introduction to NiO framework (1): demo demo of UDP two way communication based on netty4 on server [attachment Download]](http://www.52im.net/thread-36…
Introduction to NiO framework (2): demo demo of UDP two way communication based on mina2 on server [download attachment]](http://www.52im.net/thread-37…
《 [ Introduction to NiO framework ( three ) Cross platform UDP two way communication between IOS, mina2 and netty4 [ Attachment Download ]](http://www.52im.net/thread-37…
[introduction to NiO framework (4): cross platform UDP two way communication between Android and mina2, netty4 [attachment Download]](http://www.52im.net/thread-38…
[a websocket real time chat Demo: Based on node.js + socket.io [attachment Download]](http://www.52im.net/thread-51…
Suitable for novices: develop an IM server from scratch (based on netty, with complete source code)
Pick up the keyboard and do it: develop a distributed IM system with my bare hands
Correctly understand the heartbeat and reconnection mechanism of IM long connection, and implement it by hand (with complete im source code)
Suitable for novices: hand in hand to teach you how to quickly build a high-performance and scalable IM system with go (with source code)
Learn along with the source code: hand in hand to teach you how to create web IM chat with websocket

The official account of the “instant messaging technology circle” has been released.
Learn along with the source code: hand in hand to teach you how to create web IM chat with websocket
The link on the official account is:Click here to enter. The synchronous publishing link is:http://www.52im.net/thread-3483-1-1.html

Recommended Today

Review of SQL Sever basic command

catalogue preface Installation of virtual machine Commands and operations Basic command syntax Case sensitive SQL keyword and function name Column and Index Names alias Too long to see? Space Database connection Connection of SSMS Connection of command line Database operation establish delete constraint integrity constraint Common constraints NOT NULL UNIQUE PRIMARY KEY FOREIGN KEY DEFAULT […]