一个周末一直在进一步研究项目的管理端和代理端通信问题,早上终于有了点眉目,做个笔记。
实验的是一个远程控制代理端关机的功能。
管理端是用Java搭的前台网站,所以使用SNMP4J包编写程序。代理端使用agent++和snmp++两个开发包。
管理端的代码如下:
public static void main(String[] args) {
Snmp snmp;
try {
//设置TransportMapping
TransportMapping transport = new DefaultUdpTransportMapping();
snmp = new Snmp(transport);
transport.listen();
//设置target
CommunityTarget target = new CommunityTarget();
target.setCommunity(new OctetString("public"));
target.setAddress(GenericAddress.parse(("10.150.0.84/161"));
target.setRetries(2);
target.setTimeout(5000);
target.setVersion(SnmpConstants.version1);
//设置请求PDU
PDU requestPDU = new PDU();
requestPDU.setType(PDU.SET);//设置PDU类型
//设置Variable
//Gauge32型 无符号长整型
requestPDU.add(new VariableBinding(new OID("1.3.6.1.4.1.502.1.70"), new Gauge32(Integer.valueOf("1"))));
//处理返回结果
ResponseEvent respEvnt = snmp.send(requestPDU,target); //向Agent发送PDU,并接收Response
PDU responsePDU = respEvnt.getResponse();//解析response
if(responsePDU.getErrorIndex() == PDU.noError
&& responsePDU.getErrorStatus() == PDU.noError) {
//如果接收包没有出错
String strResult = responsePDU.getVariableBindings().firstElement().toString();
int len = strResult.indexOf("=");
strResult = strResult.substring(len+1, strResult.length()).trim();
if(strResult.equals(value)){ //如果返回值与设置值相同,返回true,否则,返回false
System.out.println("set操作成功!");
} else{
System.out.println("set操作失败!oid值与设置值不同!");
} else{
System.out.println("set操作失败,接收包有错误:"+responsePDU.getErrorStatusText());
}
snmp.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
代理端比较复杂,主要代码如下:
//Subagent_Main.h
#include "StdAfx.h"
#include <windows.h>
#include <agent_pp/agent++.h>
#include <agent_pp/mib.h>
#include <agent_pp/mib_context.h>
#include <agentx_pp/agentx_subagent.h>
#define GROUP_OID "1.3.6.1.4.1.502.1" //定义oid组
#define SHUTDOWN_OID "1.3.6.1.4.1.502.1.70" //定义关机操作的oid
class Subagent_Main : public MibGroup {
public:
Subagent_Main(const NS_SNMP OctetStr&, SubAgentXMib*);
~Subagent_Main();
};
class Subagent_Shutdown : public MibLeaf {
public:
Subagent_Shutdown();
virtual ~Subagent_Shutdown();
virtual int set(const Vbx&);
};
实现:
//Subagent_Main.cpp
#include "stdafx.h"
#include "Subagent_Main.h"
Subagent_Main::Subagent_Main(const NS_SNMP OctetStr&, SubAgentXMib*)
:MibGroup(GROUP_OID, "Subagent") {
add(new Subagent_Shutdown());
}
Subagent_Main::~Subagent_Main() {
}
Subagent_Shutdown::Subagent_Shutdown()
: MibLeaf(SHUTDOWN_OID, READWRITE, new Gauge32()) {
}
Subagent_Shutdown::~Subagent_Shutdown() {
}
Subagent_Shutdown::set(const Vbx& vb) {
int isShutdown;
vb.get_value(isShutdown);
if(isShutdown) {
//以下关机代码只对Win2000及以上版本系统有效
HANDLE hToken;
TOKEN_PRIVILEGES tkp;
if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hToken)) {
AfxMessageBox("无法打开存取命令");
return 1;
}
LookupPrivilegeValue(NULL,SE_SHUTDOWN_NAME,&tkp.Privileges[0].Luid);
tkp.PrivilegeCount=1;
tkp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken,FALSE,&tkp,0,(PTOKEN_PRIVILEGES)NULL,0);
if(GetLastError()!=ERROR_SUCCESS) {
AfxMessageBox("无法关机");
return 1;
}
if(!ExitWindowsEx(EWX_POWEROFF|EWX_FORCE,0)) {
AfxMessageBox("无法关机");
return 1;
}
}
return 0;
}
测试函数内容如下:
先定义处理的线程函数Subagent:
UINT Subagent(LPVOID pPar) {
//AfxMessageBox("子代理线程启动");
SubAgentXMib* mib = new SubAgentXMib();
AgentXSlave* agentx = new AgentXSlave();
AgentXRequestList* reqList = new AgentXRequestList(agentx);
// register requestList for outgoing requests
mib->set_request_list(reqList);
mib->add(new Subagent_Main("", mib)); //初始化
mib->init();
Request* req;
while (TRUE) {
req = reqList->receive(20000);
if (req) {
mib->process_request(req);
} else {
mib->ping_master(); // ping the master
}
}
//AfxMessageBox("子代理线程结束");
delete mib;
delete agentx;
return 0;
}
最后在main函数里加入到开启线程函数即可:
AfxBeginThread(Subagent, NULL); //开启子代理线程监听
运行时,需要代理端装有snmp服务,但不能开启(一直不明白原因)。代理端先开启MasterAgent,再启动我们所写的代理Subagent。这时候管理端即可发控制请求。一运行,代理端即关机。
PS:大致流程如此,中间有很多细节需要慢慢调试。比如snmp++,agent++源码的下载,dll文件的编译生成等等。
下面仅附上SNMP4J开发包。