目录

Snmp学习总结系列——开篇

Snmp学习总结(一)——Snmp的基本概念

一、SNMP简单概述

1.1、什么是Snmp

1.2、SNMP 和 UDP

1.3、Snmp版本

二、Snmp的实现结构

三、Snmp有关的基本概念

3.1、代理和管理站的模型

3.2、SNMP的操作命令

3.3、SNMP的消息构成

3.4、PDU(协议数据单元)

3.5、MIB(管理信息库)

3.6、OID(Object Identifier)

四、Snmp的初步体验

Snmp学习总结(二)——WinXP安装和配置SNMP

一、安装SNMP

二、配置SNMP

三、测试Snmp

Snmp学习总结(三)——Win7安装和配置SNMP

一、安装SNMP

二、WIN7配置SNMP

2.1、配置接受的社区名称

2.2、配置进行监控的主机的IP地址

三、WIN7系统下SNMP的完整配置  

Snmp学习总结(四)——WinServer2003安装和配置SNMP

一、安装SNMP

二、配置SNMP

2.1、配置【接受的团体名称】

2.2、配置【接受来自下列主机的SNMP数据包】

三、设置防火墙开启UDP 161端口

Snmp学习总结(五)——WindowsServer2008安装和配置SNMP

一、安装SNMP  

二、配置SNMP

2.1、配置【接受的社区名称】

2.2、配置【接受来自下列主机的SNMP数据包】

三、设置防火墙开启UDP 161端口

四、测试SNMP

Snmp学习总结(六)——linux下安装和配置SNMP

一、安装SNMP

1.1、下载Net-SNMP的源代码

1.2、把压缩包传输到远程的Linux服务器

1.3、对源代码包进行解压缩

1.4、通过configure来生成编译规则

1.5、编译和安装

1.6、配置snmpd.conf

1.7、启动snmp服务

1.8、测试SNMP

1.9、开启UDP 161端口的访问权限

二、卸载SNMP

Snmp学习总结(七)——SNMP4J介绍

一、SNMP4J介绍  

二、SNMP4J重要的类和接口介绍

2.1、Snmp类

2.2、PDU类和ScopedPDU类

2.3、Target接口和CommunityTarget类以及UserTarget类

2.4、TransportMapping接口

2.5、Snmp、Target、PDU三者的关系

三、SNMP4J的两种消息发送模式

四、使用SNMP4J实现管理端的步骤

4.1 、初始化

4.2、 构造发送目标

4.3、 构造发送报文

4.4、 构造响应监听对象(异步模式)

4.5、 发送消息

五、使用SNMP4J实现管理端的编程实现

5.1、获取远程计算机的名称

5.2、获得本机的信息


Snmp学习总结系列——开篇

  进入公司以来,一直参与到公司的产品研发工作当中去,在产品研发中有一个监控远程服务器CPU使用率,内存使用情况,硬盘的需求,技术总监提出了使用Snmp协议作为远程监控的技术解决方案,头一次听说Snmp这个东西,我主动请缨,负责攻克使用snmp来实现监控远程服务器的技术难关,经过一个多月的研究,参考了网上的一些资料,总算是有所收获,在此打算写一个关于Snmp的学习总结系列,初步预定有以下内容:

1、Snmp基本概念介绍

2、Snmp协议在各个操作系统下的安装

  • WinXP安装和配置SNMP
  • Win7安装和配置SNMP
  • WinServer2003安装和配置SNMP
  • WindowsServer2008安装和配置SNMP
  • linux下安装和配置SNMP

3、使用Java的开源组件snmp4j实现Snmp的各种功能

Snmp学习总结(一)——Snmp的基本概念

一、SNMP简单概述

1.1、什么是Snmp

  SNMP是英文"Simple Network Management Protocol"的缩写,中文意思是"简单网络管理协议"。SNMP是一种简单网络管理协议,它属于TCP/IP五层协议中的应用层协议,用于网络管理的协议。SNMP主要用于网络设备的管理。由于SNMP协议简单可靠 ,受到了众多厂商的欢迎,成为了目前最为广泛的网管协议。

  SNMP协议主要由两大部分构成:SNMP管理站和SNMP代理。SNMP管理站是一个中心节点,负责收集维护各个SNMP元素的信息,并对这些信息进行处理,最后反馈给网络管理员;而SNMP代理是运行在各个被管理的网络节点之上,负责统计该节点的各项信息,并且负责与SNMP管理站交互,接收并执行管理站的命令,上传各种本地的网络信息。

  SNMP管理站和SNMP代理之间是松散耦合。他们之间的通信是通过UDP协议完成的。一般情况下,SNMP管理站通过UDP协议向SNMP代理发送各种命令,当SNMP代理收到命令后,返回SNMP管理站需要的参数。但是当SNMP代理检测到网络元素异常的时候,也可以主动向SNMP管理站发送消息,通告当前异常状况。

  SNMP的基本思想:为不同种类的设备、不同厂家生产的设备、不同型号的设备,定义为一个统一的接口和协议,使得管理员可以是使用统一的外观面对这些需要管理的网络设备进行管理。通过网络,管理员可以管理位于不同物理空间的设备,从而大大提高网络管理的效率,简化网络管理员的工作。

  SNMP的工作方式:管理员需要向设备获取数据,所以SNMP提供了【读】操作;管理员需要向设备执行设置操作,所以SNMP提供了【写】操作;设备需要在重要状况改变的时候,向管理员通报事件的发生,所以SNMP提供了【Trap】操作。

1.2、SNMP 和 UDP

  SNMP采用UDP协议在管理端和agent之间传输信息。 SNMP采用UDP 161端口接收和发送请求,162端口接收trap,执行SNMP的设备缺省都必须采用这些端口。SNMP消息全部通过UDP端口161接收,只有Trap信息采用UDP端口162。

1.3、Snmp版本

  SNMP目前共有v1,v2,v3这三个版本:

  • SNMP v1是SNMP协议的最初版本,不过依然是众多厂家实现SNMP基本方式。
  • SNMP v2通常被指是基于community的SNMP V2。Community实质上就是密码。
  • SNMP v3 是最新版本的SNMP。它对网络管理最大的贡献在于其安全性。增加了对认证和密文传输的支持。

二、Snmp的实现结构

  在具体实现上,SNMP为管理员提供了一个网管平台(NMS),又称为【管理站】,负责网管命令的发出、数据存储、及数据分析。【被】监管的设备上运行一个SNMP代理(Agent)),代理实现设备与管理站的SNMP通信。

  

  管理站与代理端通过MIB进行接口统一,MIB定义了设备中的被管理对象。管理站和代理都实现了相应的MIB对象,使得双方可以识别对方的数据,实现通信。管理站向代理申请MIB中定义的数据,代理识别后,将管理设备提供的相关状态或参数等数据转换为MIB定义的格式,应答给管理站,完成一次管理操作。

三、Snmp有关的基本概念

3.1、代理和管理站的模型

  Snmp分2种角色:SNMP管理站(manager,我们的本机127.0.0.1)和SNMP代理(agent, 我们要操作的机器,比如 192.168.1.144)。管理站指的是运行了可以执行网络管理任务软件的服务器,通常被称作为网络管理工作站(NMS),NMS负责采样网络中agent的信息,并接受agent的trap。代理是实际网络设备中用来实现SNMP功能的部分。代理在UDP的161端口接收NMS的读写请求消息,管理站在UDP的162端口接收代理的事件通告消息。所以,一旦获取设备的访问权限(community,默认为public),就可以访问设备信息、改写和配置设备参数。由于采用UDP协议,不需要在代理和管理站之间保持连接。

  

3.2、SNMP的操作命令

  SNMP协议之所以易于使用,这是因为它对外提供了三种用于控制MIB对象的基本操作命令。它们是:Get、Set 和 Trap

  1. Get:管理站读取代理者处对象的值。它是SNMP协议中使用率最高的一个命令,因为该命令是从网络设备中获得管理信息的基本方式。
  2. Set:管理站设置代理者处对象的值。它是一个特权命令,因为可以通过它来改动设备的配置或控制设备的运转状态。它可以设置设备的名称,关掉一个端口或清除一个地址解析表中的项等。
  3. Trap: 代理者主动向管理站通报重要事件。它的功能就是在网络管理系统没有明确要求的前提下,由管理代理通知网络管理系统有一些特别的情况或问题 发生了。如果发生意外情况,客户会向服务器的162端口发送一个消息,告知服务器指定的变量值发生了变化。通常由服务器请求而获得的数据由服务器的161 端口接收。Trap 消息可以用来通知管理站线路的故障、连接的终端和恢复、认证失败等消息。管理站可相应的作出处理。

3.3、SNMP的消息构成

  SNMP协议定义了数据包的格式,及网络管理员和管理代理之间的信息交换,它还控制着管理代理的MIB数据对象。因此,可用于处理管理代理定义的各种任务。
    一条SNMP消息由"版本号"、"SNMP共同体名"和"协议数据单元(PDU)"构成,数据包的长度不是固定的。

  

  • 版本识别符(version identifier):用于说明现在使用的是哪个版本的SNMP协议,确保SNMP代理使用相同的协议,每个SNMP代理都直接抛弃与自己协议版本不同的数据报。
  • 团体名(Community Name)团体(community)是基本的安全机制,用于实现SNMP网络管理员访问SNMP管理代理时的身份验证。类似于密码,默认值为 public。团体名(Community name)是管理代理的口令,管理员被允许访问数据对象的前提就是网络管理员知道网络代理的口令。如果把配置管理代理成可以执行Trap命令,当网络管理 员用一个错误的分区名查询管理代理时,系统就发送一个autenticationFailure trap报文。
  • 协议数据单元(PDU)PDU (协议数据单元)是SNMP消息中的数据区, 即Snmp通信时报文数据的载体。PDU指明了SNMP的消息类型及其相关参数

3.4、PDU(协议数据单元)

  PDU (协议数据单元)是SNMP消息中的数据区, 即Snmp通信时报文数据的载体。

3.5、MIB(管理信息库)

  管理信息(MIB)库可以理解成为agent维护的管理对象数据库,MIB中定义的大部分管理对象的状态和统计信息都可以被NMS访问。MIB是一个按照层次结构组织的树状结构,每个被管对象对应树形结构的一个叶子节点,称为一个object,拥有唯一的数字标识符

  MIB数据对象以一种树状分层结构进行组织,这个树状结构中的每个分枝都有一个专用的名字和一个数字形式的标识符。结构树的分枝实际表示的是数据对象的逻 辑分组。而树叶,有时候也叫节点(node),代表了各个数据对象。在结构树中使用子树表示增加的中间分枝和增加的树叶。 
    使用这个树状分层结构,MIB浏览器能够以一种方便而且简洁的方式访问整个MIB数据库。MIB浏览器是这样一种工具,它可以遍历整棵MIB结构树,通常 以图形显示的形式来表示各个分枝和树叶对象。可以通过其数字标识符来查找MIB中的数据对象,这个数字标识符号从结构树的顶部(或根部)开始,直到各个叶 子节点(即数据对象)为止。这种访问方式和文件系统的组织方式一致。两者的主要区别在于文件系统中的路径名可以以绝对也可以以相对方式表示,而MIB数据 对象只能以绝对方式表示,不能使用相对方式。
    每一个节点都有一个对象标识符(OID)来唯一的标识,每个节点用数字和字符两种方式显示,其中对象标识符OID是由句点隔开的一组整数,也就是从根节点 通向它的路径。一个带标号节点可以拥有包含其它带标号节点为它的子树,如果没有子树它就是叶子节点,它包含一个值并被称为对象。比如网络设备名的oid 是.1.3.6.1.2.1.1.5.0,其值为设备名称的字符串。
    网络资源被抽象为对象进行管理。但SNMP中的对象是表示被管资源某一方面的数据变量。对象被标准化为跨系统的类,对象的集合被组织为管理信息库 (MIB)。MIB作为设在代理者处的管理站访问点的集合,管理站通过读取MIB中对象的值来进行网络监控。管理站可以在代理者处产生动作,也可以通过修改变量值改变代理者处的配置。

3.6、OID(Object Identifier)

  每个管理对象都有自己的OID(Object Identifier),管理对象通过树状结构进行组织,OID由树上的一系列整数组成,整数之间用点( . )分隔开,树的叶子节点才是真正能够被管理的对象。

