淺析websocket的基本應用spring boot + vue +C# + WPF


1.基本概念

首先websocket是基於H5的一種通信。在網頁中如果定時獲取服務器端的實時數據,我們常采用long poll 和ajax輪詢的方式。但是在輪詢過程中,由於根本沒有新數據的改變,而造成一種資源的浪費,同時也不能夠保證數據的實時性。long poll是一種保持長連接的方式獲取數據,但是需要進行頭文件的各種校驗,也是一種資源的浪費。

websocket完美的解決了這種兩種方式的不足,首先能夠保證數據的實時性,同時保證資源的完整利用,是網頁和服務端的全雙工通信,即可以接收來自網頁端的消息,同時可以發送通知網頁端。websocket還支持多種方式,本篇討論java,C#(WPF)和vue,即服務端為java,客戶端分別為vue和WPF來進行驗證。

2.基本原理

websocket是基於TCP的一種通信,所以在建立通信之前首先需要建立TCP的一系列連接(三次握手等)。

服務端采用Springboot來實現,首先在pom.xml中添加WebSocect的依賴

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

  接下來建立WebSocket並實現ServerEndPoint  我這里采用注解的方式

package com.koalin.rpc.websocket;

import com.utils.DateTimeUtils;
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.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;


/**
* @version 1.0
* @ClassName WebSocket
* @Author koalin
* @Description //TODO WebSocket的描述
* @Date 2019/12/24 23:27
*/
@ServerEndpoint("/koalin/websocket/{userName}")
@Component
public class WebSocket {
private Session session;
private static CopyOnWriteArraySet<WebSocket> webSockets = new CopyOnWriteArraySet<>();

private static Map<String, Session> sessionPool = new HashMap<String, Session>();
volatile static boolean isRunning = false;
private String userName;

@OnOpen
public void onOpen(Session session, @PathParam(value = "userName") String userName) {
this.session = session;
this.userName = userName;
webSockets.add(this);
sessionPool.put(session.getId(), session);
System.out.println(userName + "【websocket消息】有新的連接,總數為:" + webSockets.size());

if (webSockets.size()==1) {
isRunning = true;
new Runnable() {
@Override
public void run() {
while (true) {
try {
for (WebSocket client :
webSockets) {
String msg = "Hello I am WebSocekt " + client.userName + "我的時間:" + DateTimeUtils.DateTimeYYYYMMDDHHMMSS(new Date());
client.session.getAsyncRemote().sendText(msg);
System.out.println(msg);

Thread.sleep(2000);
}
} catch (InterruptedException e) {
e.printStackTrace();
isRunning=false;
}

}
}
}.run();
}
}

@OnClose
public void onClose() {
if (webSockets.contains(this) ){
webSockets.remove(this);
System.out.println(this.userName+"【websocket消息】連接斷開,總數為:" + webSockets.size());
}

}

@OnMessage
public void onMessage(String message,Session session) {
//System.out.println("【websocket消息】收到客戶端消息:" + message);
System.out.println("【websocket消息】收到客戶端消息:" + message);
}

// 此為廣播消息
public void sendAllMessage(String message) {
for (WebSocket webSocket : webSockets) {
System.out.println("【websocket消息】廣播消息:" + message);
try {
webSocket.session.getAsyncRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}

// 此為單點消息
public void sendOneMessage(String userName, String message) {
System.out.println("【websocket消息】單點消息:" + message);
Session session = sessionPool.get(userName);
if (session != null) {
try {
session.getAsyncRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}

}

添加WebSocketConfig 創建默認的EndPointServer

package com.koalin.rpc.websocket;

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

/**
 * @version 1.0
 * @ClassName WebSocketConfig
 * @Author koalin
 * @Description //TODO WebSocketConfig的描述
 * @Date 2019/12/24 23:36
 */
@Configuration
public class WebSocketConfig {

    /**
     * @return
     * @Author koalin
     * @Description //TODO這個bean會自動注冊使用了@ServerEndpoint注解聲明的Websocket endpoint
     * @Date 22:47 2019/12/24
     * @Param
     **/
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

以上完成服務端代碼

接下來先驗證C#客戶端

首選在nuget中下載websocket-sharp

 

 

 

創建WebSocketClient

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using WebSocketSharp;

namespace WebSocketLib
{
    public class WebSocketClient
    {
        private WebSocket ws;
        private string url = "";
        CancellationToken token = new CancellationToken();
        public WebSocketClient(string url)
        {

            this.url = url;
        }

        public void Start()
        {

            try
            {
                ws = new WebSocket(url, token, 102392,
                      () =>
                      {//OnOpen
                          return Task.Run(() =>
                          {
                              Console.WriteLine("websocket連接正常....");
                          });
                      },
                      (e) =>
                      {//OnClose
                          return Task.Run(() =>
                          {
                              Console.WriteLine("websocket關閉正常...");
                          });
                      },
                      (e) =>
                      {//OnMessage
                          return Task.Run(() =>
                          {
                              Console.WriteLine("接收到服務端的消息" + e.Text.ReadToEnd());
                          });
                      },
                      (e) =>
                      {//OnError
                          return Task.Run(() =>
                          {
                              Console.WriteLine("連接異常..." + e.Message);
                          });
                      }
                      );

                ws.Connect();
            }
            catch (Exception e)
            {

                Console.WriteLine(e.ToString());
            }
        }
 public void StartSendMessage()
        {
 
         
            Task.Run(async () =>
            {
                await Task.Delay(1000);
                while (true)
                {
                    try
                    {
                        if (ws != null ) 
                        {
 
         
                             Task<bool> tast= ws.Send(("HI i am C# client"+DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")));
 
         
                             Console.WriteLine(tast.Result);
                        }
                    }
                    catch (Exception ex)
                    {
 
         

                    }
                    await Task.Delay(2000);
                }
            });
        }
public void Close()
        {
            if (ws!=null)
            {
                ws.Close();
                ws.Dispose();
            }
        }
    }
}

 

簡單的建立wpf窗體應用程序然后添加引用

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WebSocketDemo
{
    /// <summary>
    /// MainWindow.xaml 的交互邏輯
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();


        }
        WebSocketLib.WebSocketClient client = null;
        private void Test()
        {

          client = new WebSocketLib.WebSocketClient("ws://www.koalin.com:8081/koalin/websocket/test");

            client.Start();

            
            client.StartSendMessage();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Test();
        }

        protected override void OnClosed(EventArgs e)
        {
            if (client!=null)
            {
                client.Close();
                
            }
            base.OnClosed(e);
        }
    }
}

  啟動服務和客戶端進行簡單的驗證

 

 

 

 

 

建立vue工程,然后添加如下websockt關鍵代碼

initWebSocket () {

        // 連接錯誤
        this.websocket.onerror = this.setErrorMessage
        // 連接成功
        this.websocket.onopen = this.setOnopenMessage
        // 收到消息的回調
        this.websocket.onmessage = this.setOnmessageMessage
        // 連接關閉的回調
        this.websocket.onclose = this.setOncloseMessage
        // 監聽窗口關閉事件,當窗口關閉時,主動去關閉websocket連接,防止連接還沒斷開就關閉窗口,server端會拋異常。
        window.onbeforeunload = this.onbeforeunload
      },
      setErrorMessage () {
        console.log('WebSocket連接發生錯誤   狀態碼:' + this.websocket.readyState)
      },
      setOnopenMessage () {
        console.log('WebSocket連接成功    狀態碼:' + this.websocket.readyState)

        
      },
      setOnmessageMessage (event) {
        // 根據服務器推送的消息做自己的業務處理
        console.log('服務端返回:' + event.data)
      },
      setOncloseMessage () {
        console.log('WebSocket連接關閉    狀態碼:' + this.websocket.readyState)
      },
      onbeforeunload () {
        this.closeWebSocket()
      },
      
      closeWebSocket () {
        this.websocket.close()
      }
      },
  mounted() {
    this.restaurants = this.loadAll();
// WebSocket
      if ('WebSocket' in window) {
      // var url='ws://www.koalin.com:8081/koalin/websocket/' + new Date();
        this.websocket = new WebSocket('ws://www.koalin.com:8081/koalin/websocket/' + new Date());
       console.log(  this.websocket);
        this.initWebSocket();
      } else {
        alert('當前瀏覽器 Not support websocket')
      }
  },
  beforeDestroy () {

      this.onbeforeunload()

    }

 

啟動vue服務。然后在網頁中輸入對應的鏈接。

 

 完成簡單的客戶端與服務端的通信

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM