16. WebSocket chat room

Based on SpringBoot 2.6.11

1.WebSocket

WebSocket is a protocol provided by HTML5 for full-duplex communication on a single TCP connection, which can be used directly on html pages.

WebSocket makes the data exchange between client and server easier, allowing the server to actively push data to the client. In the WebSocket API, the browser and the server only need to complete a handshake, and a persistent connection can be created directly between the two, and two-way data transmission can be performed.

In the WebSocket API, the browser and the server only need to do a handshake, and then a fast channel is formed between the browser and the server. Data can be directly transmitted between the two.

In the past, many websites used Ajax polling to implement push technology. Polling is at a specific time interval (such as every 1 second), the browser sends an HTTP request to the server, and then the server returns the latest data to the client's browser. This traditional mode brings obvious disadvantages, that is, the browser needs to continuously send requests to the server, but the HTTP request may contain a long header, and the real valid data may be only a small part, which is obviously a waste A lot of bandwidth and other resources.

The WebSocket protocol defined by HTML5 can better save server resources and bandwidth, and enable more real-time communication.

image-20220409085906297

The browser sends a request to the server to establish a WebSocket connection through JavaScript. After the connection is established, the client and the server can directly exchange data through the TCP connection.

After obtaining the WebSocket connection, you can send data to the server through the send() method, and receive the data returned by the server through the onmessage event.

WebSocket no longer uses http protocol during transmission, but Stomp protocol

1.1 STOMP

STOMP is Simple (or Streaming) Text Orientated Messaging Protocol, which provides an interoperable connection format that allows STOMP clients to interact with any STOMP message broker (Broker). The STOMP protocol is widely used in multiple languages ​​and platforms due to its simple design and easy development of clients.

The predecessor of the STOMP protocol is the TTMP protocol (a simple text-based protocol), which is designed for message middleware.

STOMP is a very simple and easy-to-implement protocol whose design was inspired by the simplicity of HTTP. Although the implementation of the STOMP protocol on the server side may be difficult, the implementation on the client side is very easy. For example, you can use Telnet to log in to any STOMP agent and interact with the STOMP agent.

1.2 WebSocket events

Following are the related events of WebSocket object.

event event handler describe
open Socket.onopen Triggered when a connection is established
message Socket.onmessage Triggered when the client receives data from the server
error Socket.onerror Triggered when a communication error occurs
close Socket.onclose Fired when the connection is closed

1.3 WebSocket method

The following are the relevant methods of the WebSocket object.

method describe
Socket.send() Send data using the connection
Socket.close() close connection

2. Use WebSocket to realize the chat room on the web page

        import dependencies

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

        Create a configuration class

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

/**
 * @author: Elivs.Xiang
 * @Email: [email protected]
 * @create 2022-12-06 15:09
 * @verson IDEA 2020.3
 */

@Configuration
public class WebsocketConfig {
    @Bean    //在容器中创建bean对象,在WebSocketUtil中需要用到的RemoteEndpoint对象
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

        Create tool class

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.websocket.Session;
import javax.websocket.RemoteEndpoint;
public class WebSocketUtil {
	//HashMap:不支持多线程,并发情况下线程不安全
	public static final Map<String, Session> MESSAGEMAP = new ConcurrentHashMap<>();
	