四、Snmp的初步体验

  我们来做一个最简单的Snmp操作:获取一台IP为192.168.1.144的电脑的名称。
     首先要给为192.168.1.144的电脑安装Snmp环境。Window组件的管理监视工具里包含Snmp,只需要安装一下就可以了。具体的安装步骤后面的博文会有详细的介绍,Snmp默认的通信端口是161/162

  接下来需要一个能够进行Snmp通信的工具,比较简单的有snmputil,snmputil下载地址:http://download.csdn.net/detail/aliang0708/4180259

  将下载下来的【snmputil.zip】压缩包解压后就可以得到如下图所示的两个工具了,其中snmputil是我们要用来进行Snmp通信的工具。

  
  snmputil工具的命令规则是snmputil [get|getnext|walk] agent community oid [oid ...]
  [get|getnext|walk]:为消息类型,我们此次进行的操作是get
  agent:指Snmp代理即你想进行操作的网络设备的ip或名称,即192.168.1.144
  community:分区域,即密码,默认是public
  oid:想要操作的MIB数据对象号,设备名称对应的MIB对象号是.1.3.6.1.2.1.1.5.0

  打开命令行窗口,进入snmputil所在路径,键入:snmputil get 192.168.1.144 public .1.3.6.1.2.1.1.5.0
  如果参数都正确,控制台就会显示出192.168.1.144的机器名。如下图所示:

  

  以上就是一些Snmp基本概念介绍和SNMP的初步体验,从下一篇博文开始,将重点介绍Window和Linux下Snmp协议的安装。

Snmp学习总结(二)——WinXP安装和配置SNMP

一、安装SNMP

  今天讲解一下在XP下安装SNMP协议,安装步骤如下:

  

  

  

  

  

  

  

  

  

  到此,XP系统下安装SNMP的工作就算是全部完成了。

二、配置SNMP

  安装好SNMP协议之后,还需要进行"团体名称"的配置,配置步骤如下:

  找到SNMP的服务,如下图所示:

  

  

  点击【添加】按钮添加一个团体名称并设置访问权限,例如:public,访问权限是可读写,另外还需要指明可以接收来自哪些主机发送的SNMP数据包,可以使用具体的IP地址指明,这里为了方便演示,就使用"接受来自任何主机的SNMP数据包"。

  

  到此,Snmp的配置工作就算是全部完成了。

三、测试Snmp

  XP下安装好Snmp之后,我们可以使用snmputil工具简单测试一下,打开命令行窗口,进入snmputil所在路径,键入:snmputil get 192.168.1.144 public .1.3.6.1.2.1.1.5.0
  如果参数都正确,控制台就会显示出192.168.1.144的机器名。如下图所示:

  

  以上就是关于在XP下安装Snmp的过程,在实际的生产环境中肯定不会使用XP系统作为服务器的,因此在XP下安装Snmp了解一下即可。

Snmp学习总结(三)——Win7安装和配置SNMP

一、安装SNMP

  Win7操作系统默认情况下是不安装SNMP服务的,今天讲解一下在Win7操作系统下安装SNMP,具体安装步骤如下:

  

  WIN7操作系统下安装SNMP的步骤如下:

  

  开始安装SNMP

  

  正在安装SNMP

  

  SNMP安装完成

  

二、WIN7配置SNMP

  Win7安装好SNMP后,还需要进行一些简单的配置

2.1、配置接受的社区名称

  配置步骤如下:

  1、找到找到SNMP对应的SNMP Service,如下图所示:

  

  选中SNMP Service→右键→属性,如下图所示:

  

  切换到【安全】选项卡

  

  点击【添加】按钮添加新的社区,如下图所示:

  

  

  经过以上步骤,我们新添加了一个public社区,权限是只读。

2.2、配置进行监控的主机的IP地址

  

  

  到此,WIN7系统下的SNMP的安装和相关配置就全部完成了。

三、WIN7系统下SNMP的完整配置  

  WIN7系统下SNMP的完整配置信息如下:

  

Snmp学习总结(四)——WinServer2003安装和配置SNMP

一、安装SNMP

  今天讲解一下在WinServer2003安装和配置SNMP,具体操作步骤如下:

  找到【控制面板】→【添加或删除程序】

  

  

  

  

  

二、配置SNMP

  WindowsServer2003安装好SNMP后,还需要进行一些简单的配置

2.1、配置【接受的团体名称】

  配置步骤如下:

  【我的电脑】→【管理】,如下图所示:

  

  找到SNMP对应的服务,如下图所示:

  

  选中【SNMP Service】→点击鼠标【右键】→选择【属性】,如下图所示:

  

  切换到【安全】选项卡,可以看到,目前【接受团体名称】列表中没有数据,如下图所示:

  

  点击【添加】按钮添加新的团体名称,如下图所示:

  

  经过以上步骤,新的团体名称就创建好了,如下图所示:

  

2.2、配置【接受来自下列主机的SNMP数据包】

  配置步骤如下图所示:

  

  接受来自下列主机的SNMP数据包添加上192.168.1.144这个IP地址之后,192.168.1.144这台远程监控主机就可以向这台WinServer2003服务器发送SNMP数据包,从而达到远程监控这台WinServer2003服务器的目的。

  SNMP完整的配置信息如下图所示:

  

  到处,WinServer2003下SNMP的安装和以及相关配置就全部完成了。

三、设置防火墙开启UDP 161端口

  远程监控主机是通过服务器UDP 161端口来获取监控数据的,如果服务器不对外开放UDP161端口,那么远程监控主机将无法通过UDP 161端口访问服务器的SNMP服务,如下所示:

  

  所以要设置防火墙对外开放UDP161端口。

  下面设置开启WinServer2003服务器的UDP 161端口的步骤:

  1、【开始】→【设置】→【控制面板】,如下图所示:

  

  2、找到控制面板中的【Windows防火墙】,如下图所示:

  

  双击打开【Windows 防火墙】,如下图所示:

  

  

  

  开启UDP 161端口的对外访问权限之后,此时就可以正常访问了,如下所示:

  

  以上就是关于在WinServer2003安装和配置SNMP的全部内容。

Snmp学习总结(五)——WindowsServer2008安装和配置SNMP

一、安装SNMP  

  在Windows Server 2008以及Windows Server 2008 R2中,SNMP是以一个服务器功能的形式存在的,SNMP的安装步骤如下所示:

  1、打开【开始】→【管理工具】→【服务器管理】

  

  2、服务器管理器-功能-添加功能

  

  选择SNMP服务,下一步

  

  点击【安装】

  

  正在安装

  

  安装完成后,点【关闭】

  

  

  安装完成之后重新启动服务器,使SNMP服务生效,否则不能正确配置SNMP,如果不想重启服务器,重新启动一下SNMP服务也可以,重启SNMP服务方法:【服务器管理器】→【配置】→【服务】→选中【SNMP Service】→【右键】→【重新启动】即可。

  

  

