<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          SpringBoot+WebSocket實現(xiàn)服務端、客戶端

          共 8023字,需瀏覽 17分鐘

           ·

          2020-09-12 20:27

          點擊上方藍色字體,選擇“標星公眾號”

          優(yōu)質(zhì)文章,第一時間送達

          ? 作者?|??47號Gamer丶?

          來源 |? urlify.cn/jUR3Af

          66套java從入門到精通實戰(zhàn)課程分享?

          一、引言

          本人最近一直在使用springboot框架開發(fā)項目,畢竟現(xiàn)在很多公司都在采用此框架,之后本人也會陸續(xù)寫關于springboot開發(fā)常用功能的文章。?

          什么場景下會要使用到websocket的呢?

          websocket主要功能就是實現(xiàn)網(wǎng)絡通訊,比如說最經(jīng)典的客服聊天窗口、您有新的消息通知,或者是項目與項目之間的通訊,都可以采用websocket來實現(xiàn)。

          二、websocket介紹

          在公司實際使用websocket開發(fā),一般來都是這樣的架構,首先websocket服務端是一個單獨的項目,其他需要通訊的項目都是以客戶端來連接,由服務端控制消息的發(fā)送方式(群發(fā)、指定發(fā)送)。但是也會有服務端、客戶端在同一個項目當中,具體看項目怎么使用。

          本文呢,采用的是服務端與客戶端分離來實現(xiàn),包括使用springboot搭建websokcet服務端、html5客戶端、springboot后臺客戶端,?具體看下面代碼。

          三、服務端實現(xiàn)

          步驟一:springboot底層幫我們自動配置了websokcet,引入maven依賴

          ?
          ????org.springframework.boot
          ????spring-boot-starter-websocket

          步驟二:如果是你采用springboot內(nèi)置容器啟動項目的,則需要配置一個Bean。如果是采用外部的容器,則可以不需要配置。

          /**
          ?*?@Description:?配置類
          ?*/
          @Component
          public?class?WebSocketConfig?{
          ??
          ????/**
          ?????*?ServerEndpointExporter?作用
          ?????*
          ?????*?這個Bean會自動注冊使用@ServerEndpoint注解聲明的websocket?endpoint
          ?????*
          ?????*?@return
          ?????*/
          ????@Bean
          ????public?ServerEndpointExporter?serverEndpointExporter()?{
          ????????return?new?ServerEndpointExporter();
          ????}
          }

          步驟三:最后一步當然是編寫服務端核心代碼了,其實本人不是特別想貼代碼出來,貼很多代碼影響文章可讀性。

          package?com.example.socket.code;
          ??
          import?lombok.extern.slf4j.Slf4j;
          import?org.springframework.stereotype.Component;
          ??
          import?javax.websocket.OnClose;
          import?javax.websocket.OnMessage;
          import?javax.websocket.OnOpen;
          import?javax.websocket.Session;
          import?javax.websocket.server.PathParam;
          import?javax.websocket.server.ServerEndpoint;
          import?java.util.concurrent.ConcurrentHashMap;
          ??
          /**
          ?*?@Description:?websocket?服務類
          ?*/
          ??
          /**
          ?*
          ?*?@ServerEndpoint 這個注解有什么作用?
          ?*
          ?*?這個注解用于標識作用在類上,它的主要功能是把當前類標識成一個WebSocket的服務端
          ?*?注解的值用戶客戶端連接訪問的URL地址
          ?*
          ?*/
          ??
          @Slf4j
          @Component
          @ServerEndpoint("/websocket/{name}")
          public?class?WebSocket?{
          ??
          ????/**
          ?????*??與某個客戶端的連接對話,需要通過它來給客戶端發(fā)送消息
          ?????*/
          ????private?Session?session;
          ??
          ?????/**
          ?????*?標識當前連接客戶端的用戶名
          ?????*/
          ????private?String?name;
          ??
          ????/**
          ?????*??用于存所有的連接服務的客戶端,這個對象存儲是安全的
          ?????*/
          ????private?static?ConcurrentHashMap?webSocketSet?=?new?ConcurrentHashMap<>();
          ??
          ??
          ????@OnOpen
          ????public?void?OnOpen(Session?session,?@PathParam(value?=?"name")?String?name){
          ????????this.session?=?session;
          ????????this.name?=?name;
          ????????//?name是用來表示唯一客戶端,如果需要指定發(fā)送,需要指定發(fā)送通過name來區(qū)分
          ????????webSocketSet.put(name,this);
          ????????log.info("[WebSocket]?連接成功,當前連接人數(shù)為:={}",webSocketSet.size());
          ????}
          ??
          ??
          ????@OnClose
          ????public?void?OnClose(){
          ????????webSocketSet.remove(this.name);
          ????????log.info("[WebSocket]?退出成功,當前連接人數(shù)為:={}",webSocketSet.size());
          ????}
          ??
          ????@OnMessage
          ????public?void?OnMessage(String?message){
          ????????log.info("[WebSocket]?收到消息:{}",message);
          ????????//判斷是否需要指定發(fā)送,具體規(guī)則自定義
          ????????if(message.indexOf("TOUSER")?==?0){
          ????????????String?name?=?message.substring(message.indexOf("TOUSER")+6,message.indexOf(";"));
          ????????????AppointSending(name,message.substring(message.indexOf(";")+1,message.length()));
          ????????}else{
          ????????????GroupSending(message);
          ????????}
          ??
          ????}
          ??
          ????/**
          ?????*?群發(fā)
          ?????*?@param?message
          ?????*/
          ????public?void?GroupSending(String?message){
          ????????for?(String?name?:?webSocketSet.keySet()){
          ????????????try?{
          ????????????????webSocketSet.get(name).session.getBasicRemote().sendText(message);
          ????????????}catch?(Exception?e){
          ????????????????e.printStackTrace();
          ????????????}
          ????????}
          ????}
          ??
          ????/**
          ?????*?指定發(fā)送
          ?????*?@param?name
          ?????*?@param?message
          ?????*/
          ????public?void?AppointSending(String?name,String?message){
          ????????try?{
          ????????????webSocketSet.get(name).session.getBasicRemote().sendText(message);
          ????????}catch?(Exception?e){
          ????????????e.printStackTrace();
          ????????}
          ????}
          }

          四、客戶端實現(xiàn)

          HTML5實現(xiàn):以下就是核心代碼了,其實其他博客有很多,本人就不多說了。

          var?websocket?=?null;
          ???if('WebSocket'?in?window){
          ???????websocket?=?new?WebSocket("ws://192.168.2.107:8085/websocket/testname");
          ???}
          ?
          ???websocket.onopen?=?function(){
          ???????console.log("連接成功");
          ???}
          ?
          ???websocket.onclose?=?function(){
          ???????console.log("退出連接");
          ???}
          ?
          ???websocket.onmessage?=?function?(event){
          ???????console.log("收到消息"+event.data);
          ???}
          ?
          ???websocket.onerror?=?function(){
          ???????console.log("連接出錯");
          ???}
          ?
          ???window.onbeforeunload?=?function?()?{
          ???????websocket.close(num);
          ???}

          SpringBoot后臺實現(xiàn):本人發(fā)現(xiàn)多數(shù)博客都是采用js來實現(xiàn)客戶端,很少有用后臺來實現(xiàn),所以本人也就寫了寫,大神請勿噴?。很多時候,項目與項目之間通訊也需要后臺作為客戶端來連接。

          步驟一:首先我們要導入后臺連接websocket的客戶端依賴



          ????org.java-websocket
          ????Java-WebSocket
          ????1.3.5

          步驟二:把客戶端需要配置到springboot容器里面去,以便程序調(diào)用。

          package?com.example.socket.config;
          ??
          import?lombok.extern.slf4j.Slf4j;
          import?org.java_websocket.client.WebSocketClient;
          import?org.java_websocket.drafts.Draft_6455;
          import?org.java_websocket.handshake.ServerHandshake;
          import?org.springframework.context.annotation.Bean;
          import?org.springframework.stereotype.Component;
          ??
          import?java.net.URI;
          ??
          /**
          ?*?@Description:?配置websocket后臺客戶端
          ?*/
          @Slf4j
          @Component
          public?class?WebSocketConfig?{
          ??
          ????@Bean
          ????public?WebSocketClient?webSocketClient()?{
          ????????try?{
          ????????????WebSocketClient?webSocketClient?=?new?WebSocketClient(new?URI("ws://localhost:8085/websocket/test"),new?Draft_6455())?{
          ????????????????@Override
          ????????????????public?void?onOpen(ServerHandshake?handshakedata)?{
          ????????????????????log.info("[websocket]?連接成功");
          ????????????????}
          ??
          ????????????????@Override
          ????????????????public?void?onMessage(String?message)?{
          ????????????????????log.info("[websocket]?收到消息={}",message);
          ??
          ????????????????}
          ??
          ????????????????@Override
          ????????????????public?void?onClose(int?code,?String?reason,?boolean?remote)?{
          ????????????????????log.info("[websocket]?退出連接");
          ????????????????}
          ??
          ????????????????@Override
          ????????????????public?void?onError(Exception?ex)?{
          ????????????????????log.info("[websocket]?連接錯誤={}",ex.getMessage());
          ????????????????}
          ????????????};
          ????????????webSocketClient.connect();
          ????????????return?webSocketClient;
          ????????}?catch?(Exception?e)?{
          ????????????e.printStackTrace();
          ????????}
          ????????return?null;
          ????}
          ??
          }

          步驟三:使用后臺客戶端發(fā)送消息

          1、首先本人寫了一個接口,里面有指定發(fā)送和群發(fā)消息兩個方法。

          2、實現(xiàn)發(fā)送的接口,區(qū)分指定發(fā)送和群發(fā)由服務端來決定(本人在服務端寫了,如果帶有TOUSER標識的,則代表需要指定發(fā)送給某個websocket客戶端)。

          3、最后采用get方式用瀏覽器請求,也能正常發(fā)送消息。

          package?com.example.socket.code;
          ??
          /**
          ?*?@Description:?websocket?接口
          ?*/
          public?interface?WebSocketService?{
          ??
          ????/**
          ?????*?群發(fā)
          ?????*?@param?message
          ?????*/
          ?????void?groupSending(String?message);
          ??
          ????/**
          ?????*?指定發(fā)送
          ?????*?@param?name
          ?????*?@param?message
          ?????*/
          ?????void?appointSending(String?name,String?message);
          }
          package?com.example.socket.chat;
          ??
          import?com.example.socket.code.ScoketClient;
          import?org.springframework.beans.factory.annotation.Autowired;
          import?org.springframework.web.bind.annotation.GetMapping;
          import?org.springframework.web.bind.annotation.RequestMapping;
          import?org.springframework.web.bind.annotation.RestController;
          ??
          /**
          ?*?@Description:?測試后臺websocket客戶端
          ?*/
          @RestController
          @RequestMapping("/websocket")
          public?class?IndexController?{
          ??
          ????@Autowired
          ????private?ScoketClient?webScoketClient;
          ??
          ????@GetMapping("/sendMessage")
          ????public?String?sendMessage(String?message){
          ????????webScoketClient.groupSending(message);
          ????????return?message;
          ????}
          }

          五、最后

          注意:

          如果是單例的情況下,這個對象的值都會被修改。

          本人就抽了時間Debug了一下,經(jīng)過下圖也可以反映出,能夠看出,webSokcetSet中存在三個成員,并且vlaue值都是不同的,所以在這里沒有出現(xiàn)對象改變而把之前對象改變的現(xiàn)象。

          服務端這樣寫是沒問題的。

          最后總結(jié):在實際WebSocket服務端案例中為什么沒有出現(xiàn)這種情況,當WebSokcet這個類標識為服務端的時候,每當有新的連接請求,這個類都是不同的對象,并非單例。

          import?com.alibaba.fastjson.JSON;
          ??
          import?java.util.concurrent.ConcurrentHashMap;
          ??
          /**
          ?*?@Description:
          ?*/
          public?class?TestMain?{
          ??
          ????/**
          ?????*?用于存所有的連接服務的客戶端,這個對象存儲是安全的
          ?????*/
          ????private?static?ConcurrentHashMap?webSocketSet?=?new?ConcurrentHashMap<>();
          ??
          ????public?static?void?main(String[]?args)?{
          ????????Student?student?=?Student.getStudent();
          ????????student.name?=?"張三";
          ????????webSocketSet.put("1",?student);
          ??
          ????????Student?students?=?Student.getStudent();
          ????????students.name?=?"李四";
          ????????webSocketSet.put("2",?students);
          ??
          ????????System.out.println(JSON.toJSON(webSocketSet));
          ????}
          }
          ??
          /**
          ?*?提供一個單例類
          ?*/
          class?Student?{
          ??
          ????public?String?name;
          ??
          ????private?Student()?{
          ????}
          ??
          ????private?static?final?Student?student?=?new?Student();
          ??
          ????public?static?Student?getStudent()?{
          ????????return?student;
          ??
          ????}
          }

          打印結(jié)果:

          {"1":{"name":"李四"},"2":{"name":"李四"}}





          ??? ?



          感謝點贊支持下哈?

          瀏覽 69
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产日韩视频在线观看 | 成人视频毛片 | 亚洲无码18禁 | 久久视频成人 | 黄色视频在线观看国产 |