使用.NET開(kāi)發(fā)掃描電腦中服務(wù)的工具
發(fā)布時(shí)間:2008-08-05 閱讀數(shù): 次 來(lái)源:網(wǎng)樂(lè)原科技
大中型企業(yè)信息系統(tǒng)中,對(duì)客戶(hù)端PC的管理,往往是容易出現(xiàn)問(wèn)題的環(huán)節(jié)。因此,很多大公司引入了各種分布式的管理系統(tǒng),例如防病毒方面的Norton AntiVirus,BlackICE防火墻,微軟的 SMS (System Management Server),等等,這些系統(tǒng)都會(huì)在客戶(hù)端的系統(tǒng)安裝相應(yīng)的客戶(hù)端軟件,一般都是以服務(wù)的形式出現(xiàn),但是由于種種原因,這些服務(wù)會(huì)停止運(yùn)行或者該客戶(hù)機(jī)根本沒(méi)有安裝這些客戶(hù)端服務(wù),這樣管理系統(tǒng)就出現(xiàn)了疏漏,有可能造成問(wèn)題,如因無(wú)法防御病毒而成為病毒源,無(wú)法為該客戶(hù)端發(fā)布軟件,無(wú)法管理客戶(hù)PC等等。在此,我們提供一個(gè)方案,可以定時(shí)按照IP地址掃描網(wǎng)絡(luò),報(bào)告出那些系統(tǒng)的特定的服務(wù)的狀態(tài)。
這個(gè)方案使用了Microsoft.NET技術(shù),同時(shí)也用到了.NET Framework中的,ADO.NET ,WMI management,XML。其核心是一個(gè)由VB.NET寫(xiě)的程序以及它的兩個(gè)配置文件,配置文件為XML格式,該程序按IP掃描網(wǎng)絡(luò),得到每個(gè)系統(tǒng)的服務(wù) 的狀態(tài),如果IP地址沒(méi)有對(duì)應(yīng)系統(tǒng),則忽略該IP,針對(duì)沒(méi)有安裝服務(wù)或服務(wù)停止的系統(tǒng)我們?cè)诹硪粋€(gè)線程中運(yùn)行NBTSTAT命令,得到其機(jī)器名,用戶(hù)名,MAC地址域等信息,以便我們找到機(jī)器解決問(wèn)題。其次為了保存掃描的結(jié)果,我們需要一個(gè)很小的數(shù)據(jù)庫(kù)MS-Access或MS-SQL server都可以,本文使用SQL2000 。最后為了呈現(xiàn)出掃描的結(jié)果,以便我們采取行動(dòng),這里我們使用網(wǎng)頁(yè)的形式把數(shù)據(jù)庫(kù)中的結(jié)果展現(xiàn)出來(lái)。
1. VB.NET程序
該程序使用兩個(gè)XML格式的配置文件,當(dāng)程序啟動(dòng)時(shí)會(huì)讀入這些配置。其中一個(gè)文件定義了需要掃描的網(wǎng)段,包括排除在外的地址段。另一個(gè)文件定義了連接數(shù)據(jù)庫(kù)的信息,以及數(shù)據(jù)表的定義。這兩個(gè)文件的內(nèi)容如下:
<IPLIST>
<IP LANID="192.168.100." ><EXP L="1" H="30"/></IP>
<IP LANID="192.168.101." />
<IP LANID="192.168.102." />
<IP LANID="192.168.103." />
<IP LANID="192.168.104." ><EXP L="1" H="40"/></IP>
</IPLIST>
該文件定義將要掃描5個(gè)網(wǎng)段,其中兩個(gè)網(wǎng)段有些地址需要排除在外(分配給打印機(jī)等設(shè)備),對(duì)于192.168.100段,我們排除從1到30,對(duì)于192.168.104段我們排除1到40。
DBServer
DB
REPORT
REPORT
SERVICE
該文件定義了連接數(shù)據(jù)庫(kù)所需的信息。
: SCANSERVICE數(shù)據(jù)庫(kù)的服務(wù)器名
: SCANSERVICE數(shù)據(jù)庫(kù)名
:用于更新SCANSERVICE數(shù)據(jù)庫(kù)的數(shù)據(jù)庫(kù)用戶(hù)名
:用于更新SCANSERVICE數(shù)據(jù)庫(kù)的數(shù)據(jù)庫(kù)用戶(hù)的密碼
:該TAG的 inner 定義了我們希望掃描的Service的名字,這里我們假定希望掃描服務(wù)名為SERVICE
該TAG的屬性定義了數(shù)據(jù)庫(kù)中表名,該表用于保存掃描結(jié)果
以下是程序代碼:
——————
‘首先我們定義一個(gè)類(lèi),主要用于得到某個(gè)IP地址的Service的狀態(tài)信息,并在服務(wù)狀態(tài)不正常時(shí)觸發(fā)另一線程得到該系統(tǒng)的詳細(xì)信息。
Imports System.ServiceProcess
Imports System.Xml
Imports System.Threading
Public Class GetStatus
Private IServiceName As String ‘服務(wù)的名稱(chēng)
Private IMachineIP As String ‘IP地址
Private ITable As String ‘在DATESET中的表名
'構(gòu)造函數(shù)
Sub New(ByVal Ip As String, ByVal SvcName As String, ByVal updatetable As String)
IMachineIP = Ip
IServiceName = SvcName
ITable = updatetable
End Sub
‘每個(gè)線程所運(yùn)行的方法,用于得到服務(wù)的狀態(tài),如果狀態(tài)不正常則觸發(fā)另一線程得到該IP的信息
Sub GetStausF()
Dim ServiceP As New ServiceController() ‘實(shí)例化一個(gè)ServiceController類(lèi)
ServiceP.MachineName = IMachineIP
ServiceP.ServiceName = IServiceName
Dim myRow As DataRow
Dim status As String
Dim Run As Boolean = False
myRow = ds.Tables(ITable).NewRow
Try
If ServiceP.Status.ToString <> "Running" Then
status = ServiceP.Status.ToString‘如果狀態(tài)不是RUNNING則將狀態(tài)賦予字符串變量
Else
Run = True ‘如果狀態(tài)為RUNNING,則不做任何事
End If
Catch er As Exception ‘以下處理取得狀態(tài)時(shí)候發(fā)生的異常
status = Left(er.Message, 35)
If InStr(status, "Service Control Manager") = 0 Then
status = "Not installed or open service failed" ‘沒(méi)有安裝該服務(wù)
ElseIf InStr(er.Message, "Manager") > 0 Then
status = "Can not detected" ‘服務(wù)的狀態(tài)不可得
End If
End Try
ServiceP.Close() ‘關(guān)閉ServiceController實(shí)例
‘以下判斷如果狀態(tài)不是RUNNING,則記錄該系統(tǒng),并觸發(fā)線程得到它的詳細(xì)信息。
If Not Run Then
myRow("msg") = status
myRow("ip") = IMachineIP
SyncLock GetType(AddRow) ‘為保證多線程情況下,對(duì)DataSet只有一個(gè)寫(xiě)操作,鎖定AddRow類(lèi)
Dim AddRowIns As New AddRow(myRow) '將IP和狀態(tài)通過(guò)我們自己寫(xiě)的AddRow類(lèi)插入DataSet
End SyncLock
‘觸發(fā)另一線程取得機(jī)器信息
Dim HostInfo2 As New HostInfo(IMachineIP)
Dim HostThr2 As New Thread(New ThreadStart(AddressOf HostInfo2.sysInfo))
HostThr2.Start()
SyncLock GetType(HostInfoThreadCounter)
HostInfoThreadCounter.counter += 1 ‘啟動(dòng)線程數(shù)加1
End SyncLock
End If
SyncLock GetType(StoppCounter)
StopThr.AddStop()
End SyncLock
End Sub
End Class
‘該類(lèi)只有一個(gè)方法,就是將停止的線程數(shù)減1
Class StoppCounter
Sub AddStop()
ThreadCounterStopped = ThreadCounterStopped + 1
End Sub
End Class
‘此類(lèi)用于將已有的行插入DataSet
Class AddRow
‘第一個(gè)構(gòu)造函數(shù),以構(gòu)造好的行為輸入?yún)?shù)
Sub New(ByVal row As DataRow)
Try
ds.Tables(0).Rows.Add(row)
Catch ee As Exception
End Try
End Sub
‘第二個(gè)構(gòu)造函數(shù),以機(jī)器名用戶(hù)名等字符串為參數(shù),更新已有的行
Sub New(ByVal IP As String, ByVal user As String, ByVal hostname As String, ByVal Mac As String, ByVal domain As String, ByVal timeout As Char)
Dim RowTimeOut As DataRow
Try
For Each RowTimeOut In ds.Tables(0).Select("IP='" & IP & "'")
RowTimeOut.Item("LastUID") = user
RowTimeOut.Item("Name") = hostname
RowTimeOut.Item("Mac") = Mac
RowTimeOut.Item("Domain") = domain
RowTimeOut.Item("Timeout") = timeout 'Set timeout flag to this item
Exit For 'just run once
Next
Catch er As Exception
End Try
End Sub
End Class
‘由于篇幅限制,這里省略了根據(jù)IP取得機(jī)器信息的類(lèi)的代碼。
Imports System.Threading ‘用于支持多線程
Imports System.Xml ‘用于分析XML格式的參數(shù)文件
Imports System.Data ‘用于保存結(jié)果到數(shù)據(jù)庫(kù)
Module Module1
Public ds As New DataSet()
Public conn1 As SqlClient.SqlConnection ‘?dāng)?shù)據(jù)庫(kù)連接
Public ipf As String ‘IP列表文件名
Public dbf As String ‘?dāng)?shù)據(jù)庫(kù)信息文件
Public ThreadCounterStopped As Integer
Public StopThr As New StoppCounter()
Sub Main() ‘程序主程序
Dim machineIP As String
Dim iplistF As New Xml.XmlDocument()
Dim iplist As Xml.XmlNode
Dim ipitem As Xml.XmlNode
Dim DBinfoF As New Xml.XmlDocument()
Dim DBinfo As Xml.XmlNode
Dim LanID As String
Dim i As Integer
Dim timestart As Integer
Dim ThreadCounterStarted As Integer
ThreadCounterStarted = 0
ThreadCounterStopped = 0
Dim server As String
Dim database As String
Dim uid As String
Dim pwd As String
Dim table As String
Dim connstr, connstr1 As String
Dim ServiceName As String
Dim Purgestr As String
Try
DBinfoF.Load(dbf) ‘讀取數(shù)據(jù)庫(kù)信息文件
Catch nodb As Exception
MsgBox(nodb.Message & "Wrong DB info file name.")
Exit Sub
End Try
Try
iplistF.Load(ipf) ‘讀取IP列表文件
Catch noip As Exception
MsgBox(noip.Message & "Wrong IP list file name.")
Exit Sub
End Try
‘分析數(shù)據(jù)庫(kù)信息文件
DBinfo = DBinfoF.ChildNodes(0)
server = DBinfo.ChildNodes(0).InnerText
database = DBinfo.ChildNodes(1).InnerText
uid = DBinfo.ChildNodes(2).InnerText
pwd = DBinfo.ChildNodes(3).InnerText
ServiceName = DBinfo.ChildNodes(4).InnerText
table = DBinfo.ChildNodes(4).Attributes(0).Value
‘根據(jù)分析所得,構(gòu)造連接字符串
connstr1 = "server=" & server & ";database=" & database & ";uid=" & uid & ";password=" & pwd
conn1 = New SqlClient.SqlConnection(connstr1) ‘實(shí)例化數(shù)據(jù)庫(kù)連接
conn1.Open() ‘打開(kāi)數(shù)據(jù)庫(kù)連接
Dim sa As SqlClient.SqlDataAdapter = New SqlClient.SqlDataAdapter("select * from " & table, conn1)
Dim combu As New SqlClient.SqlCommandBuilder(sa)
sa.Fill(ds, table) ‘填充DataSet
ds.Clear() ‘清空舊的數(shù)據(jù)
Dim IPAddress As String
‘分析IP列表文件
iplist = iplistF.ChildNodes(0)
Dim Ai As Integer
Dim ipexcepCount As Integer
Dim ipexcep As Xml.XmlNode
For Each ipitem In iplist.ChildNodes
Dim Excep(2, 83) As Integer
LanID = ipitem.Attributes(0).Value‘得到網(wǎng)絡(luò)ID
For i = 2 To 254 '從2到254,根據(jù)每個(gè)網(wǎng)絡(luò)ID構(gòu)造IP地址
Ai = 0
‘以下判斷是為了跳過(guò)保留地址段
If ipitem.HasChildNodes Then
ipexcepCount = ipitem.ChildNodes.Count
ReDim Excep(2, ipexcepCount - 1)
For Each ipexcep In ipitem.ChildNodes
Excep(0, Ai) = CInt(ipexcep.Attributes(0).Value)
Excep(1, Ai) = CInt(ipexcep.Attributes(1).Value)
Ai = Ai + 1
Next
End If
For Ai = 0 To ipexcepCount - 1
If i >= Excep(0, Ai) And i <= Excep(1, Ai) Then
Console.WriteLine("跳過(guò)保留地址: " & LanID & i.ToString)
GoTo SkipIP
End If
Next
machineIP = LanID & i.ToString ‘IP地址
‘以下觸發(fā)線程以,得到服務(wù)狀態(tài)
Dim getSt As New GetStatus(machineIP, ServiceName, table)
Dim GetStThread As New Thread(New ThreadStart(AddressOf getSt.GetStausF))
GetStThread.Start()
ThreadCounterStarted = ThreadCounterStarted + 1‘啟動(dòng)線程數(shù)加1
Console.WriteLine("線程" & machineIP & " 啟動(dòng)。檢測(cè) " & ServiceName)
‘每啟動(dòng)100個(gè)線程,程序主線程停止15秒,避免太多線程造成內(nèi)存溢出
If (ThreadCounterStarted Mod 100) = 0 Then
Console.WriteLine("等待 .......")
Thread.CurrentThread.Sleep(15000)
GC.Collect() 'force garbage collection to aviod outOfMemory when run with long IP list
End If
SkipIP:
Next
Next
Console.WriteLine("Exiting program ...") '所有線程都已觸發(fā)
Finish:
Thread.CurrentThread.Sleep(5000) ‘以下程序等待所有線程結(jié)束
GC.Collect()
If ThreadCounterStopped = ThreadCounterStarted And HostInfoThreadCounter.counter = HostInfoThreadCounter.counterSTOP Then‘如果觸發(fā)線程等于結(jié)束線程
Dim row As Data.DataRow
For Each row In ds.Tables(table).Rows
row.Item("SysTime") = Now
Next
Purgestr = "delete " & table
Dim com1 As New SqlClient.SqlCommand(Purgestr, conn1)
com1.ExecuteNonQuery() ‘刪除舊記錄
sa.InsertCommand = combu.GetInsertCommand
sa.Update(ds, table) ' 將新記錄寫(xiě)入數(shù)據(jù)庫(kù)
Else
GoTo Finish ' goto finish and wait another 30 seconds
End If
End Sub
——————————
可以利用如下命令在DOS窗口啟動(dòng)該程序。
Scanservice -i iplist.xml -d dbinfo.xml
2. SCANSERVICE 數(shù)據(jù)庫(kù)
該數(shù)據(jù)庫(kù)保存保存程序運(yùn)行結(jié)果,以便用WEB等方式展現(xiàn)出來(lái)。以下是建立表的腳本,包含域名,用戶(hù)名,機(jī)器名,IP以及服務(wù)狀態(tài)。
CREATE TABLE [dbo].[Service] (
[IP] [varchar] (50) NULL ,
[狀態(tài)] [varchar] (50) NULL ,
[用戶(hù)名] [varchar] (50) NULL ,
[機(jī)器名] [varchar] (50) NULL ,
[MAC地址] [varchar] (50) NULL ,
[域] [varchar] (50) NULL ,
[超時(shí)] [varchar] (10) NULL ,
[時(shí)間安] [DateTime] (8) NULL ,
)
總結(jié):
以上是一個(gè)完整的方法,也是比較簡(jiǎn)單明晰的解決方法,如果要求技巧和性能的話,還有一些地方可以做些改進(jìn),比如對(duì)線程池的使用。另外還有一些方面需要大家自己完成,比如將數(shù)據(jù)庫(kù)中的信息以WEB的方式展現(xiàn)出來(lái)。