二、配置SNMP

  WindowsServer2008安装好SNMP后,还需要进行一些简单的配置

2.1、配置【接受的社区名称】

  配置步骤如下:

  1、找到找到SNMP对应的SNMP Service,如下图所示:

  

  双击打开【SNMP Service】,切换到【安全】选项,点击【添加】按钮添加新的【接受的社区名称】,如下图所示:

  

  团体权限:只读,社区名称:public,如下图所示:

  

  新添加的【接受的社区名称】如下图所示:

  

2.2、配置【接受来自下列主机的SNMP数据包】

  点击【添加】按钮,弹出【SNMP 服务配置】对话框,在【主机名,IP或IPX 地址(H):】填写监控主机的的IP地址:192.168.1.144

  

  

  接受来自下列主机的SNMP数据包添加上192.168.1.144这个IP地址之后,192.168.1.144这台远程监控主机就可以向这台WinServer2008服务器发送SNMP数据包,从而达到远程监控这台WinServer2008服务器的目的。当然也可以选择【接受来自任何主机的SNMP数据包】,为了安全,建议配置为监控主机的IP地址。

  点击【应用】按钮→【确定】按钮,重启SNMP服务,使配置生效。

三、设置防火墙开启UDP 161端口

  远程监控主机是通过服务器UDP 161端口来获取监控数据的,如果服务器不对外开放UDP161端口,那么远程监控主机将无法通过UDP 161端口访问服务器的SNMP服务,所以要设置防火墙对外开放UDP161端口。

  下面设置开启服务器的UDP 161端口的步骤:
  1、打开防火墙:【开始】→【管理工具】→【高级安全Windows防火墙】,如下图所示:
  

  2、点【入站规则】,再打开右上角的【新建规则】,如下图所示:

  

  3、选择【端口】,点击【下一步】按钮继续下一步操作,如下图所示:

  

  4、选择【UDP】,特定本地端口:161,点击【下一步】按钮继续下一步操作,如下图所示:

  

  5、选中【允许连接】,点击【下一步】按钮继续下一步操作,如下图所示:

  

  6、【域】、【专用】、【公用】三个复选框都勾选,点击【下一步】按钮继续下一步操作,如下图所示:

  

  7、【名称(N)】、【描述】随便填写,点击【完成(F)】按钮完成配置,如下图所示:

  

  

四、测试SNMP

  我们刚才配置了IP为192.168.1.233这台WindowsServer2008服务器接受的IP为192.168.1.144主机的SNMP数据包,因此我们可以在192.168.1.144这台主机上使用Snmputil.exe工具测试IP为192.168.1.233这台WindowsServer2008服务器的SNMP服务。

  执行测试命令:snmputil get 192.168.1.233 public .1.3.6.1.2.1.1.5.0,执行结果如下:

  

  我们成功地在192.168.1.144主机上获取到了192.168.1.233这台WindowsServer2008服务器的计算机名,这就说明了192.168.1.233这台WindowsServer2008服务器上的SNMP服务正常启动,外界也可以正常访问了。

  以上就是针对WindowsServer2008服务器的SNMP的安装和配置过程。

Snmp学习总结(六)——linux下安装和配置SNMP

一、安装SNMP

1.1、下载Net-SNMP的源代码

  选择一个SNMP版本,比如5.7.1,下载地址如下:http://sourceforge.net/projects/net-snmp/files/net-snmp/5.7.1/,如下图所示:

  

  

  下载完成之后得到如下图所示的一个压缩包

  

1.2、把压缩包传输到远程的Linux服务器

  把压缩包使用FTP传输工具传输到远程的Linux服务器

  

  上传成功后在linux的root目录下就可以看到压缩包了,如下图所示:

  

  也可以在linux命令行窗口使用ls命令罗列出Root目录所有的文件进行查看,如下图所示:

  

1.3、对源代码包进行解压缩

  使用命令"tar xzvf net-snmp-5.7.1.tar.gz"对下载的源代码包进行解压缩,如下图所示:

  

  解压成功后得到一个net-snmp-5.7.1文件夹,如下图所示:

  

1.4、通过configure来生成编译规则

  使用命令"cd net-snmp-5.7.1"进入net-snmp-5.7.1目录,如下图所示:

  

  net-snmp-5.7.1目录下的configure是可执行文件,如果想指定程序包的安装路径,那么首先建立相应的文件夹来存放安装信息,可以写成./configure –-prefix=/指定的路径名。参数--prefix用来告诉系统安装信息存放的路径,如果没有指定路径,直接执行./configure,那么程序包都会安装在系统默认的目录下,通常为:/usr/local下

  执行命令"./configure --prefix=/usr/local/snmp --with-mib-modules='ucd-snmp/diskio ip-mib/ipv4InterfaceTable'",注意,以上的--with-mib-modules=ucd-snmp/diskio选项,可以让服务器支持磁盘I/O监控。如下图所示:

  

  回车出现下面问题,可以直接回车而不用回答,系统会采用默认信息,其中日志文件默认安装在/var/log/snmpd.log.数据存贮目录默认存放在/var/net-snmp下
    1、default version of-snmp-version(3): 
    2、System Contact Information (@@no.where)(配置该设备的联系信息):
    3、System Location (Unknown)(该系统设备的地理位置):
    4、Location to write logfile (日志文件位置):
    5、Location to Write persistent(数据存储目录):

  

  1、default version of-snmp-version:

  

  2、Systemcontact information(配置该设备的联系人):

  

  3、System location (该设备的位置):

  

  4、Location to write logfile (日志文件位置): /var/log/snmpd.log

  

  5、Location to Write persistent(数据存储目录): /var/net-snmp

  

1.5、编译和安装

  执行编译并安装"make  &&  make install"命令,如下图所示:

  

  

1.6、配置snmpd.conf

  使用"ls"命令查看/usr/local/snmp目录下是否存在etc目录,如果不存在etc目录,就创建一个,如下图所示:

  

  查看结果显示不存在etc目录,在/usr/local/snmp目录下创建一个etc目录,创建etc目录命令"mkdir /usr/local/snmp/etc",如下图所示:

  

  找到SNMP源码目录(net-snmp-5.7.1)下EXAMPLE.conf文件,如下图所示:

  

  复制EXAMPLE.conf文件到到/usr/local/snmp/etc目录,并重命名为snmpd.conf:"cp EXAMPLE.conf /usr/local/snmp/etc/snmpd.conf"

  

  使用vi编辑器打开snmpd.conf文件:"vi /usr/local/snmp/etc/snmpd.conf",编辑snmpd.conf文件中的内容

  

  按下键盘上的字母【I】或者【Insert】键进入vi编辑器的编辑模式

  

  要配置snmpd.conf中内容如下:

  1、配置允许网络访问

  找到【AGENT BEHAVIOUR】,如下图所示:

  

  修改如下:添加"agentAddress udp:161"配置项,如下图所示:

  

  2、选择v2c SNMP协议的版本

  找到【ACTIVE MONITORING】,如下图所示:

  

  修改如下:

  

  3、设置访问权限

  找到【ACCESS CONTROL】如下图所示:

  

  找到【rocommunity public default -V systemonly】,把 -V systemonly去掉,这是设置访问权限的,去掉后能访问全部,如下图所示:

  

  4、保存snmpd.conf后退出

  按下键盘左上角上的【Esc】键退出vi编辑器的编辑模式,然后输入命令":wq"保存文件并退出,如下图所示:

  

  经过以上的4个步骤,针对SNMP的snmpd.conf文件的配置工作算是全部完成了。

1.7、启动snmp服务

  由于我们刚才修改SNMP的snmpd.conf文件,所以在启动snmp服务之前,先使用命令"ps aux | grep snmp | grep -v grep |awk '{print $2}'| xargs kill"关闭snmp的相关服务,如下图所示:

  

  使用"netstat -an |grep 161"查看snmp的服务是否启动,如下图所示:

  

  启动snmp服务:"/usr/local/snmp/sbin/snmpd -c /usr/local/snmp/etc/snmpd.conf",如下图所示:

  

1.8、测试SNMP

  获取本机的系统名字,使用命令:"snmpget -v 2c -c public localhost sysName.0"或者"snmpget -v 2c -c public 本机的ip地址 sysName.0"或者"snmpget -v 2c -c public 本机的ip地址 .1.3.6.1.2.1.1.5.0"进行测试,

  执行以下的几个命令都可以获取到本机的系统名字:

    snmpget -v 2c -c public localhost sysName.0
    snmpget -v 2c -c public 127.0.0.1 sysName.0
    snmpget -v 2c -c public 192.168.1.229 sysName.0
    snmpget -v 2c -c public localhost .1.3.6.1.2.1.1.5.0
    snmpget -v 2c -c public 127.0.0.1 .1.3.6.1.2.1.1.5.0
    snmpget -v 2c -c public 192.168.1.229 .1.3.6.1.2.1.1.5.0

  如下图所示:

  

  能够正常返回系统的名字就说明SNMP安装配置成功了。也可以使用命令"snmpget --version"查看当前的安装版本号来验证是否安装成功,如果安装成功,则显示当前的安装版本号,如下图所示:

  

1.9、开启UDP 161端口的访问权限

  完成snmpd的配置并且SNMP测试通过之后,要确保Linux的iptables防火墙对外开放了udp 161端口的访问权限,可以使用"iptables –L –n"查看当前iptables规则,如下图所示:

  

  可以看到,目前iptables防火墙并没有对外开放udp 161端口的访问权限,也就是说,此时外面的计算机是无法访问Linux下的SNMP服务的,可以使用"iptables -I INPUT -p udp --dport 161 -j ACCEPT"命令添加UDP 161端口到iptables防火墙中,然后执行"iptables save"命令保存防火墙的更改,如下图所示:

  

  我们可以在window系统下使用Snmputil.exe工具测试对Linux下的SNMP访问,测试Linux的UDP 161端口是否对外开放,如下图所示:

  

  "snmputil get 192.168.1.229 public .1.3.6.1.2.1.1.5.0"命令返回192.168.1.229这台Linux服务器的名字,可以看到,Linux服务器的名称已经正常返回输出到命令行窗口了。

二、卸载SNMP

  如果安装后想卸载SNMP,可以执行"make uninstall"命令,卸载步骤如下:

  1、使用"netstat -an |grep 161"查看snmp服务是否启动

  2、如果已经启动就使用命令"ps aux | grep snmp | grep -v grep |awk '{print $2}'| xargs kill"关闭snmp的相关服务

  3、进入SNMP源码目录(net-snmp-5.7.1)

  4、执行"make uninstall"命令卸载SNMP

  5、删除/usr/local下的snmp目录以及snmp目录里面的所有文件,usr/local/snmp是SNMP的安装路径

  如下图所示:

  

  开始卸载SNMP,如下图所示:

  

  snmp卸载完成,如下图所示:

  

  删除/usr/local下的snmp目录以及snmp目录里面的所有文件,如下图所示:

  

Snmp学习总结(七)——SNMP4J介绍

一、SNMP4J介绍  

  SNMP4J是一个用Java来实现SNMP(简单网络管理协议)协议的开源项目.它支持以命令行的形式进行管理与响应。SNMP4J是纯面向对象设计与SNMP++(用C++实现SNMPv1/v2c/v3)相类似。

  SNMP4J API 提供以下下特性:

  • 支持MD5和SHA验证,DES,3DES,AES128、AES192和AES256加密的SNMPv3。
  • 支持MPv1,MPv2C和MPv3,带执行的可阻塞的信息处理模块。
  • 全部PDU格式。
  • 可阻塞的传输拓扑。支持UPD、TCP、TLS 。
  • 可阻塞的超时模块。
  • 同步和异步请求。
  • 命令发生器以及命令应答器的支持。
  • 基于Apache license的开源免费。
  • JAVA 1.4.1或更高版本(2.0或更高版本需要jdk1.6及以上的支持)。
  • 基于LOG4J记录日志。
  • 使用GETBULK实现Row-based的有效的异步表格获取。
  • 支持多线程。

二、SNMP4J重要的类和接口介绍

2.1、Snmp类

  Snmp类:该类是SNMP4J中最为核心的类。负责SNMP报文的接受和发送。它提供了发送和接收PDU的方法,所有的PDU类型都可以采用同步或者异步的方式被发送

