zoukankan      html  css  js  c++  java
  • 浅析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服务。然后在网页中输入对应的链接。

     完成简单的客户端与服务端的通信

  • 相关阅读:
    XSS
    web安全之sql注入
    12、API
    7、源与值(Source/Values)
    3、高级方法(Advanced Recipes)
    树莓派实现SIM868 ppp拨号上网
    【转】在树莓派上实现人脸识别
    树莓派raspbian安装matchbox-keyboard虚拟键盘
    python+树莓派实现IoT(物联网)数据上传到服务器
    python安装MySQLclient
  • 原文地址:https://www.cnblogs.com/Koalin/p/12113309.html
Copyright © 2011-2022 走看看