内容简介:在构建Spring boot项目时已经提供webSocket依赖的勾选。webSocket是TCP之上的一个非常薄的轻量级层 ,webSocket主要的应用场景离不开即时通讯与消息推送,但只要应用程序需要在浏览器和服务器之间来回发送消息,就可以使用webSocket来降低客户端流量与服务器的负载。下面将基于Spring boot实现一个非常简单的HelloWorld程序,用来熟悉项目构建逻辑。主要的webSocket依赖
在构建Spring boot项目时已经提供webSocket依赖的勾选。webSocket是TCP之上的一个非常薄的轻量级层 ,webSocket主要的应用场景离不开即时通讯与消息推送,但只要应用程序需要在浏览器和服务器之间来回发送消息,就可以使用webSocket来降低客户端流量与服务器的负载。
下面将基于Spring boot实现一个非常简单的HelloWorld程序,用来熟悉项目构建逻辑。
1.导入依赖
主要的webSocket依赖
<dependency> <group>>org.springframework.boot</group>> <artifact>>spring-boot-starter-websocket</artifact>> </dependency>
一些js库依赖,这里也使用maven方式导入,官网 https://www.webjars.org/
<dependency> <group>>org.webjars</group>> <artifact>>webjars-locator-core</artifact>> </dependency> <dependency> <group>>org.webjars</group>> <artifact>>sockjs-client</artifact>> <version>1.0.2</version> </dependency> <dependency> <group>>org.webjars</group>> <artifact>>stomp-websocket</artifact>> <version>2.3.3</version> </dependency> <dependency> <group>>org.webjars</group>> <artifact>>bootstrap</artifact>> <version>3.3.7</version> </dependency> <dependency> <group>>org.webjars</group>> <artifact>>jquery</artifact>> <version>3.1.0</version> </dependency>
Thymeleaf模板引擎,不用多说了吧
<dependency> <group>>org.springframework.boot</group>> <artifact>>spring-boot-starter-thymeleaf</artifact>> </dependency>
2.配置开启
接下来开启webSocket并配置一番
@Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Overr> public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/gs-gu>).withSockJS(); } @Overr> public vo> configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/topic"); config.setApplicationDestinationPrefixes("/app"); } }
这里从上向下说一下,
1)@EnableWebSocketMessageBroker注解用于开启使用STOMP协议来传输基于代理(MessageBroker)的消息,这时候控制器(controller)开始支持@MessageMapping,就像是使用@requestMapping一样。
2)registerStompEndpoints()方法只写了一行代码:
registry.addEndpoint("/gs-gu>).withSockJS();
一是用来注册一个Stomp的节点(endpoint),也就是webSocket的服务端地址,客户端在链接时使用到;
二是withSockJs()方法指定使用SockJS协议。SockJs是一个WebSocket的通信js库,Spring对这个js库进行了后台的自动支持,如果使用它不需要进行过多配置。
3)配置消息代理(MessageBroker),该方法干了两件事,一是启用一个简单的message broker并配置一个或多个前缀来过滤针对代理的目的地(例如以“/topic”为前缀的目的地),该前戳限制了网页客户端设置本地地址时的前戳。 二是设置了一个客户端访问服务端地址的前缀。比如我们设置@MessageMapping("/hello"),那客户端要发送消息到服务器上的地址是 /app/hello。
3.提供接口
@Controller public class GreetingController { @MessageMapping("/hello") @SendTo("/topic/greetings") public Greeting greeting(HelloMessage message) throws Exception { System.out.println("收到:" + message.toString() + "消息"); return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!"); }
1)@MessageMapping和@RequestMapping功能类似,用于设置URL映射地址,浏览器向服务器发起请求,需要通过该地址。
需要注意,这里设置路径为/hello,但是客户端需要访问/app/hello,原因前面已经讲述。
2)@SendTo("/topic/greetings") 设置目的地,这里的目的地是站在服务端的角度对客户端而言。客户端也需要设置相同的地址,而且必须使用/topic前戳,前面也已经讲述。
本示例中如果服务器接受到了消息,就会对订阅了@SendTo括号中的地址传送消息。
备注:HtmlUtils.htmlEscape()方法会将特殊字符转换为HTML字符引用。
HelloMessage.java
public > HelloMessage { private String name; ... }
Greeting.java
public > Greeting { private String content; }
4.客户端
在resources/static目录下:
index.html
<!DOCTYPE html> <html> <head> <title>Hello WebSocket</title> <link href="/webjars/bootstrap/css/bootstrap.min.css" rel="stylesheet"> <link href="/main.css" rel="stylesheet"> <link rel="shortcut icon" href="/favicon.ico" /> <script src="/webjars/jquery/jquery.min.js"></script> <script src="/webjars/sockjs-client/sockjs.min.js"></script> <script src="/webjars/stomp-websocket/stomp.min.js"></script> <script src="/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 >="main-content" >="container"> <div >="row"> <div >="col-md-6"> <form >="form-inline"> <div >="form-group"> <label for="connect">WebSocket connection:</label> <button >="connect" >="btn btn-default" type="submit">Connect</button> <button >="disconnect" >="btn btn-default" type="submit" disabled="disabled">Disconnect </button> </div> </form> </div> <div >="col-md-6"> <form >="form-inline"> <div >="form-group"> <label for="name">What is your name?</label> <input type="text" >="name" >="form-control" placeholder="Your name here..."> </div> <button >="send" >="btn btn-default" type="submit">Send</button> </form> </div> </div> <div >="row"> <div >="col-md-12"> <table >="conversation" >="table table-striped"> <thead> <tr> <th>Greetings</th> </tr> </thead> <tbody >="greetings"> </tbody> </table> </div> </div> </div> </body> </html>
main.css
body { background-color: #f5f5f5; } #main-content { max-w>: 940px; padding: 2em 3em; margin: 0 auto 20px; background-color: #fff; border: 1px sol>#e5e5e5; -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; }
app.js
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('/gs-gu>); stompClient = Stomp.over(socket); stompClient.connect({">: "header"}, 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(); }); });
这部分最值得说的就是JS部分了,比较简单主要就是3个点击事件,包括连接、断开、发送消息。
当我们点击了连接后,会新建SockjS对象,并设置服务端的连接点(/gs-gu>
subscribe()方法的第一个参数是注册客户端地址,注意前戳必须是/topic开头,因为在前面服务端已经配置了目的地前戳。与@SendTo中的地址对应。
客户端发送消息只需要调用send()方法,还方法的第一个参数是服务端@MessageMapping地址并且加了指定的/app前戳,第二个参数为header头部信息,第三个是发送的消息内容。
5.运行程序
成功实现Hello World程序,下面将通过搭建一个一对一聊天服务器,深入学习更多的socket知识。
6.发送消息
在前面我们写了一个自动回复的小例子,用到了@MessageMapping("/hello")和@SendTo("/topic/greetings")两个注解,其实我们还可以使用编程的方式发送消息。
其实很简单,直接引用该消息模板
@Autowired private SimpMessagingTemplate messagingTemplate;
消息模板内置了一系列方法,比如
vo> convertAndSendToUser(String user, String destination, Object payload) vo> convertAndSend(D destination, Object payload)
这俩方法会包装为消息并将其发送到给定的目的地。
7.STOMP监听
监听webSocket服务器的连接只需要实现ApplicationListener<>接口。代码如下:
@Component public > STOMPConnectEventListener implements ApplicationListener<SessionConnectEvent> { @Overr> public vo> onApplicationEvent(SessionConnectEvent event) { StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage()); //判断客户端的连接状态 switch (sha.getCommand()) { case CONNECT: System.out.println("上线"); break; case DISCONNECT: System.out.println("下线"); break; case SUBSCRIBE: System.out.println("订阅"); break; case SEND: System.out.println("发送"); break; case UNSUBSCRIBE: System.out.println("取消订阅"); break; default: break; } } }
监听的所有状态被封装在一个枚举类中,其实还有很多,这里不再一一列举。
有了上面这些知识,我们就可以基于此开发一对一聊天服务器。
8.Session存取
webSocket提供的是一个socket框架,并不会帮我们管理session,我们需要自己去编写session管理类,进行session的读写。代码如下:
@Component public > SocketSessionMap { private final static ConcurrentMap<String, String> sessionMap = new ConcurrentHashMap<>(); /** * 注册Session * @param userId * @param sessionId */ public synchronized vo> registerSession(String user> { sessionMap.put(userId,sessionId); } /** * 移除Session * @param userId * @param sessionId */ public synchronized vo> removeSession(String user> { sessionMap.remove(userId); } /** * 获取用户的SessionId * @param userId * @return */ public String getUserSession>(String user>{ return sessionMap.get(userId); } /** * 获取所有Session集合 * @return */ public Map<String, String> queryAllSession(){ return sessionMap; } /** * 获取集合的大小 */ public int onlineCount(){ return sessionMap.size(); } }
9.监听注册
接着,对STOMP监听类进行扩展。
@Component public > STOMPConnectEventListener implements ApplicationListener<SessionConnectEvent> { @Autowired SocketSessionMap socketSessionMap; @Overr> public vo> onApplicationEvent(SessionConnectEvent event) { StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage()); String user>">); String sessionId = sha.getSessionId(); switch (sha.getCommand()) { case CONNECT: System.out.println("上线:" + user>" " + sessionId); socketSessionMap.registerSession(userId, sessionId); break; default: break; } } }
服务端通过 sha.getFirstNativeHeader(">读取到客户端的>
当服务端监听到客户端连接时,会将用户Session>
10.监听下线
这里我们使用更可靠的请求下线方式,代码如下:
@MessageMapping("/chatOut") public vo> sayHello(String user> { String sessionId = socketSessionMap.getUserSessionId(userId); System.out.println("下线:" + user>" " + sessionId); socketSessionMap.removeSession(userId,sessionId); }
当收到下线请求时,移除Session>
关于Session,也可以设置一个最大值,超时自动移除。
11.一对一消息处理
在一对一服务器中,主要处理的就是一对一的消息发送。大致逻辑是接收客户端消息,分析消息结构,通过SessionMap判断对方是否在线,然后发送相应内容。代码如下:
@MessageMapping("/chat") public vo> sayHello(Message user) { System.out.println(user.get>"-->"+user.getP>":"+user.getContent()); String userP>String.valueOf(user.getPid()); String user>String.valueOf(user.getId()); String sendTo = "/topic/chat/"+userPid; String content = user.get>":"+user.getContent(); if (socketSessionMap.getUserSession>null){ messagingTemplate.convertAndSend(sendTo, HtmlUtils.htmlEscape(content)); }else { sendTo = "/topic/chat/"+userId; content = "对方已下线"; messagingTemplate.convertAndSend(sendTo, HtmlUtils.htmlEscape(content)); } }
值得一体的是,关于用户>
Message.java
public > Message { private int >//用户> private String content;//发送内容 private int p>//发送到用户
12.聊天网页
服务端使用FreeMarker模板引擎返回html网页,代码如下:
@RequestMapping("/chat/{>) public String chat_page(@PathVariable int id, ModelMap model) { model.addAttribute(">, id); int count = socketSessionMap.onlineCount(); model.addAttribute("count", count); return "chat"; }
通过RESTful形式的URl注册>
chat.html
<!DOCTYPE html > <html xmlns:th="http://www.thymeleaf.org"> <head> <title>Hello WebSocket</title> <link href="/webjars/bootstrap/css/bootstrap.min.css" rel="stylesheet"> <link href="/main.css" rel="stylesheet"> <link rel="shortcut icon" href="/favicon.ico" /> <script src="/webjars/jquery/jquery.min.js"></script> <script src="/webjars/sockjs-client/sockjs.min.js"></script> <script src="/webjars/stomp-websocket/stomp.min.js"></script> <script src="/chat.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 >="main-content" >="container"> <div >="row"> <div >="col-md-6"> <form >="form-inline"> <div >="form-group"> <label for="connect">WebSocket connection:</label> <button >="connect" >="btn btn-default" type="submit">Connect</button> <button >="disconnect" >="btn btn-default" type="submit" disabled="disabled">Disconnect </button> </div> </form> </div> <div >="col-md-6"> <form >="form-inline"> <div >="form-group"> <label th:text="'Online:'+${count}"> </label> <input type="text" >="> >="form-control" th:value="${>required> <input type="text" >="content" >="form-control" placeholder="Your name here..."required> <input type="text" >="p> >="form-control" placeholder="Your P>required> </div> <button >="send" >="btn btn-default" type="submit">Send</button> </form> </div> </div> <div >="row"> <div >="col-md-12"> <table >="conversation" >="table table-striped"> <thead> <tr> <th>Greetings</th> </tr> </thead> <tbody >="greetings"> </tbody> </table> </div> </div> </div> </body> </html>
chat.js
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('/gs-gu>); stompClient = Stomp.over(socket); stompClient.connect({">: $("#>).val()}, function (frame) { //客户端> setConnected(true); console.log('Connected: ' + frame); stompClient.subscribe('/topic/chat/' + $("#>).val(), function (greeting) { //表明客户端地址 showGreeting(greeting.body); }, {">: "Host_" + $("#>).val()}); }); } function disconnect() { if (stompClient !== null) { stompClient.send("/app/chatOut", {},$("#>).val()); stompClient.disconnect(); } setConnected(false); console.log("Disconnected"); } function sendName() { stompClient.send("/app/chat", {}, JSON.stringify({ 'content': $("#content").val(), '>: $("#>).val(), 'p>: $("#p>).val() })); showGreeting("我:" + $("#content").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(); }); });
网页客户端的主要逻辑在chat.js中。非常有必要的是在stompClient.connect()方法的第一个参数中传入header头信息,该头部信息必须设置>
其次是在用户断开链接前,会向服务端发送断开通知。
stompClient.send("/app/chatOut", {},$("#>).val());
13.程序演示
打开两个网页,URl分别为http://localhost:8080/chat/100和http://localhost:8080/chat/101,点击连接。
查看控制台输出:
接下来演示用户101向用户100发送消息:
查看用户100收到信息:
再次查看控制输出:
消息成功发送!
当用户断开链接时,控制台输出为:
关于Spring boot webSocket就先到这里。
Linux公社的RSS地址 : https://www.linuxidc.com/rssFeed.aspx
本文永久更新链接地址: https://www.linuxidc.com/Linux/2018-11/155549.htm
以上所述就是小编给大家介绍的《Spring Boot webSocket从入门到放弃》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Fluent Python
Luciano Ramalho / O'Reilly Media / 2015-8-20 / USD 39.99
Learn how to write idiomatic, effective Python code by leveraging its best features. Python's simplicity quickly lets you become productive with it, but this often means you aren’t using everything th......一起来看看 《Fluent Python》 这本书的介绍吧!