2.2、PDU类和ScopedPDU类

  该类是SNMP报文单元的抽象,其中PDU类适用于SNMPv1和SNMPv2c。ScopedPDU类继承于PDU类,适用于SNMPv3。

2.3、Target接口和CommunityTarget类以及UserTarget类

  对应于SNMP代理的地址信息,包括IP地址和端口号(161)。其中Target接口适用于SNMPv1和SNMPv2c。CommunityTarget类实现了Target接口,用于SNMPv1和SNMPv2c这两个版本,UserTarget类实现了Target接口,适用于SNMPv3。

2.4、TransportMapping接口

  该接口代表了SNMP4J所使用的传输层协议。这也是SNMP4J一大特色的地方。按照RFC的规定,SNMP是只使用UDP作为传输层协议的。而SNMP4J支持管理端和代理端使用UDP或者TCP进行传输。该接口有两个子接口。

2.5、Snmp、Target、PDU三者的关系

  Target代表远程设备或者远程实体、PDU代表管理端同Target通信的数据,Snmp就代表管理者管理功能(其实就是数据的收发)的具体执行者。

  打个比方:Target就是你远方的恋人,PDU就是你们之间传递的情书、而Snmp就是负责帮你寄信收信的邮差。

三、SNMP4J的两种消息发送模式

  SNMP4J支持两种消息发送模式:同步发送模式和异步发送模式

  同步发送模式也称阻塞模式当管理端发送出一条消息之后,线程会被阻塞,直到收到对方的回应或者时间超时。同步发送模式编程较为简单,但是不适用于发送广播消息。

  异步发送模式也称非阻塞模式。当程序发送一条消息之后,线程将会继续执行,当收到消息的回应的时候,程序会对消息作出相应的处理。要实现异步发送模式,需要实例化一个实现了ResponseListener接口的类的对象。ResponseListener接口中有一个名为onResponse的函数。这是一个回调函数,当程序收到响应的时候,会自动调用该函数。由该函数完成对响应的处理。

四、使用SNMP4J实现管理端的步骤

  该部分说明了利用SNMP4J编写SNMP管理端的大致过程,读者在阅读之后会对SNMP4J有一个宏观上的认识。在附录部分,作者给出了一个用SNMP4J开发管理站的样例程序,如果有进一步的需要,请参考附录部分。

4.1 、初始化

  ①、明确SNMP在传输层所使用的协议

    一般情况下,我们都使用使用UDP协议作为SNMP的传输层协议,所以我们需要实例化的是一个DefaultUdpTransportMapping接口对象;

  ②、实例化一个snmp对象

    在此过程中,我们需要将1中实例化的DefaultUdpTransportMapping接口的对象作为参数,穿snmp类的构造函数中。另外,如果实现的SNMPv3协议,我们还需要设置安全机制,添加安全用户等等;

  ③、监听snmp消息

    在此,我们可以调用刚刚实例化的DefaultUdpTransportMapping的接口对象的listen方法,让程序监听snmp消息;

4.2、 构造发送目标

  如果实现的是SNMPv2c或者说SNMPv1,需要实例化一个CommunityTarget对象。如果实现的是SNMPv3程序,则需要实例化一个UserTarget对象。

  之后,我们还需要对实例化的对象做一些设置。如果是CommunityTarget的对象,则需要设置使用的Snmp版本,重传时间和等待时延。如果是UserTarget对象,我们不仅需要设置版本、重传时间、等待时延,还需要设置安全级别和安全名称。

4.3、 构造发送报文

  如果发送的是SNMPv2c或者说SNMPv1的报文,我们需要实例化一个PDU类的对象。如果发送的是SNMPv3的报文,我们则需要实例化一个ScopedPDU类的对象。之后,我们还需要生成一个OID对象,其中包含了我们所需要获取的SNMP对象在MIB库中的ID。然后我们需要将OID和之前生成的PDU对象或者是ScopedPDU对象绑定,并且设置PDU的报文类型(五种SNMP报文类型之一)。

4.4、 构造响应监听对象(异步模式)

  当使用异步模式的时候,我们需要实例化一个实现了ResponseListener接口的对象,作为响应消息的监听对象。在构造该对象的过程中,我们需要重写ResponseListener的OnResponse函数,该函数是一个回调函数,用来处理程序收到响应后的一些操作。

4.5、 发送消息

  当所有上述操作都设置完毕之后,就可以发送消息了。同步模式和异步模式发送消息调用的函数名字均为send,但是两个函数所需参数不一样。同步模式的参数仅为4.2和4.3中构造的目标对象和报文对象,而异步模式还需要4.4中构造的监听对象。

  同步模式发送消息后便等待响应的到达,到达之后会返回一个ResponseEvent对象,该对象中包含了响应的相应信息。

  异步模式发送消息之后便会继续执行,当收到响应消息时便会调用监听对象的OnResponse函数。该函数中的语句便是我们对响应的处理

五、使用SNMP4J实现管理端的编程实现

  ①、设定远程实体

    snmp4j中,用CommunityTarget对象来表示远程实体(要进行snmp消息通信的远程主机,使用snmp的v2版本)

  ②、指定远程实体的地址

    snmp4j中使用Address接口对象来表示,Address对象需要通过实现该接口的类的对象向上转型来实例化

  ③、通过CommunityTarget以及其父接口中提供的setXX方法来设定远程实体的属性,如设定远程实体的snmp共同体属性、远程实体的地址、超时时间、重传次数、snmp版本等

  ④、设定使用的传输协议

    snmp4j中,用TransportMapping接口的对象来表示传输协议(tcp/udp)

  ⑤、调用TransportMapping中的listen()方法,启动监听进程,接收消息,由于该监听进程是守护进程,最后应调用close()方法来释放该进程

  ⑥、创建SNMP对象,用于发送请求PDU

  a、创建请求pdu,即创建PDU类的对象,调用PDU类中的add()方法绑定要查询的OID,调用PDU中的setType()方法来确定该pdu的类型(与snmp中五种操作想对应)

  b、通过PDU的构造方法  public SNMP(TransportMapping transportingMapping),或者其他构造方法来生成pdu,之后调用 ResopnseEvent send(PDU pdu,Target target)发送pdu,该方法返回一个ResponseEvent对象

  ⑦、通过ResponseEvent对象来获得SNMP请求的应答pdu,方法:public PDU getResponse()

  ⑧、通过应答pdu获得mib信息(之前绑定的OID的值),方法:VaribleBinding get(int index)

