浏览器上进行数据实时展示 C#,WebSocket,EChart.js

444 阅读4分钟

目前做的项目中需要将设备中的实时数据展现到浏览器上,自己查了一些资料,发现用websocket可以满足后端与前端大量数据的传输需求。做了一个简单的demo记录一下。

下图就是后端实时传送上来的数据利用Echart进行展示的效果。

 

先来实现后端的数据上传逻辑:

后端代码我用的是C# 语言和 Fleck nuget包,还是比较容易建立websocket 服务的。首先我们建立一个websocket server class,设置两个属性,一个是服务器的websocket对象,此对象是为了创建websocket链接,另一个是客户端连到此服务上的socket链接数组。

public class Server
    {
        private WebSocketServer SocketServer { get; set; }
        private List<IWebSocketConnection> ClientSocket { get; set; } = new List<IWebSocketConnection>();
        
        }

接下来我们要写服务端的初始化代码,主要是是初始化websocketServer 对象,让它开始监听,以及当socket开始,关闭时的业务逻辑,我们此时只要在开始,和关闭时把对应的动作打印在console里做个日志输出即可。

public void Start(int port)
        {
            SocketServer = new WebSocketServer("ws://127.0.0.1:" + port);

            SocketServer.Start(socket =>
            {
                socket.OnOpen = () =>
                {
                    Console.WriteLine("Open!");
                    ClientSocket.Add(socket);
                };
                socket.OnClose = () =>
                {
                    ClientSocket.Remove(socket);
                    Console.WriteLine("Close!");
                };
                
            });

完成websocket的初始化建立后,开始写添加收到客户端发来的消息,并处理消息的逻辑代码。我是想当客户端索要数据时,我会不停的给它发送实时数据,直到客户端喊停为止。所以需要先创建一个方法能生成实时数据,因为想在echart里展示,我就做两个list,分别代表x轴和y轴。还需要一个把数据序列化的方法,这样传送时传送序列化后的数据效率会有提升。

数据结构很简单:

public class Data
    {
        public List<int> x { get; set; }
        public List<int> y { get; set; }
    }

        做序列化的代码:

private string ConvertObjectToString(Data obj)
        {
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(obj.GetType());
            MemoryStream memoryStream = new MemoryStream();
            serializer.WriteObject(memoryStream, obj);

            string json = Encoding.Default.GetString(memoryStream.ToArray());
            return json;
        }

        之后用random对象去生成些实时数据填进去:

private void ProcessClientRequest(){
    while (true)
        {
            Thread.Sleep(300);
            Data a = new Data()
            {
                x = new List<int>(),
                y = new List<int>()
            };
            Random r = new Random();
            for (int i = 1; i < 100; i++)
            {
                a.x.Add(i);
                a.y.Add(r.Next(1, 100));
             }

             ClientSocket.ToList().ForEach(s => s.Send(ConvertObjectToString(a)));
          }

}

        这样一来我们就可以无限的去生成数据,并通过客户端的连接数组发给所有链接我们的客户端了。但还有一点,我们想要让客户端控制数据开始和停止传送,目前无法停止。我想的是把传送数据代码段另开一个线程,不影响我们的服务器继续接受客户端指令。我们新建一个属性,来存放是否停止,我把此属性命名为IsStop:

socket.OnMessage = message =>
                {
                    Console.WriteLine(message);
                    Thread thread1 = new Thread(ProcessClientRequest);
                    thread1.Start(message);
                };

        首先把监听客户端信息的代码实现,我们另开辟一个线程去处理它,具体的处理也需要做一些变化,先是根据指令检查是否是stop ,如果是将IsStop赋值为true,这时另一个线程中也收到了变为true的信号,便停止了发送。当然这里有个漏洞,那就是如果前端一直发送开始发送的指令,就会有越来越多的线程给前端发送数据,不过我打算忽略它~哈哈哈:

private bool IsStop { get; set; }

private void ProcessClientRequest(object obj)
        {
            string cmd = obj.ToString();
            if (cmd !== "STOP")
            {
                IsStop = false;
                while (!IsStop)
                {
                    Thread.Sleep(300);
                    Data a = new Data()
                    {
                        x = new List<int>(),
                        y = new List<int>()
                    };
                    Random r = new Random();
                    for (int i = 1; i < 100; i++)
                    {
                        a.x.Add(i);
                        a.y.Add(r.Next(1, 100));
                    }

                    ClientSocket.ToList().ForEach(s => s.Send(ConvertObjectToString(a)));
                }
            }
            else
            {
                IsStop = true;
            }
        }

        整体的服务端代码就出来了:

        

public class Server
    {
        private WebSocketServer SocketServer { get; set; }
        private List<IWebSocketConnection> ClientSocket { get; set; } = new List<IWebSocketConnection>();
        private bool IsStop { get; set; }

        public void Start(int port)
        {
            SocketServer = new WebSocketServer("ws://127.0.0.1:" + port);

            SocketServer.Start(socket =>
            {
                socket.OnOpen = () =>
                {
                    Console.WriteLine("Open!");
                    ClientSocket.Add(socket);
                };
                socket.OnClose = () =>
                {
                    ClientSocket.Remove(socket);
                    Console.WriteLine("Close!");
                };
                socket.OnMessage = message =>
                {
                    Console.WriteLine(message);
                    Thread thread1 = new Thread(ProcessClientRequest);
                    thread1.Start(message);
                };
            });

            while (true)
            {
                
            }
        }

        private void ProcessClientRequest(object obj)
        {
            string cmd = obj.ToString();
            if (cmd == "ACD")
            {
                IsStop = false;
                while (!IsStop)
                {
                    Thread.Sleep(300);
                    Data a = new Data()
                    {
                        x = new List<int>(),
                        y = new List<int>()
                    };
                    Random r = new Random();
                    for (int i = 1; i < 100; i++)
                    {
                        a.x.Add(i);
                        a.y.Add(r.Next(1, 100));
                    }

                    ClientSocket.ToList().ForEach(s => s.Send(ConvertObjectToString(a)));
                }
            }
            else
            {
                IsStop = true;
            }
        }

        private string ConvertObjectToString(Data obj)
        {
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(obj.GetType());
            MemoryStream memoryStream = new MemoryStream();
            serializer.WriteObject(memoryStream, obj);

            string json = Encoding.Default.GetString(memoryStream.ToArray());
            return json;
        }

    }


//////////////////////////////////////////////////////////////////////
static void Main(string[] args)
        {
            Server server = new Server();
            server.Start(8800);
        }

完成后端的数据上传逻辑之后开始编写前端逻辑代码:

前端代码比较简单我用的是echart.js和javaScript 。首先写一个简单的html,将echart的容器定义出来:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="./echarts.js"></script>
    <script src="./main.js" defer></script>
    <title>data from websocket</title>
</head>
<body>
    <div id="main-chart" style="width: 600px;height:400px;"></div>
    <button class="btn_close">close</button>
</body>
</html>

        然后便是JS,需要定义一些echart的配置,以及设置连接后端的代码和更新图表的逻辑:

let myChart = echarts.init(document.getElementById('main-chart'));

let option = {
    title: {
      text: 'ECharts real time chart!!!'
    },
    tooltip: {},
    legend: {
      data: ['y']
    },
    xAxis: {
      data: []
    },
    yAxis: {},
    series: [
      {
        name: 'y',
        type: 'bar',
        data: []
      }
    ]
  };


let socket = new WebSocket('ws://127.0.0.1:8800');

socket.onopen = function(event)
{
    socket.send("ACD");
}

socket.onmessage = function(event)
{
    let data = JSON.parse(event.data);
    option.xAxis.data = data.x;
    option.series[0].data = data.y;
    myChart.setOption(option);
}

socket.onclose = function(event)
{
    console.log("socket closed");
}

let btn = document.getElementsByClassName('btn_close');

btn[0].addEventListener("click",function(){socket.send("STOP");});

ps        我发现当我的发送间隔极小的时候,比如50ms以下,echart的更新就有些跟不上了。。。所以我选择间隔几百毫秒发送一次。