Spring4+WebSocket small example

1. Introduction The
    WebSocket protocol defines a new and important capability for web applications: full-duplex bidirectional communication between the client and the server. In short, the Websocket protocol uses http to do the initial handshake, and then uses http to send a protocol upgrade (or protocol change) request to the server. If the server agrees, it will return a 101 status code. If the handshake is successful, the corresponding underlying tcp socket will remain open after the http request is upgraded, and both the server and the client can use it to send messages.
2. Back-up options An important challenge for WebSocket applications is to consider browsers that do not support WebSockets, such as IE starting from IE10 (see http://caniuse.com/websockets
  for specific browser support ). The fallback option refers to being able to emulate the Websocket API in other ways when needed to take advantage of Websocket functionality in browsers that do not support Websocket.   Spring FrameWork is based on the transparent support provided by SockJs protocal, which can be configured without modifying the application code. For the specific content of SockJs, please refer to https://github.com/sockjs/sockjs-protocol . 3. Subprotocols in Websocket


  Websocket defines the structure of the message, but does not specify a specific protocol. Directly using TPCP to convert a byte stream into a message stream is extremely cumbersome, and unlike HTTP in the application layer protocol, for an incoming message, there is not enough information in the WebSocket protocol for the framework or container to know how to route it. it, and how to deal with it. Therefore, Websocket is too low-level and trivial for applications, just like most web applications now use a web framework instead of directly using servlet api.
  For this reason, the Websocket RFC defines subprotocols. During the handshake process, the server and client can use the Sec-WebSocket-Protocol in the request header to agree on a high-level application-level sub-protocol. Although subprotocols are not required, even if they are not, your application needs to define a message format that both the client and server can recognize.
  STOMP - a simple text-oriented messaging protocol is provided in the Spring Framework. Although STOMP is text-oriented, the message content is not limited to text but can also be of binary type. For the specific content of STOMP, please refer to http://stomp.github.io/stomp-specification-1.2.html .
Fourth, when to use Websocket
  The most suitable use of Websocket in web applications is the occasion where the server and the client exchange information with high frequency and low latency. For example, finance, stocks, games, etc. are all industries that are very sensitive to delays and exchange information frequently.
5. Example
This example does not use maven, and adopts the configuration form of xml. 1. There is no difference between
web.xml
and ordinary spring mvc. The function
a. defines a servlet interceptor and Mapping rules
b. Introduce spring-context configuration file
c. Add log4j configuration file
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
    id="WebApp_ID" version="3.1">
    <display-name>SpringTest</display-name>
    <!-- Processes application requests -->
    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
        <async-supported>true</async-supported>
    </servlet>

    <context-param>
        <param-name>log4jConfigLocation</param-name>
        <param-value>/resources/log4j/log4j.properties</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
    </listener>
    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- Disables Servlet Container welcome file handling. Needed for compatibility
        with Servlet 3.0 and Tomcat 7.0 -->
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>
</web-app>

2. DispatcherServlet Context configuration file, functions
a. activate annotation configuration function
b. configure resource file path
c. configure view layer technology
d. introduce specific component configuration document
e. configure websocket related content
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:websocket="http://www.springframework.org/schema/websocket"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/mvc
		http://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/websocket
		http://www.springframework.org/schema/websocket/spring-websocket.xsd
		">

	<!-- DispatcherServlet Context: defines this servlet's request-processing
		infrastructure -->

	<!-- Enables the Spring MVC @Controller programming model -->
	<mvc:annotation-driven/>

	<!-- Handles HTTP GET requests for /resources/** by efficiently serving
		up static resources in the ${webappRoot}/resources/ directory -->
	<mvc:resources mapping="/resources/**" location="/resources/" />
	<!-- Resolves views selected for rendering by @Controllers to .jsp resources
		in the /WEB-INF/views directory -->
<!-- 	<bean -->
<!-- 		class="org.springframework.web.servlet.view.InternalResourceViewResolver"> -->
<!-- 		<property name="prefix" value="/WEB-INF/views/" /> -->
<!-- 		<property name="suffix" value=".jsp" /> -->
<!-- 	</bean> -->
	<bean id="templateResolver"
		class="org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver">
		<property name="prefix" value="/WEB-INF/views/" />
		<property name="suffix" value=".html" />
		<!-- Template cache is true by default. Set to false if you want -->
		<!-- templates to be automatically updated when modified. -->
		<property name="cacheable" value="false" />
	</bean>
	<!-- SpringTemplateEngine automatically applies SpringStandardDialect and -->
	<!-- enables Spring's own MessageSource message resolution mechanisms. -->
	<bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine">
		<property name="templateResolver" ref="templateResolver" />
	</bean>
	
	<bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
        <property name="templateEngine" ref="templateEngine" />
    </bean>
	
	<!-- Imports user-defined @Controller beans that process client requests -->
	<import resource="controllers.xml" />
	<!-- Enable STOMP over WebSocket -->
	<websocket:message-broker application-destination-prefix="/app">
		<websocket:stomp-endpoint path="/websocket">
			<websocket:sockjs />
		</websocket:stomp-endpoint>
		<websocket:simple-broker prefix="/topic, /queue" />
	</websocket:message-broker>
</beans>


Key points:
a. The header introduces "xmlns:websocket="http://www.springframework.org/schema/websocket"", the namespace corresponding to websocket
b. "<websocket:message-broker application-destination-prefix = "/app">" indicates that requests starting with app will be considered as websocket requests
c. "<websocket:stomp-endpoint path="/websocket">" Register a websocket endpoint and use stomp as a sub-protocol, this The endpoint will be used in websocket handshake (initialize websocket in js)
d. "<websocket:sockjs />" means to enable the sockjs backup option, so that the effect of websocket can be simulated normally on the browser side that does not support websocket
e. "<websocket :simple-broker prefix="/topic, /queue" />" indicates that the broker adopts SimpleBroker, and defines that the addresses corresponding to the broker request are topic and queue
3. Component configuration document, the main function
a. Configure the default page path
b. Configure the component Scanning path
Specific file
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

    <!-- Maps '/' requests to the 'home' view -->
    <mvc:view-controller path="/" view-name="views/index.html"/>
    
    <context:component-scan base-package="test.spring.socket"/>
     
</beans>

4. Code
a.controller accepts user requests, including http requests and websocket requests
package test.spring.socket;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import test.spring.socket.service.GreetingService;

@Controller
public class GreetingController {

	private static final Log logger = LogFactory.getLog(GreetingController.class);

	@Autowired
	private GreetingService service;

	@MessageMapping("/hello")
	@SendTo("/topic/greetings")
	public Greeting greeting(HelloMessage message) throws Exception {
		service.greeting();
		return new Greeting("Hello, " + message.getName() + "!");
	}

	@RequestMapping("/index")
	public String hello() {
		logger.info("start to maping request hell");
		return "socket/index";
	}

}


b.service simulates logic processing and sends a message to the broker after processing is complete
/**
 *
 */
