C# WPF网络实时监测客户端

       前段时间一个客户项目,现场部署的是无线网络,使用我们系统的时候,发现总在某些时间段出现请求特别慢,并且客户有三个工厂,一工厂、二工厂使用的没问题,唯独三工厂一直出现问题,经过几次与客户方IT交流,始终不承认是网络有问题,估计是因为害怕担责任,因此不得不写一个程序,来实现实时网络监测,以洗清我们的责任(这就是甲方和乙方,甲方是大爷,乙方不如甲方的狗)。

一、需求分析

        我们的目标是实时监测网络情况,我们没有小工具前是如何做的呢?正常情况下,依靠windows系统的时候我们都是用打开命令窗口,用ping ip 的方式,看看网络有没有很大的延迟。好了,我们也不要做的太复杂,第一点就是实现这个ping。然后,我们想到,如果只是ping一次,那么我们还要这个工具干嘛?为啥不每次手动输入呢?所以,第二点我们需要实现自动长时间ping,有点像 ping -t 的命令。我们只是查看这个结果还不行,还需要有个记录,就像数据库一样的东西,记录下来,这样也好分析时间等等因素,这就是第三点,我们要实现日志记录功能。再想想,还有什么需要我们做的吗?对,如果能在界面上随意添加一个ip,我们就可以实现ping,多好,对,这就是我们要实现的第四点功能,ip可以随时添加,随时移除。大概就是这么多了,以后有其他的功能在慢慢迭代吧!

 

二、概要设计

       首先,按照一个产品的生命周期来算,我应该出一个设计图了,不管是用什么工具画的,至少需要一个什么样的图,以指导开发,当然,这里的开发就我一个人,谁让我没有一个开发团队呢(不用急,楼主以后肯定会有的)?好,下面我就用最简单的windows 画图工具画一个,简单省事。

              

          上面就是设计图,是不是超简单,但是清晰明了,我要的东西上面都有。

三、详细设计

         下面就开始枯燥乏味的编码了,嗯,就是编码,而且是枯燥乏味的。

1、画界面

         用的是WPF,所以界面的画法其实跟前端写页面差不多,这里就直接上代码了。

<Window x:Class="NetWork_Watch.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:NetWork_Watch"
        mc:Ignorable="d"
        Icon="Resource/ico.ico"
        ContentRendered="window_contentRendered"
        Closing="Window_Closing"
        Title="Network Watch" Height="700" Width="800" ResizeMode="CanMinimize">
    <Grid Name="MainGrid">
        <Grid.RowDefinitions>
            <RowDefinition Height="300"/>
            <RowDefinition Height="400"/>
        </Grid.RowDefinitions>

        <Grid Name="TopGrid" Grid.Row="0">
            <Grid.RowDefinitions>
                <RowDefinition Height="300"></RowDefinition>
            </Grid.RowDefinitions>
            <Rectangle Fill="#EEEEEE" HorizontalAlignment="Right" Margin="0,0,400,10" Width="390"/>
            <Rectangle Fill="#EEEEEE" Height="290" Margin="400,0,0,0" VerticalAlignment="Top" Width="390"/>
            <Label x:Name="label" Content="设置" HorizontalAlignment="Left" FontSize="20" FontWeight="Bold" Margin="10,10,0,0" VerticalAlignment="Top"/>
            <Label x:Name="ip_lab" Content="IP/网址:" FontSize="14" HorizontalAlignment="Left" Margin="40,80,0,0" VerticalAlignment="Top"/>
            <TextBox x:Name="ip_tb" HorizontalAlignment="Left" FontSize="14" Height="30" Margin="120,80,0,0" TextWrapping="Wrap" VerticalAlignment="Top" VerticalContentAlignment="Center"  Width="240"/>
            <Button x:Name="remove_btn" Content="移 除" HorizontalAlignment="Left" Margin="69,180,0,0" VerticalAlignment="Top" Width="75" Height="30" FontSize="16" Click="button_remove_Click"/>
            <Button x:Name="add_btn" Content="添 加" HorizontalAlignment="Left" Margin="270,180,0,0" VerticalAlignment="Top" Width="75" Height="30" FontSize="16" Click="button_add_Click"/>
            <Label x:Name="label1" Content="IP/网址" HorizontalAlignment="Left" FontSize="20" FontWeight="Bold" Margin="420,10,0,0" VerticalAlignment="Top"/>
            <DataGrid x:Name="log_dg" IsReadOnly="True" ItemsSource="{Binding}" HorizontalAlignment="Left" Margin="420,50,0,0" VerticalAlignment="Top" Height="230" Width="350" SelectionChanged="log_dg_SelectionChanged">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="IP/网址" Width="150" Binding="{Binding ip}"/>
                    <DataGridTextColumn Header="创建时间" Width="*" Binding="{Binding createTime}"/>
                </DataGrid.Columns>
            </DataGrid>
        </Grid>
        <Grid Name="BottomGrid" Grid.Row="1" Margin="0,0,0,30">
            <Grid.RowDefinitions>
                <RowDefinition Height="400"></RowDefinition>
            </Grid.RowDefinitions>
            <Rectangle Fill="#EEEEEE" HorizontalAlignment="Left" Margin="0,10,0,0" Width="800" Height="400" VerticalAlignment="Top"/>
            <Label x:Name="label2" Content="日志统计" HorizontalAlignment="Left" Margin="10,20,0,0" FontSize="20" FontWeight="Bold" VerticalAlignment="Top"/>
            <TextBox x:Name="log_tb" HorizontalAlignment="Left" Height="290" Margin="10,60,0,0" VerticalAlignment="Top" Width="760"/>
        </Grid>
    </Grid>
