Java implements a simple webSocket chat demo

1. Dependence

Add pom file dependency


   <!-- websocket-->
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-websocket</artifactId>
     </dependency>
        

2. Configuration preparation

Create a webSocketConfig configuration class, that is, register ServerEndpointExporter. This bean is used to scan classes annotated with @ServerEndpoint and serve as the server.


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * @Author: chuxia0811
 * @Date: 2023/7/9 10:15
 * @Description : webSocketConfig配置类,该bean用于扫描被@ServerEndpoint注解的类
 */
@Configuration
public class WebSocketConfig {
    
    

    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
    
    
        return  new ServerEndpointExporter();
    }
}

3. Writing demo code

Create a class annotated with @ServerEndpoint to serve as a webSocket server and specify the connection uri.
Use the annotation to mark the corresponding method, and start the program.


/**
 * @Author: chuxia0811
 * @Date: 2023/7/9 10:21
 * @Description :
 */
@ServerEndpoint(value = "/chat/{username}")
@Component
@Slf4j
public class SocketServer {
    
    
    Logger log = LoggerFactory.getLogger(getClass());

    // 保存链接的session,key为用户名,value为对应的session名
    private ConcurrentHashMap<String, Session> sessionMap = new ConcurrentHashMap<>();

    /**
     * 创建连接
     * 用于监听建立连接,当有客户端与该服务端点建立连接时,将会自回调该注解标注的方法
     * @param session
     * @param username
     */
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "username") String username) {
    
    
        log.info("用户{}已创建连接", username);
    }


    /**
     * 用于监听客户端向服务端发送消息,当客户端与服务端发送消息时,将会回调该注解标注的方法
     * @param msg
     * @param username
     */
    @OnMessage
    public void onMessage(String msg,@PathParam(value = "username") String username){
    
    
        log.info("用户{}发来消息:{}",username,msg);
    }


    /**
     * 用于监听连接关闭,当客户端与该服务端点断开连接时,将会回调该注解标注的方法
     * @param session
     * @param username
     */
    @OnClose
    public void onClose(Session session,@PathParam(value = "username") String username){
    
    
        log.info("用户{}已关闭连接", username);
    }


    /**
     * 用于监听该连接上的任何错误,当客户端与该服务端点的连接发生任何异常,都将回调该注解标注的方法
     * 注意该方法的参数必选Throwable,可选Sessiion以及0-n个String参数,且String参数需要使用@PathParam注解标注
     * @param throwable
     * @param username
     */
    @OnError
    public void onError(Throwable throwable,@PathParam(value = "username") String username){
    
    
        log.error("用户{}连接发生异常", username);
    }

}

4. Start testing

After the project is started, test whether the websocket is available.
webSocket online test website: Websocket online test link

Insert image description here
The log is as follows:

2023-07-09 10:49:29.185  INFO 16308 --- [nio-8080-exec-5] org.sang.websocket.SocketServer         
 : 用户初夏已创建连接
2023-07-09 10:49:35.672  INFO 16308 --- [nio-8080-exec-6] org.sang.websocket.SocketServer         
 : 用户初夏已创建连接
2023-07-09 10:49:43.470  INFO 16308 --- [nio-8080-exec-7] org.sang.websocket.SocketServer       
   : 用户初夏发来消息:你好,我是初夏~

5. Writing business

After successfully completing the basic websocket connection, you can start editing business logic

1. Build the background message entity class;

/**
 * @Author: chuxia0811
 * @Date: 2023/7/9 10:58
 * @Description :
 */
public class Message {
    
    
    private Integer id;
    private String from;
    private String to;
    private String msg;
    private String date;
    private Integer type;//消息发送的类型,0系统群发,1用户私聊
    private Integer is_read;//消息是否已读,0未读,1已读

    public Integer getId() {
    
    
        return id;
    }

    public void setId(Integer id) {
    
    
        this.id = id;
    }

    public String getFrom() {
    
    
        return from;
    }

    public void setFrom(String from) {
    
    
        this.from = from;
    }

