2014-06-05 37 views
0

我們正在Delphi XE5中開發一個程序來監視本地網絡中的MFP。對於每個傳入的SNMP陷阱通知,我們能夠確定發送它的MFP是非常重要的。似乎TIdSNMP.ReceiveTrap將自上次調用以來收到的每個通知放入TIdSNMP.Trap.Value數組屬性的元素中; TIdSNMP.Trap.Host包含發送最新陷阱通知的MFP的IP地址。有人可以證實這一點嗎?有什麼方法可以獲得對應於TIdSNMP.Trap.Value其他元素的IP地址嗎?如何使用TIdSNMP從多個來源接收陷阱通知

出於測試目的,我們使用下面的代碼來接收和顯示SNMP陷阱消息:

unit Unit1; 

interface 

uses 
    Winapi.Windows, Winapi.Messages, 
    System.SysUtils, System.Variants, System.Classes, 
    Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, 
    IdBaseComponent, IdComponent, IdUDPBase, IdUDPClient, IdSNMP; 

type 
    TForm1 = class(TForm) 
    Button2: TButton; 
    IdSNMP1: TIdSNMP; 
    Memo1: TMemo; 
    procedure Button2Click(Sender: TObject); 
    private 
    { Private-Deklarationen } 
    public 
    { Public-Deklarationen } 
    end; 

var 
    Form1: TForm1; 

implementation 

{$R *.dfm} 

procedure TForm1.Button2Click(Sender: TObject); 

    procedure FormatTrap(ASnmpInfo : TSnmpInfo); 
    var 
    i : integer; 
    begin 
    Memo1.Lines.Add('{'); 
    with ASnmpInfo do begin 
     Memo1.Lines.Add(Format('Host=%s, ', [Host])); 
     Memo1.Lines.Add(Format('Port=%d, ', [Port])); 
     Memo1.Lines.Add(Format('Enterprise=%s, ', [Enterprise])); 
     Memo1.Lines.Add(Format('GenTrap=%d, ', [GenTrap])); 
     Memo1.Lines.Add(Format('SpecTrap=%d, ', [SpecTrap])); 
     Memo1.Lines.Add(Format('Version=%d, ', [Version])); 
     Memo1.Lines.Add(Format('PDUType=%d, ', [PDUType])); 
     Memo1.Lines.Add(Format('TimeTicks=%d, ', [TimeTicks])); 
     Memo1.Lines.Add(Format('ID=%d, ', [ID])); 
     Memo1.Lines.Add(Format('ErrorStatus=%d, ', [ErrorStatus])); 
     Memo1.Lines.Add(Format('ErrorIndex=%d, ', [ErrorIndex])); 
     Memo1.Lines.Add(Format('Community=%s, ', [Community])); 
     Memo1.Lines.Add(Format('ValueCount=%d, ', [ValueCount])); 
     for i := 0 to ValueCount-1 do begin 
     Memo1.Lines.Add(Format('Value[%d]=%s, ', [i,Value[i]])); 
     Memo1.Lines.Add(Format('ValueOID[%d]=%s, ', [i,ValueOID[i]])); 
     Memo1.Lines.Add(Format('ValueType[%d]=%d, ', [i,ValueType[i]])); 
     end; 
     Memo1.Lines.Add('}'); 
    end; 
    end; 

begin 
    while not Application.Terminated do begin 
    if IdSNMP1.ReceiveTrap() then begin 
     FormatTrap(IdSNMP1.Trap); 
    end; 
    Sleep(100); 
    Application.ProcessMessages(); 
    end; 
end; 

end. 

兩個MFP - 一個柯尼卡美能達BIZHUB C364e 192.168.197.159下和192.168下柯尼卡美能達BIZHUB C364。 197.19 - 已配置爲向運行此程序的計算機發送SNMP陷阱通知。下面是一些示例結果:

{ 
Host=192.168.197.159, 
Port=32884, 
Enterprise=1.3.6.1.4.1.18334, 
GenTrap=6, 
SpecTrap=10, 
Version=0, 
PDUType=164, 
TimeTicks=7792839, 
ID=0, 
ErrorStatus=0, 
ErrorIndex=0, 
Community=public, 
ValueCount=5, 
Value[0]=No Paper, 
ValueOID[0]=1.3.6.1.4.1.18334.1.1.1.2.1.105.2.2, 
ValueType[0]=4, 
Value[1]=No Paper, 
ValueOID[1]=1.3.6.1.4.1.18334.1.1.1.2.1.105.2.2, 
ValueType[1]=4, 
Value[2]=No Paper, 
ValueOID[2]=1.3.6.1.4.1.18334.1.1.1.2.1.105.2.2, 
ValueType[2]=4, 
Value[3]=No Paper, 
ValueOID[3]=1.3.6.1.4.1.18334.1.1.1.2.1.105.2.2, 
ValueType[3]=4, 
Value[4]=Job End, 
ValueOID[4]=1.3.6.1.4.1.18334.1.1.1.2.1.105.2.2, 
ValueType[4]=4, 
} 

{ 
Host=192.168.197.19, 
Port=53365, 
Enterprise=1.3.6.1.4.1.18334, 
GenTrap=6, 
SpecTrap=10, 
Version=0, 
PDUType=164, 
TimeTicks=12469234, 
ID=0, 
ErrorStatus=0, 
ErrorIndex=0, 
Community=public, 
ValueCount=6, 
Value[0]=No Paper, 
ValueOID[0]=1.3.6.1.4.1.18334.1.1.1.2.1.105.2.2, 
ValueType[0]=4, 
Value[1]=No Paper, 
ValueOID[1]=1.3.6.1.4.1.18334.1.1.1.2.1.105.2.2, 
ValueType[1]=4, 
Value[2]=No Paper, 
ValueOID[2]=1.3.6.1.4.1.18334.1.1.1.2.1.105.2.2, 
ValueType[2]=4, 
Value[3]=No Paper, 
ValueOID[3]=1.3.6.1.4.1.18334.1.1.1.2.1.105.2.2, 
ValueType[3]=4, 
Value[4]=Job End, 
ValueOID[4]=1.3.6.1.4.1.18334.1.1.1.2.1.105.2.2, 
ValueType[4]=4, 
Value[5]=Job End, 
ValueOID[5]=1.3.6.1.4.1.18334.1.1.1.2.1.83.3.1, 
ValueType[5]=4, 
} 

1.3.6.1.4.1.18334.1.1.1.2.1.105.2.2是對的sysObjectID和C364e的1.3.6.1.4.1.18334.1.1.1.2.1.83.3.1 SysObjectID for C364。因此,似乎除了最後一個通知之外,所有通知都來自192.168.197.159下的MFP,儘管最後6個通知TIdSNMP.Trap.Host包含值192.168.197.19。

回答

2

您描述的內容看起來像是TIdSNMP.ReceiveTrap()中的一個錯誤。它不會每次調用TIdSNMP.Trap.Clear()。它不應該保持一段時間以來收到的陷阱的歷史。我剛剛檢查了Indy的SVN。如果你不想,或不能,升級您的印地安裝,簡單的解決方法是調用IdSNMP1.ReceiveTrap()之前調用IdSNMP1.Trap.Clear()

while not Application.Terminated do begin 
    IdSNMP1.Trap.Clear(); // <-- add this 
    if IdSNMP1.ReceiveTrap() then begin 
    FormatTrap(IdSNMP1.Trap); 
    end; 
    Sleep(100); 
    Application.ProcessMessages(); 
end; 

隨着中說,使用一個繁忙的循環中的UI點擊處理程序是不是好主意。我建議你使用TTimer代替:

unit Unit1; 

interface 

uses 
    Winapi.Windows, Winapi.Messages, 
    System.SysUtils, System.Variants, System.Classes, 
    Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, 
    Vcl.ExtCtrls, IdBaseComponent, IdComponent, IdUDPBase, IdUDPClient, IdSNMP; 

type 
    TForm1 = class(TForm) 
    Button2: TButton; 
    IdSNMP1: TIdSNMP; 
    Memo1: TMemo; 
    Timer1: TTimer; 
    procedure Button2Click(Sender: TObject); 
    procedure Timer1Timer(Sender: TObject); 
    private 
    { Private-Deklarationen } 
    public 
    { Public-Deklarationen } 
    end; 

var 
    Form1: TForm1; 

implementation 

{$R *.dfm} 

procedure TForm1.Button2Click(Sender: TObject); 
begin 
    Timer1.Interval := 100; 
    Timer1.Enabled := True; 
end; 

procedure TForm1.Timer1Timer(Sender: TObject); 

    procedure FormatTrap(ASnmpInfo : TSnmpInfo); 
    var 
    i : integer; 
    begin 
    Memo1.Lines.BeginUpdate; 
    try 
     Memo1.Lines.Add('{'); 
     Memo1.Lines.Add(Format('Host=%s, ', [ASnmpInfo.Host])); 
     Memo1.Lines.Add(Format('Port=%d, ', [ASnmpInfo.Port])); 
     Memo1.Lines.Add(Format('Enterprise=%s, ', [ASnmpInfo.Enterprise])); 
     Memo1.Lines.Add(Format('GenTrap=%d, ', [ASnmpInfo.GenTrap])); 
     Memo1.Lines.Add(Format('SpecTrap=%d, ', [ASnmpInfo.SpecTrap])); 
     Memo1.Lines.Add(Format('Version=%d, ', [ASnmpInfo.Version])); 
     Memo1.Lines.Add(Format('PDUType=%d, ', [ASnmpInfo.PDUType])); 
     Memo1.Lines.Add(Format('TimeTicks=%d, ', [ASnmpInfo.TimeTicks])); 
     Memo1.Lines.Add(Format('ID=%d, ', [ASnmpInfo.ID])); 
     Memo1.Lines.Add(Format('ErrorStatus=%d, ', [ASnmpInfo.ErrorStatus])); 
     Memo1.Lines.Add(Format('ErrorIndex=%d, ', [ASnmpInfo.ErrorIndex])); 
     Memo1.Lines.Add(Format('Community=%s, ', [ASnmpInfo.Community])); 
     Memo1.Lines.Add(Format('ValueCount=%d, ', [ASnmpInfo.ValueCount])); 
     for i := 0 to ASnmpInfo.ValueCount-1 do begin 
     Memo1.Lines.Add(Format('Value[%d]=%s, ', [i,ASnmpInfo.Value[i]])); 
     Memo1.Lines.Add(Format('ValueOID[%d]=%s, ', [i,ASnmpInfo.ValueOID[i]])); 
     Memo1.Lines.Add(Format('ValueType[%d]=%d, ', [i,ASnmpInfo.ValueType[i]])); 
     end; 
     Memo1.Lines.Add('}'); 
    finally 
     Memo1.Lines.EndUpdate; 
    end; 
    end; 

begin 
    IdSNMP1.Trap.Clear(); 
    if IdSNMP1.ReceiveTrap() then begin 
    FormatTrap(IdSNMP1.Trap); 
    end; 
end; 

end. 
+0

非常感謝你的解釋和解決方法/修復!有一點我仍然不太清楚:如果192.168.197.159和192.168.197.19各自在相同的100毫秒間隔內發送陷阱通知會發生什麼? TIdSNMP只是放棄第一個,或者我可以調用TIdSNMP.ReceiveTrap兩次來接收這兩條消息? – Tim

+0

你必須爲每個單獨的陷阱調用'ReceiveTrap()'。是否放棄陷阱取決於操作系統,而不是Indy。 SNMP使用UDP。如果在從中讀取數據包之前,套接字的入站緩衝區已滿,則隨後的UDP數據包將被操作系統丟棄,直到緩衝區清除。但是,除非你在短時間內獲得大量的陷阱,否則這種睡眠時間間隔很小就不太可能發生。 –

+0

你可以嘗試的一件事是隻要套接字在入站緩衝區中仍然有未決數據,你的循環/定時器就調用'ReceiveTrap()'在一個循環中,然後只在緩衝區已經耗盡時才休眠。 'TIdSNMP'沒有公開對'fTrapRecvBinding'成員的訪問,所以你必須公開它,然後你可以調用它的'Readable()'方法(這就是'ReceiveTrap()'調用的)。或者在一個簡短的'RecceiveTimeout'循環中調用'ReceiveTrap()',並在返回false時中斷循環。 –