</Window>

对应的 vs 2015会显示出界面效果,如下:

      

界面算是布局完成了,接下来,需要实现逻辑了。

2、实现添加、移除IP、网址功能
       要实现移除添加ip功能,其实就是对一个list 进行插入和删除就行了。因为在这里做的比较简单,所以没有用存储数据库或者文件的方式进行序列化ip,而是每次打开这个小工具的时候进行添加要监听的ip。

      定义一个实体类,用来定义一个ip的添加时间以及ip地址,如下:

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

namespace NetWork_Watch
{
    public class IpInfo
    {
        public string ip { set; get; }
        public string createTime { set; get; }

        public IpInfo(string ip)
        {
            this.ip = ip;
            this.createTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
        }
    }
}

对,很简单,就是一个ip,一个创建时间,构造方法就是传入一个ip,然后自动记录时间。用一个全局的list来存储当前所有插入的ip地址信息,写在界面布局对应的cs文件中。

ObservableCollection<IpInfo> list = new ObservableCollection<IpInfo>();

添加、移除按钮事件如下:

     private void button_add_Click(object sender, RoutedEventArgs e)
        {
            bool flag = false;
            for(int i = 0; i< list.Count; i++)
            {
                if(list[i].ip == ip_tb.Text)
                {
                    flag = true;
                    break;
                }
            }
            if (!flag)
            {
                IpInfo ipInfo = new IpInfo(ip_tb.Text);
                list.Add(ipInfo);
                log_tb.AppendText(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")+"-增加监听客户端:" + ipInfo.ip);
                log_tb.AppendText(Environment.NewLine);
            }
            else
            {
                MessageBox.Show("已存在需要添加的ip,无法重复添加。", "提示", MessageBoxButton.OK,MessageBoxImage.Error);
            }
           
        }
     
        private void button_remove_Click(object sender, RoutedEventArgs e)
        {
            for (int i = 0; i < list.Count; i++)
            {
                if (list[i].ip == ip_tb.Text)
                {
                    ip_tb.Text = null;
                    log_dg.SelectedItem = null;
                    list.RemoveAt(i);
                    i--;
                }
            }
        }

这样就可以实现动态的添加、删除ip了。

嗯,第一步效果实现了。

3、实现日志记录功能

        看到上面日志统计里面的信息了吗?这个只会在程序开着的时候进行显示,我现在要记录到文件中怎么办?联想起Java 里面用的日志组件,log4j,嗯,肯定有c# 版的,打开NuGet,搜索,结果如下:

果然,有此神物,点击安装,然后创建配置文件,至于配置文件怎么配置在这就不详细解说了。我的配置如下:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="log4net" type="System.Configuration.IgnoreSectionHandler"/>
  </configSections>
  <log4net>
    <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
      <!--日志存储位置-->
      <file value="logs\log.log"/>
      <appendToFile value="true"/>
      <rollingStyle value="Composite"/>
      <datePattern value="yyyyMMdd"/>
      <maxSizeRollBackups value="10"/>
      <maximumFileSize value="1MB"/>
      <layout type="log4net.Layout.PatternLayout">
        <!--日志输出格式-->
        <conversionPattern value="%date [%thread] %-5level %logger - %message%newline"/>
      </layout>
    </appender>
    <root>
      <level value="All"/>
      <appender-ref ref="RollingLogFileAppender"/>
    </root>
  </log4net>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
  </startup>
</configuration>

配置完日志就没事了吗?不,还有一步,必须配置不可,在AssemblyInfo.cs添加如下一行,相当于注册组件吧!

[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config", ConfigFileExtension = "config", Watch = true)]

配置好了,该使用了。

在类的最上面,实例化组件,

private static log4net.ILog logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

然后你就可以在你想要使用的地方潇洒的使用了,例如:

logger.Info("增加监听客户端:" + ipInfo.ip);

在我们项目文件夹下面,将会产生一个log的文件夹,这个是在config中配置的路径,同时log文件夹中会有一个log文件,打开查看如下:

2019-03-26 10:41:19,763 [1] INFO  NetWork_Watch.MainWindow - 增加监听客户端:192.168.113.15

好了,日志功能实现了。

4、实现监听

        前面做了这么多,都是为了这一步做铺垫,要想时刻进行监听,就需要用一个循环,在不停的跑,然后实现实时监听。其实我们只需要对存储ip的list进行不断的ping,即可完成需求。c# 的Ping 类可以解决我们发送请求的问题,while(true)可以实现我们不断循环的需要。因此,我们就用这个来实现吧!

 Thread _ping = null;
        ObservableCollection<IpInfo> list = new ObservableCollection<IpInfo>();
        public MainWindow()
        {
            InitializeComponent();
            _ping = new Thread(PingRemote);
            _ping.Start();
        }

void PingRemote()
            {
            while (true) { 
                for(int i = 0; i< list.Count; i++)
                {
                    string host = list[i].ip;
                    PingReply reply = p1.Send(host,10); //发送主机名或Ip地址
                    if (reply.Status == IPStatus.Success)
                    {
                        logger.Info("远程地址:" + host + "请求连接成功,往返时间:" + reply.RoundtripTime + ",TTL:" + reply.Options.Ttl);
                        sendTBlog("远程地址:" + host + "请求连接成功,往返时间:" + reply.RoundtripTime + ",TTL:" + reply.Options.Ttl);                       
                    }
                    else if (reply.Status == IPStatus.TimedOut)
                    {
                        logger.Error("远程地址:" + host + "请求连接超时!");
                        sendTBlog("远程地址:" + host + "请求连接超时!");               }
                    else
                    {
                        logger.Error("远程地址:" + host + "请求连接失败!");
                        sendTBlog("远程地址:" + host + "请求连接失败!");
                    // 为了防止一直处于ping中,这里每次暂停10秒。                       }
                    Thread.Sleep(10*1000);
                }
            }
        }

 private void sendTBlog(string message)
        {
        // 注意,此处不能直接写输出到控件,因为这是另起的线程,会报错。
            log_tb.Dispatcher.BeginInvoke(new Action(() => { log_tb.AppendText(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") +"-"+message); }));
            log_tb.Dispatcher.BeginInvoke(new Action(() =>
            {
                log_tb.AppendText(Environment.NewLine);
            }));
        }

在创建界面的时候,就把线程打开,这样后面添加一项就会自动进行ping,最后实现效果:

一个简单好用的小工具,就这样实现了,虽然还有很多不足,但是可以完美解决我目前面临的问题。

  • 1
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
你好!关于在C# WPF实时绘制波形图表的问题,你可以尝试使用Chart控件来实现。下面是一些基本的步骤: 1. 在WPF窗口或用户控件中,添加一个Chart控件,并设置其属性和样式。 2. 创建一个数据源,用于存储实时获取的波形数据。 3. 使用定时器或后台线程,定期更新数据源中的数据。 4. 在定时器或后台线程的回调函数中,将数据源中的数据绑定到Chart控件上。 5. 根据需要,可以使用不同类型的Series(如LineSeries、AreaSeries等)来展示波形图表。 以下是一个简单的示例代码,帮助你入门: ```csharp using System; using System.Collections.Generic; using System.Windows; using System.Windows.Controls.DataVisualization.Charting; namespace RealTimeChartExample { public partial class MainWindow : Window { private List<double> data; private Random random; public MainWindow() { InitializeComponent(); // 初始化数据源和随机数生成器 data = new List<double>(); random = new Random(); // 设置Chart控件属性 chart.Title = "Real-time Waveform"; chart.Margin = new Thickness(10); // 启动定时器 System.Windows.Threading.DispatcherTimer timer = new System.Windows.Threading.DispatcherTimer(); timer.Tick += Timer_Tick; timer.Interval = TimeSpan.FromMilliseconds(100); timer.Start(); } private void Timer_Tick(object sender, EventArgs e) { // 更新数据源 double value = random.NextDouble() * 100; // 模拟实时数据 data.Add(value); // 绑定数据源到Chart控件 LineSeries series = new LineSeries(); series.ItemsSource = data; // 清空Chart控件的Series集合并添加新的Series chart.Series.Clear(); chart.Series.Add(series); } } } ``` 你可以根据自己的需求对示例代码进行修改和扩展,以适应实际的应用场景。希望能对你有所帮助!如果有任何问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值