package test.spring.socket.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;

import test.spring.socket.Greeting;

/**
 * @author dwxx-chengaofeng
 *
 */
@Service
public class GreetingService {
	@Autowired
	private SimpMessagingTemplate template;

	public void greeting() throws InterruptedException {
		for (int i = 0; i < 10; i++) {
			Thread.sleep(1000); // simulated delay
			template.convertAndSend("/topic/greetings", new Greeting("the number is" + i));
		}
	}
}


c.html code
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Hello WebSocket</title>
    <link th:href="@{/resources/socket/css/main.css}" rel="stylesheet">
    <script type="text/javascript" th:src="@{/resources/js/jquery-2.0.3.js}"></script>
    <script type="text/javascript" th:src="@{/resources/socket/js/sockjs.js}"></script>
    <script type="text/javascript" th:src="@{/resources/socket/js/stomp.js}"></script>
    <script type="text/javascript" th:src="@{/resources/socket/js/app.js}"></script>
</head>
<body>
<noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websocket relies on Javascript being
    enabled. Please enable
    Javascript and reload this page!</h2></noscript>
<div id="main-content" class="container">
    <div class="row">
        <div class="col-md-6">
            <form class="form-inline">
                <div class="form-group">
                    <label for="connect">WebSocket connection:</label>
                    <button id="connect" class="btn btn-default" type="submit">Connect</button>
                    <button id="disconnect" class="btn btn-default" type="submit" disabled="disabled">Disconnect
                    </button>
                </div>
            </form>
        </div>
        <div class="col-md-6">
            <form class="form-inline">
                <div class="form-group">
                    <label for="name">What is your name?</label>
                    <input type="text" id="name" class="form-control" placeholder="Your name here...">
                </div>
                <button id="send" class="btn btn-default" type="submit">Send</button>
            </form>
        </div>
    </div>
    <div class="row">
        <div class="col-md-12">
            <table id="conversation" class="table table-striped">
                <thead>
                <tr>
                    <th>Greetings</th>
                </tr>
                </thead>
                <tbody id="greetings">
                </tbody>
            </table>
        </div>
    </div>
    </form>
</div>
</body>
</html>

d.js code
var stompClient = null;

function setConnected(connected) {
    $("#connect").prop("disabled", connected);
    $("#disconnect").prop("disabled", !connected);
    if (connected) {
        $("#conversation").show();
    }
    else {
        $("#conversation").hide();
    }
    $("#greetings").html("");
}

function connect() {
    var socket = new SockJS ('/ SpringWebsocket / websocket');
    stompClient = Stomp.over(socket);
    stompClient.connect({}, function (frame) {
        setConnected(true);
        console.log('Connected: ' + frame);
        stompClient.subscribe('/topic/greetings', function (greeting) {
            showGreeting(JSON.parse(greeting.body).content);
        });
    });
}

function disconnect() {
    if (stompClient != null) {
        stompClient.disconnect();
    }
    setConnected(false);
    console.log("Disconnected");
}

function sendName() {
    stompClient.send("/app/hello", {}, JSON.stringify({'name': $("#name").val()}));
}

function showGreeting(message) {
    $("#greetings").append("<tr><td>" + message + "</td></tr>");
}

$(function () {
    $("form").on('submit', function (e) {
        e.preventDefault();
    });
    $( "#connect" ).click(function() { connect(); });
    $( "#disconnect" ).click(function() { disconnect(); });
    $( "#send" ).click(function() { sendName(); });
});


Key point
a. "var socket = new SockJS('/SpringWebsocket/websocket');" initializes a SockJs object, where SpringWebsocket is its own project name, and websocket is the endpoint
b of the websocket configured in Servlet-context.xml." stompClient.send("/app/hello", {}, JSON.stringify({'name': $("#name").val()}));" to send a message to websocket, the corresponding method is in the controller The @MessageMapping("/hello") annotation method, note that there is an additional app prefix, which is the application-destination-prefix configured in Servlet-context.xml
c. "stompClient.subscribe('/topic/greetings', function ( greeting) {
            showGreeting(JSON.parse(greeting.body).content);
        });" means to subscribe to /topic/greetings from the broker, when a message is sent to /topic/greetings, the client can receive a notification, and get the content of the message.
The final project catalog:



Please refer to the attachment for the specific code of the project

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=327067607&siteId=291194637