    public String getTo() {
    
    
        return to;
    }

    public void setTo(String to) {
    
    
        this.to = to;
    }

    public String getMsg() {
    
    
        return msg;
    }

    public void setMsg(String msg) {
    
    
        this.msg = msg;
    }

    public String getDate() {
    
    
        return date;
    }

    public void setDate(String date) {
    
    
        this.date = date;
    }

    public Integer getType() {
    
    
        return type;
    }

    public void setType(Integer type) {
    
    
        this.type = type;
    }

    public Integer getIs_read() {
    
    
        return is_read;
    }

    public void setIs_read(Integer is_read) {
    
    
        this.is_read = is_read;
    }
}

2. Transformation method

@ServerEndpoint(value = "/chat/{username}")
@Component
@Slf4j
public class SocketServer {
    
    
    Logger log = LoggerFactory.getLogger(getClass());

    // 保存链接的session,key为用户名,value为对应的session名
    private ConcurrentHashMap<String, Session> sessionMap = new ConcurrentHashMap<>();

    /**
     * 创建连接
     * 用于监听建立连接,当有客户端与该服务端点建立连接时,将会自回调该注解标注的方法
     * @param session
     * @param username
     */
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "username") String username) {
    
    
        log.info("用户{}已创建连接", username);
        sessionMap.put(username,session);
    }


    /**
     * 用于监听客户端向服务端发送消息,当客户端与服务端发送消息时,将会回调该注解标注的方法
     * @param msg
     * @param username
     */
    @OnMessage
    public void onMessage(String msg,@PathParam(value = "username") String username){
    
    
        log.info("用户{}发来消息:{}",username,msg);
        Message message = JSON.parseObject(msg, Message.class);
        //根据message中的to属性获取接收消息的用户的session,利用其session将消息转发过去
        Session toSession = sessionMap.get(message.getTo());
        sendMessage(toSession, message.getMsg());
    }


    /**
     * 用于监听连接关闭,当客户端与该服务端点断开连接时,将会回调该注解标注的方法
     * @param session
     * @param username
     */
    @OnClose
    public void onClose(Session session,@PathParam(value = "username") String username){
    
    
        log.info("用户{}已关闭连接", username);
        sessionMap.remove(username);
    }


    /**
     * 用于监听该连接上的任何错误,当客户端与该服务端点的连接发生任何异常,都将回调该注解标注的方法
     * 注意该方法的参数必选Throwable,可选Sessiion以及0-n个String参数,且String参数需要使用@PathParam注解标注
     * @param throwable
     * @param username
     */
    @OnError
    public void onError(Throwable throwable,@PathParam(value = "username") String username){
    
    
        log.error("用户{}连接发生异常", username);
    }


    /**
     * 用来发送消息的方法,参数分别为接收消息的用户的session,和对应的消息
     */
    private void sendMessage(Session toSession,String msg){
    
    
        try {
    
    
            toSession.getBasicRemote().sendText(msg);
        } catch (IOException e) {
    
    
            throw new RuntimeException(e);
        }
    }


}

3. Create three new windows for websocket online testing and create three chain connections.


2023-07-09 11:34:13.330  INFO 5812 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 20 ms
2023-07-09 11:34:13.372  INFO 5812 --- [nio-8080-exec-1] org.sang.websocket.SocketServer          : 用户初夏1已创建连接
2023-07-09 11:34:15.302  INFO 5812 --- [nio-8080-exec-2] org.sang.websocket.SocketServer          : 用户初夏2已创建连接
2023-07-09 11:34:18.101  INFO 5812 --- [nio-8080-exec-3] org.sang.websocket.SocketServer          : 用户初夏3已创建连接

Test, use Chuxia 2 user to send chat message to Chuxia 3:
Insert image description here

We can see that only Chuxia 3 received the chat message, but Chuxia 1 did not:
Insert image description here
Insert image description here
At this point, we have implemented webSocke technology and implemented the chat function.

Guess you like

Origin blog.csdn.net/m0_37899908/article/details/131620267