	//发送消息给客户端
	public static void sendMessage(Session session,String message) {
		if (session!=null) {
			final RemoteEndpoint.Basic basic = session.getBasicRemote();
			if (basic!=null) {
				try {
					basic.sendText(message);//发送消息回客户端
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	//将消息给所有聊天室的人
	//循环发送
	public static void sendMessageToAll(String message) {
		MESSAGEMAP.forEach((sessionId,session)->sendMessage(session, message));
	}
}

        create handler

import java.io.IOException;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

import com.example.web.util.WebSocketUtil;
import org.springframework.web.bind.annotation.RestController;

@RestController
@ServerEndpoint("/WebSocketHandler/{userName}")		//表示接受的是STOMP协议提交的数据
public class WebSocketHandler {
	
	//建立连接
	@OnOpen
	public void openSession(@PathParam("userName")String userName,Session session) {
		//消息
		String message = "欢迎:"+userName+"加入群聊";
		//加入聊天室
		WebSocketUtil.MESSAGEMAP.put(userName, session);
		//发送消息
		WebSocketUtil.sendMessageToAll(message);
	}
	
	@OnMessage
	public void onMessage(@PathParam("userName")String userName,String message) {
		message = userName+":"+message;
		WebSocketUtil.sendMessageToAll(message);
	}
	
	//离开聊天室
	@OnClose
	public void onClose(@PathParam("userName")String userName,Session session) {
		//将当前用户从map中移除 注销
		WebSocketUtil.MESSAGEMAP.remove(userName);
		//群发消息
		WebSocketUtil.sendMessageToAll("用户:"+userName+"离开聊天室");
		//关闭session
		try {
			session.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	//连接异常
	@OnError
	public void onError(Session session,Throwable throwable) {
		try {
			session.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

        The main startup class opens websocket

@SpringBootApplication
@EnableWebSocket
public class WebApplication {
    public static void main(String[] args) {
        SpringApplication.run(WebApplication.class, args);
    }
}

        front-end code

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    <link rel="stylesheet" type="text/css" href="css/index.css">
    <script type="text/javascript" src="js/jquery.min.js"></script>
    <script type="text/javascript">
        $(document).ready(function(){
            let url = "ws://192.168.7.131:9988/WebSocketHandler/";
            let ws = null;
            //连接服务器
            $("#join").click(function(){
                let userName = $("#userName").val();
                let newUrl = url + userName;	//传递用户名
                console.info(newUrl);
                //创建对象,连接服务器
                ws = new WebSocket(newUrl); 	//html5中提供了

                //给open事件绑定方法
                ws.onopen = function(){
                    console.info("连接成功");
                }
                //接收到数据
                ws.onmessage = function(result){
                    var textarea = document.getElementById('textarea');
                    textarea.append(result.data+"\n");
                    //将文本域的滚动条滚动到最后
                    textarea.scrollTop = textarea.scrollHeight;
                }
                //关闭连接
                ws.onclose = function(){
                    $("#textarea").append("用户:"+userName+"离开聊天室"+"\n");
                    console.info("关闭连接");
                }
            });
            //发送消息
            $("#send").click(function(){
                //将输入框中的消息发送给服务器,并且显示到消息框中
                var messageInput = $("#message");
                var message = messageInput.val();
                if(ws!=null){
                    ws.send(message);	//发送消息
                    messageInput.val("");
                }
            });
            //断开连接
            $("#out").click(function(){
                if(ws!=null){
                    ws.close();
                }
            });
        })
    </script>
</head>
<body>
<div id="box">
    <p>蜗牛聊天室</p>
    <textarea rows="10" cols="50" disabled="disabled" id="textarea"></textarea><br>
    <div class="infoBox">
        用户名:<input type="text" id="userName"><br><br>
        <button style="color: green;" id="join">加入聊天室</button>
        &nbsp;&nbsp;&nbsp;&nbsp;
        <button style="color: red;" id="out">离开聊天室</button>
    </div>
    <br><br>
    <div class="infoBox">
        消&nbsp;&nbsp;&nbsp;息:<input type="text" id="message"><br><br>
        <button id="send">发送消息</button>
    </div>
    <br>

</div>
</body>
</html>

        css

#box{
	width: 500px;
	background: pink;
	text-align: center;
}
.infoBox{
	text-align:left;
	position: relative;
	left: 62px;
}
#message{
	width: 322px;
}
#send{
	position:relative;
	left:50px;
	height:30px;
	width: 326px;
}

        Start project error

Caused by: java.lang.IllegalStateException: javax.websocket.server.ServerContainer not available

Add to @SpringBootTest on the test class

webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class WebApplicationTests {
    @Test
    void contextLoads() {
    }
}

Integrate Gateway

Configure websocket routing in application.yml

spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
        - id: chat  #
          uri: "ws://127.0.0.1:8000"   # ws 协议    需要指定IP,通过微服务名字会报503
          predicates:
            - Path=/WebSocketHandler/**

Guess you like

Origin blog.csdn.net/LB_bei/article/details/132589629
Recommended