5.1、获取远程计算机的名称

package me.gacl.snmp;import java.io.IOException;
import org.snmp4j.CommunityTarget;
import org.snmp4j.PDU;
import org.snmp4j.ScopedPDU;
import org.snmp4j.Snmp;
import org.snmp4j.Target;
import org.snmp4j.TransportMapping;
import org.snmp4j.UserTarget;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.event.ResponseListener;
import org.snmp4j.mp.MPv3;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.security.AuthMD5;
import org.snmp4j.security.PrivDES;
import org.snmp4j.security.SecurityLevel;
import org.snmp4j.security.SecurityModels;
import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.security.USM;
import org.snmp4j.security.UsmUser;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultUdpTransportMapping;public class Snmp4jFirstDemo {private Snmp snmp = null;private int version ;public Snmp4jFirstDemo(int version) {try {this.version = version;TransportMapping transport = new DefaultUdpTransportMapping();snmp = new Snmp(transport);if (version == SnmpConstants.version3) {// 设置安全模式USM usm = new USM(SecurityProtocols.getInstance(),new OctetString(MPv3.createLocalEngineID()), 0);SecurityModels.getInstance().addSecurityModel(usm);}// 开始监听消息transport.listen();} catch (IOException e) {e.printStackTrace();}}public void sendMessage(Boolean syn, final Boolean bro, PDU pdu, String addr)throws IOException {// 生成目标地址对象Address targetAddress = GenericAddress.parse(addr);Target target = null;if (version == SnmpConstants.version3) {// 添加用户snmp.getUSM().addUser(new OctetString("MD5DES"),new UsmUser(new OctetString("MD5DES"), AuthMD5.ID,new OctetString("MD5DESUserAuthPassword"),PrivDES.ID, new OctetString("MD5DESUserPrivPassword")));target = new UserTarget();// 设置安全级别((UserTarget) target).setSecurityLevel(SecurityLevel.AUTH_PRIV);((UserTarget) target).setSecurityName(new OctetString("MD5DES"));target.setVersion(SnmpConstants.version3);} else {target = new CommunityTarget();if (version == SnmpConstants.version1) {target.setVersion(SnmpConstants.version1);((CommunityTarget) target).setCommunity(new OctetString("public"));} else {target.setVersion(SnmpConstants.version2c);((CommunityTarget) target).setCommunity(new OctetString("public"));}}// 目标对象相关设置target.setAddress(targetAddress);target.setRetries(5);target.setTimeout(1000);if (!syn) {// 发送报文 并且接受响应ResponseEvent response = snmp.send(pdu, target);// 处理响应System.out.println("Synchronize(同步) message(消息) from(来自) "+ response.getPeerAddress() + "\r\n"+"request(发送的请求):"+ response.getRequest() + "\r\n"+"response(返回的响应):"+ response.getResponse());/*** 输出结果:* Synchronize(同步) message(消息) from(来自) 192.168.1.233/161request(发送的请求):GET[requestID=632977521, errorStatus=Success(0), errorIndex=0, VBS[1.3.6.1.2.1.1.5.0 = Null]]response(返回的响应):RESPONSE[requestID=632977521, errorStatus=Success(0), errorIndex=0, VBS[1.3.6.1.2.1.1.5.0 = WIN-667H6TS3U37]]*/} else {// 设置监听对象ResponseListener listener = new ResponseListener() {public void onResponse(ResponseEvent event) {if (bro.equals(false)) {((Snmp) event.getSource()).cancel(event.getRequest(),this);}// 处理响应PDU request = event.getRequest();PDU response = event.getResponse();System.out.println("Asynchronise(异步) message(消息) from(来自) "+ event.getPeerAddress() + "\r\n"+"request(发送的请求):" + request+ "\r\n"+"response(返回的响应):" + response);}};// 发送报文snmp.send(pdu, target, null, listener);}}public static void main(String[] args) {//Snmp的三个版本号//int ver3 = SnmpConstants.version3;int ver2c = SnmpConstants.version2c;//int ver1 = SnmpConstants.version1;Snmp4jFirstDemo manager = new Snmp4jFirstDemo(ver2c);// 构造报文PDU pdu = new PDU();//PDU pdu = new ScopedPDU();// 设置要获取的对象ID,这个OID代表远程计算机的名称OID oids = new OID("1.3.6.1.2.1.1.5.0");pdu.add(new VariableBinding(oids));// 设置报文类型pdu.setType(PDU.GET);//((ScopedPDU) pdu).setContextName(new OctetString("priv"));try {// 发送消息 其中最后一个是想要发送的目标地址//manager.sendMessage(false, true, pdu, "udp:192.168.1.229/161");//192.168.1.229 Linux服务器manager.sendMessage(false, true, pdu, "udp:192.168.1.233/161");//192.168.1.233 WinServer2008服务器} catch (IOException e) {e.printStackTrace();}}
}

5.2、获得本机的信息

package me.gacl.snmp;import java.io.IOException;
import org.snmp4j.CommunityTarget;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.TransportMapping;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultUdpTransportMapping;/*** <p>ClassName: GetOID<p>* <p>Description:获得本机的信息 <p>* @author xudp* @version 1.0 V* @createTime 2014-9-15 下午04:45:12*/
public class GetOID {public static void main(String[] args) throws Exception{  try{  //设定CommunityTarget   CommunityTarget myTarget = new CommunityTarget();//定义远程主机的地址  //Address deviceAdd = GenericAddress.parse("udp:192.168.1.233/161"); //定义本机的地址   Address localAdd = GenericAddress.parse("udp:localhost/161");//设定远程主机的地址 //myTarget.setAddress(deviceAdd); //设定本地主机的地址myTarget.setAddress(localAdd);//设置snmp共同体   myTarget.setCommunity(new OctetString("public")); //设置超时重试次数   myTarget.setRetries(2); //设置超时的时间  myTarget.setTimeout(5*60);//设置使用的snmp版本 myTarget.setVersion(SnmpConstants.version2c);  //设定采取的协议   TransportMapping transport = new DefaultUdpTransportMapping();//设定传输协议为UDP//调用TransportMapping中的listen()方法,启动监听进程,接收消息,由于该监听进程是守护进程,最后应调用close()方法来释放该进程transport.listen();  //创建SNMP对象,用于发送请求PDUSnmp protocol = new Snmp(transport);  //创建请求pdu,获取mib   PDU request = new PDU(); //调用的add方法绑定要查询的OIDrequest.add(new VariableBinding(new OID("1.3.6.1.2.1.1.1")));  request.add(new VariableBinding(new OID(new int[] {1,3,6,1,2,1,1,2})));//调用setType()方法来确定该pdu的类型request.setType(PDU.GETNEXT);//调用 send(PDU pdu,Target target)发送pdu,返回一个ResponseEvent对象ResponseEvent responseEvent = protocol.send(request, myTarget);  //通过ResponseEvent对象来获得SNMP请求的应答pdu,方法:public PDU getResponse()PDU response=responseEvent.getResponse();  //输出   if(response != null){  System.out.println("request.size()="+request.size());  System.out.println("response.size()="+response.size());//通过应答pdu获得mib信息(之前绑定的OID的值),方法:VaribleBinding get(int index)VariableBinding vb1 = response.get(0);  VariableBinding vb2 = response.get(1);  System.out.println(vb1);  System.out.println(vb2);//调用close()方法释放该进程transport.close();  /*** 输出结果:* request.size()=2response.size()=21.3.6.1.2.1.1.1.0 = Hardware: x86 Family 6 Model 58 Stepping 9 AT/AT COMPATIBLE - Software: Windows 2000 Version 5.1 (Build 2600 Multiprocessor Free)1.3.6.1.2.1.1.2.0 = 1.3.6.1.4.1.311.1.1.3.1.1*/}  }catch(IOException e){  e.printStackTrace();  }  }
}

  以上就是关于SNMP4J的简单介绍和入门,后面的博文中将进行更加详细的介绍。

Snmp学习总结系列相关推荐

  1. Snmp学习总结系列——开篇

    进入公司以来,一直参与到公司的产品研发工作当中去,在产品研发中有一个监控远程服务器CPU使用率,内存使用情况,硬盘的需求,技术总监提出了使用Snmp协议作为远程监控的技术解决方案,头一次听说Snmp这 ...

  2. SQL Server 2008/2012中SQL应用系列及BI学习笔记系列--目录索引

    SQL Server 2008中的一些特性总结及BI学习笔记系列,欢迎与邀月交流. 3w@live.cn  ◆0.SQL应用系列 1.SQL Server 2008中SQL增强之一:Values新用途 ...

  3. BP算法双向传_链式求导最缠绵(深度学习入门系列之八)

    摘要: 说到BP(Back Propagation)算法,人们通常强调的是反向传播,其实它是一个双向算法:正向传播输入信号,反向传播误差信息.接下来,你将看到的,可能是史上最为通俗易懂的BP图文讲解, ...

  4. BizTalk学习笔记系列之二:实例说明如何使用BizTalk

    BizTalk学习笔记系列之二:实例说明如何使用BizTalk --.BizTalk学习笔记系列之二<?XML:NAMESPACE PREFIX = O /> Aaron.Gao,2006 ...

  5. 从0开始学习GitHub系列之「Git 速成」

    从0开始学习GitHub系列之「Git 速成」 糖果果| 2016-06-24 10:55    浏览量(32)    评论(0)   推荐(0) 数据 小编注:[从0开始学习 GitHub]是一个系 ...

  6. 从0开始学习GitHub系列之「认识并加入GitHub」

    从0开始学习GitHub系列之「认识并加入GitHub」 糖果果| 2016-06-16 16:01    浏览量(245)    评论(0)   推荐(0) 数据 小编注:[从0开始学习 GitHu ...

  7. 从0开始学习GitHub系列之「向GitHub 提交代码」

    DevStore首页 >文章 >文章详情 从0开始学习GitHub系列之「向GitHub 提交代码」 糖果果| 2016-06-15 10:57    浏览量(500)    评论(1) ...

  8. 线程的调度、优先级和亲缘性——Windows核心编程学习手札系列之七

    线程的调度.优先级和亲缘性 --Windows核心编程学习手札系列之七 每个线程都拥有一个上下文结构,在线程的内核对象中,记录线程上次运行时该线程的CPU寄存器状态.Windows会每隔20ms左右查 ...

  9. 线程基础知识——Windows核心编程学习手札系列之六

    线程基础知识 --Windows核心编程学习手札系列之六 线程与进程一样由两部分构成:一是线程的内核对象,操作系统用它来对线程实施管理,也是系统用来存放线程统计信息的地方:二是线程堆栈,用于维护线程在 ...

最新文章

  1. C#之foreach语句
  2. 国学精典书籍阅读记录
  3. Stream流思想和常用方法
  4. java虚拟机栈基本内容
  5. SELECT ... FOR UPDATE_手动加行级排他锁_行级写锁_行级独占锁
  6. java jsonobject 转对象_解析JSON中JSONObject的高级使用
  7. 武汉理工大学计算机学院2018复试,武汉理工大学2018年自主招生复试揭秘
  8. Python基础——文件的读写
  9. 【深入理解JVM】引用,句柄,指针分别是什么?
  10. 简单易用的安装文件制作工具NSIS的使用demo示例
  11. 《Android App开发入门与项目实战》资源下载和内容勘误
  12. excel公式编辑器_办公软件操作技巧035:如何在excel中输入n次方
  13. CTF-Web-[极客大挑战 2019]Knife
  14. 【技术贴】解决Eclipse编译java源文件之后没有生成class文件|找不到class文件
  15. 结对编程-李晓彤+凌嘉文
  16. [转载] 过 DNF TP 驱动保护
  17. sublime_字体更换
  18. 联合省选2022游记
  19. cmstop在列表页或者文章页调用自动摘要的办法
  20. Luogu P3014 [USACO11FEB]牛线Cow Line

热门文章

  1. java面向对象-抽象类和接口
  2. SATA、SAS、SSD三种硬盘存储性能数据
  3. 网赚无货源模式,无货源真的可以赚钱么?
  4. 关于45°角度地图坐标的计算原理 - LVin_A
  5. Flink启动报错could not be determined automatically
  6. 学习enscape动画制作渲染之前,这些技巧必看
  7. STM32输入捕获原理与配置
  8. 没错, Python杀死了Excel
  9. java.lang.classcastexception怎么解决_抛出奇怪的java.lang.ClassCastException
  10. Day17-集合(中)Map