最近需要做一个网页动态展示服务器CPU,内存等信息,类似windows任务管理器的B/S版。因为服务器是windows server 2008,所以采用了ASP.NET来开发,主要涉及2部分,一是读取系统信息;二是动态展示。
读取系统信息分为CPU,内存,硬盘和网络四部分。其中内存和硬盘信息都用了System.Management类库,CPU信息则用到了System.Diagnostics命名空间下的PerformanceCounter类,网络信息主要利用System.Net.NetworkInformation命名空间下的类。源代码如下:
public struct DiskInfo
{
public string Name;
public long Free;
public long Size;
}
public class SystemInfo
{
private const string CategoryName = "Processor";
private const string CounterName = "% Processor Time";
private const string InstanceName = "_Total";
private static PerformanceCounter pc = new PerformanceCounter(CategoryName, CounterName, InstanceName);
private static NetworkInterface[] nicArr; //网卡集合
private static long sent = 0, received = 0;
public static string NetName = "";
/// <summary>
/// CPU 使用率,可参考
/// https://msdn.microsoft.com/en-us/library/system.diagnostics.performancecounter(v=vs.110).aspx
/// https://msdn.microsoft.com/en-us/library/w8f5kw2e(v=vs.110).aspx
/// 其中CategoryName主要有如下几种
/// Cache, Memory, Objects, PhysicalDisk, Process, Processor, Server, System, Thread categories
/// 而CounterName可以通过Performance Monitor查看已有的(在Performance Monitor中,
/// 选中Performance Monitor,点击添加“+”,即可看到)。
/// </summary>
/// <returns>CPU使用百分比</returns>
public static float CPUrate()
{
// 单个CPU使用情况要除以CPU个数 Environment.ProcessorCount,但是windows似乎每个CPU使用率并不完全相同
return pc.NextValue();
}
/// <summary>
/// 机器总的内存数,注意该值可能不精确。可参考
/// https://msdn.microsoft.com/en-us/library/aa394102(v=vs.85).aspx
/// 还可以使用 Win32_PhysicalMemory 类中的 Capacity 变量来获得内存信息,可参考
/// https://msdn.microsoft.com/en-us/library/aa394347(v=vs.85).aspx
/// </summary>
/// <returns>总内存大小字节数,单位是Bytes</returns>
public static long TotalMemory()
{
long m_PhysicalMemory = 0;
ManagementClass mc = new ManagementClass("Win32_ComputerSystem");
ManagementObjectCollection moc = mc.GetInstances();
foreach (ManagementObject mo in moc)
{
if (mo["TotalPhysicalMemory"] != null)
{
m_PhysicalMemory = long.Parse(mo["TotalPhysicalMemory"].ToString());
}
}
return m_PhysicalMemory;
}
/// <summary>
/// 已用内存信息,可参考
/// https://msdn.microsoft.com/en-us/library/aa394239(v=vs.85).aspx
/// </summary>
/// <returns>返回已使用的内存大小,单位是Bytes</returns>
public static long MemUsage()
{
long m_PhysicalMemory = 0;
long availablebytes = 0;
ManagementClass mos = new ManagementClass("Win32_OperatingSystem");
foreach (ManagementObject mo in mos.GetInstances())
{
if (mo["FreePhysicalMemory"] != null)
{
availablebytes = 1024 * long.Parse(mo["FreePhysicalMemory"].ToString());
}
if (mo["TotalVisibleMemorySize"] != null)
{
m_PhysicalMemory = 1024 * long.Parse(mo["TotalVisibleMemorySize"].ToString());
}
}
return m_PhysicalMemory - availablebytes;
}
/// <summary>
/// 硬盘使用情况,可参考
/// https://msdn.microsoft.com/en-us/library/aa394173(v=vs.85).aspx
/// </summary>
/// <returns>返回了硬盘基本信息,包括名称,大小,可用大小,单位为Bytes</returns>
public static DiskInfo[] DiskUsage()
{
ManagementClass diskClass = new ManagementClass("Win32_LogicalDisk");
ManagementObjectCollection disks = diskClass.GetInstances();
List<DiskInfo> info = new List<DiskInfo>();
foreach (ManagementObject disk in disks)
{
if (int.Parse(disk["DriveType"].ToString()) == (int)DriveType.Fixed)
{
string name = disk["Name"].ToString();
long size = long.Parse(disk["Size"].ToString());
long free = long.Parse(disk["FreeSpace"].ToString());
info.Add(new DiskInfo() { Name = name, Size = size, Free = free });
}
}
return info.ToArray();
}
/// <summary>
/// 网络传输信息,可参考
/// https://msdn.microsoft.com/en-us/library/system.net.networkinformation.networkinterface(v=vs.110).aspx
/// </summary>
/// <param name="ipAddress">要监控的ip地址</param>
/// <param name="received">下载字节数,单位为Bytes</param>
/// <param name="sent">上传字节数,单位为Bytes</param>
public static void NetUsage(string ipAddress, out long received, out long sent)
{
nicArr = NetworkInterface.GetAllNetworkInterfaces();
foreach (NetworkInterface net in nicArr)
{
IPInterfaceProperties ipProperty = net.GetIPProperties();
// 该网卡为以太网且有连接
if (net.NetworkInterfaceType == NetworkInterfaceType.Ethernet && net.OperationalStatus == OperationalStatus.Up)
{
foreach (var ip in ipProperty.UnicastAddresses)
{
// 如果为IPv4地址
if (ip.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
{
NetName = ip.Address.ToString(); // 获取ip地址
}
}
if (NetName == ipAddress)
{
IPv4InterfaceStatistics curIPfs = net.GetIPv4Statistics();
info.Received = curIPfs.BytesReceived - received;
info.Sent = curIPfs.BytesSent - sent;
sent = curIPfs.BytesSent;
received = curIPfs.BytesReceived;
break;
}
}
}
}
}
为了方便,把相关的几个网址记录如下:
- CPU 使用率,可参考
https://msdn.microsoft.com/en-us/library/system.diagnostics.performancecounter(v=vs.110).aspx
https://msdn.microsoft.com/en-us/library/w8f5kw2e(v=vs.110).aspx
- 内存信息,可参考
https://msdn.microsoft.com/en-us/library/aa394102(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/aa394347(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/aa394239(v=vs.85).aspx
- 硬盘使用情况,可参考
https://msdn.microsoft.com/en-us/library/aa394173(v=vs.85).aspx
- 网络传输信息,可参考
https://msdn.microsoft.com/en-us/library/system.net.networkinformation.networkinterface(v=vs.110).aspx
动态展示主要用到ASP.NET chart控件和updatepanel控件。鉴于硬盘信息变化实时性不强,因此用两个panel分别放置动态的(CPU,内存和网络)和静态的(硬盘)chart。
aspx文件主要代码如下:
<asp:Panel ID="DynamicPanel" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<%--每2秒(2000毫秒)更新一次--%>
<asp:Timer ID="timerTest" runat="server" Interval="2000"
OnTick="timer_Tick">
</asp:Timer>
<asp:Chart ID="Chart1" runat="server" Width="640px" Height="360px">
<Series>
<asp:Series Name="Series1" ChartType="SplineArea" Color="PowderBlue" BorderColor="Blue">
</asp:Series>
</Series>
<ChartAreas>
<asp:ChartArea Name="ChartArea1" AlignmentOrientation="Horizontal">
<AxisX>
<MajorGrid LineDashStyle="Dash" />
<LabelStyle Enabled="false" />
</AxisX>
<AxisY Title="Y Axis" TitleFont="微软雅黑,16pt" TitleForeColor="RoyalBlue">
<MajorGrid LineDashStyle="Dash" />
</AxisY>
</asp:ChartArea>
</ChartAreas>
<Legends>
<asp:Legend Name="Legend1" Enabled="False"></asp:Legend>
</Legends>
</asp:Chart>
</ContentTemplate>
</asp:UpdatePanel>
</asp:Panel>
<asp:Panel ID="StaticPanel" runat="server" ScrollBars="Vertical">
</asp:Panel>
aspx.cs主要代码如下:
public struct NetInfo
{
public long Sent;
public long Received;
}
public partial class SysInfoChart : System.Web.UI.Page
{
private static string InfoType = "cpu"; // 该变量用于记录当前状态
private static float m_timeInterval = 2; // 刷新时间,用于计算网络传输速率
private static bool m_loaded = false; // 该变量用于控制单次载入
private const int gridX = 10; // 最多显示10个数据点
public static List<float> ChartData = new List<float>();
public static List<NetInfo> NetData = new List<NetInfo>();
public static DiskInfo[] DiskData;
public static int TickNum = 0;
public const float GBFactor = 1024 * 1024 * 1024.0f;
public const float MBFactor = 1024 * 1024.0f;
public const float KBFactor = 1024.0f;
public static float TotalMemory = SystemInfo.TotalMemory() / MBFactor;
protected void Page_Load(object sender, EventArgs e)
{
if (!m_loaded)
{
m_timeInterval = timerTest.Interval / 1000.0f;
if (InfoType == "disk")
{
DynamicPanel.Visible = false;
StaticPanel.Visible = true;
}
else
{
DynamicPanel.Visible = true;
StaticPanel.Visible = false;
}
TickNum = 0;
ChartData.Clear();
NetData.Clear();
m_loaded = true;
}
}
protected void timer_Tick(object sender, EventArgs e)
{
TickNum += 1;
switch(InfoType)
{
case "cpu":
ChartData.Add(SystemInfo.CPUrate());
CPU_Chart_Load(sender, e);
break;
case "mem":
ChartData.Add(SystemInfo.MemUsage() / MBFactor);
Mem_Chart_Load();
break;
case "disk":
DiskData = SystemInfo.DiskUsage();
Disk_Chart_Load();
break;
case "net":
long received, sent;
// 此处"192.168.1.1"为本机ip地址
SystemInfo.NetUsage("192.168.1.1", out received, out sent);
NetData.Add(new NetInfo(){ Received = received, Sent = sent });
Net_Chart_Load();
break;
default:
break;
}
}
/// <summary>
/// 调整数据,只显示固定数量的数据点。如果数据量不足,则显示0
/// 该实现不好,应该用固定大小Queue,不过时间不够,先凑合用
/// </summary>
/// <param name="dt">要显示的数据表</param>
private void DataLoad(ref DataTable dt)
{
if (ChartData.Count >= gridX) // 如果数据量多于要显示的
{
int index = ChartData.Count - gridX;
for (int i = 0; i < gridX; i++)
{
dt.Rows.Add(i, ChartData[index + i]);
}
}
else // 如果数据量不足
{
int index = gridX - ChartData.Count;
for (int i = 0; i < gridX; i++)
{
if (i < index)
{
dt.Rows.Add(i, 0);
}
else
{
dt.Rows.Add(i, ChartData[i - index]);
}
}
}
}
/// <summary>
/// 调整数据,只显示固定数量的数据点。如果数据量不足,则显示0。专为网络数据用。
/// 将来最好和上一个函数整合一下,时间不够凑合用
/// </summary>
/// <param name="dt">要显示的数据表</param>
private void DataLoad2(ref DataTable dt)
{
if (NetData.Count >= gridX)
{
int index = NetData.Count - gridX;
for (int i = 0; i < gridX; i++)
{
int tmp = index + i;
float divider = KBFactor * m_timeInterval;
dt.Rows.Add(i, NetData[tmp].Sent / divider,
NetData[tmp].Received / divider,
NetData[tmp].MaxSpeed / divider);
}
}
else
{
int index = gridX - NetData.Count;
for (int i = 0; i < gridX; i++)
{
if (i < index)
{
dt.Rows.Add(i, 0, 0, 0);
}
else
{
int tmp = i - index;
float divider = KBFactor * m_timeInterval;
dt.Rows.Add(i, NetData[tmp].Sent / divider,
NetData[tmp].Received / divider,
NetData[tmp].MaxSpeed / divider);
}
}
}
}
protected void CPU_Chart_Load(object sender, EventArgs e)
{
DataTable m_dt = new DataTable();
m_dt.Columns.Add("时间", typeof(int));
m_dt.Columns.Add("CPU 占用率 (%)", typeof(float));
DataLoad(ref m_dt);
Chart1.DataSource = m_dt;
Chart1.Series["Series1"].XValueMember = "时间";
Chart1.Series["Series1"].YValueMembers = "CPU 占用率 (%)";
Chart1.ChartAreas["ChartArea1"].AxisY.Title = "CPU 占用率 (%)";
Chart1.ChartAreas["ChartArea1"].AxisY.Minimum = 0;
Chart1.ChartAreas["ChartArea1"].AxisY.Maximum = 100;
Chart1.DataBind();
}
protected void Mem_Chart_Load()
{
DataTable m_dt = new DataTable();
m_dt.Columns.Add("时间", typeof(int));
m_dt.Columns.Add("内存占用", typeof(float));
DataLoad(ref m_dt);
Chart1.DataSource = m_dt;
Chart1.Series["Series1"].XValueMember = "时间";
Chart1.Series["Series1"].YValueMembers = "内存占用";
Chart1.ChartAreas["ChartArea1"].AxisY.Title = "内存占用 (MB)";
Chart1.ChartAreas["ChartArea1"].AxisY.Minimum = 0;
Chart1.ChartAreas["ChartArea1"].AxisY.Maximum = TotalMemory;
Chart1.ChartAreas["ChartArea1"].AxisY.LabelStyle.Format = "f2"; // 只显示2位小数
Chart1.DataBind();
}
protected void Disk_Chart_Load()
{
for (int i = 0; i < DiskData.Length; i++)
{
string name = DiskData[i].Name;
float used = (DiskData[i].Size - DiskData[i].Free) / GBFactor;
float free = DiskData[i].Free / GBFactor;
float size = DiskData[i].Size / GBFactor;
List<string> dataN = new List<string>();
List<string> dataS = new List<string>();
dataN.Add("可用空间");
dataN.Add("已用空间");
dataS.Add(free.ToString("f2"));
dataS.Add(used.ToString("f2"));
#region 动态添加表
Chart m_chart = new Chart();
m_chart.Width = 500;
m_chart.Height = 400;
ChartArea m_area;
Series m_series;
m_area = new ChartArea("ChartArea" + i.ToString());
m_series = new Series("Series" + i.ToString());
m_chart.ChartAreas.Add(m_area);
m_chart.Series.Add(m_series);
Legend m_legend = new Legend("Legend" + i.ToString());
m_legend.Docking = Docking.Top;
m_legend.LegendStyle = LegendStyle.Row;
m_chart.Legends.Add(m_legend);
Title m_title = new Title("磁盘驱动器" + name);
m_title.Font = new System.Drawing.Font("微软雅黑", 16);
m_title.ForeColor = System.Drawing.Color.SteelBlue;
m_chart.Titles.Add(m_title);
m_series.ChartType = SeriesChartType.Pie; // 饼图
m_series.ChartArea = m_area.Name;
m_series.LegendText = "#VALX: #VALY GB";
m_series.Label = "#VALY GB [#PERCENT]";
m_series.Points.DataBindXY(dataN, dataS);
m_series["PieLabelStyle"] = "Outside"; //饼图说明显示方式(外面)
m_series.ShadowOffset = 2;
m_series.BorderColor = System.Drawing.Color.DarkGray;
#endregion
StaticPanel.Controls.Add(m_chart);
}
}
protected void Net_Chart_Load()
{
DataTable m_dt = new DataTable();
m_dt.Columns.Add("时间", typeof(int));
m_dt.Columns.Add("上传速率 (KB/s)", typeof(float));
m_dt.Columns.Add("下载速率 (KB/s)", typeof(float));
m_dt.Columns.Add("最大速率 (KB/s)", typeof(float));
DataLoad2(ref m_dt);
Chart1.DataSource = m_dt;
Chart1.Series["Series1"].XValueMember = "时间";
Chart1.Series["Series1"].YValueMembers = "下载速率 (KB/s)";
float downLoadRate = NetData[NetData.Count - 1].Received / (m_timeInterval * KBFactor);
Chart1.Series["Series1"].LegendText = "下载速率:" + downLoadRate.ToString("f2") + " KB/s";
Series m_series = new Series("Series2");
m_series.XValueMember = "时间";
m_series.YValueMembers = "上传速率 (KB/s)";
m_series.ChartType = SeriesChartType.SplineArea;
m_series.Color = System.Drawing.Color.FromArgb(128, System.Drawing.Color.LightGreen); // 半透明,防止互相遮挡
m_series.BorderColor = System.Drawing.Color.Green;
Chart1.Series.Add(m_series);
float upLoadRate = NetData[NetData.Count - 1].Sent / (m_timeInterval * KBFactor);
Chart1.Series["Series2"].LegendText = "上传速率:" + upLoadRate.ToString("f2") + " KB/s";
Chart1.Legends["Legend1"].Enabled = true;
Chart1.ChartAreas["ChartArea1"].AxisY.Title = "上传下载速率 (KB/s)";
Chart1.DataBind();
}
}
最后这个aspx.cs文件做了些修改,可能会导致编译无法通过,如果直接复制过去的话,需要自己修改一下。另外有几个地方时间紧来不及好好设计实现了,凑合完成了功能,有时间可以再好好弄一下。比如网络可以根据当前传输速率自动切换单位为MB和KB什么的。
最后上个图