C# 自己组装SNMP的Get请求报文

版本 SNMPv2 ,先添加具体内容,最后添加报文长度及类型。

执行以下代码前,请先打开SNMP服务。并接收所有IP的SNMP请求

                string oid = "1.3.6.1.2.1.1.5.0";  //sysName 计算机名

                List<byte> lstOidBytes = new List<byte>();
                string[] mibvals = oid.Split('.');

                #region 将oid转换成byte 这一部分是从别的地方抄的,含义还不是很明白  1.3.6.1.2.1.1.5.0转换出来变成 2b 06 01 02 01 01 05 00
                // Convert the string MIB into a byte array of integer values
                // Unfortunately, values over 128 require multiple bytes
                // which also increases the MIB length
                for (int i = 0; i < mibvals.Length; i++)
                {
                    short temp = Convert.ToInt16(mibvals[i]);
                    if (temp > 127)
                    {
                        lstOidBytes.Add(Convert.ToByte(128 + (temp / 128)));
                        lstOidBytes.Add(Convert.ToByte(temp - ((temp / 128) * 128)));
                    }
                    else
                    {
                        lstOidBytes.Add(Convert.ToByte(temp));
                    }
                }
                lstOidBytes.RemoveRange(0, 2);
                lstOidBytes.Insert(0, 0x2b);
                #endregion


                //添加查询键值对 Pdu中的键值绑定部分(Variable bindings),先添加内容,最后添加长度及类型
                List<byte> lstVariablebindings = new List<byte>();
                lstVariablebindings.Add(0x06);  /类型 OBJECT IDENTIFIER  即用于查询的Oid
                lstVariablebindings.Add(Convert.ToByte(lstOidBytes.Count));//Oid 的长度 
                lstVariablebindings.AddRange(lstOidBytes); //添加OID,即1.3.6.1.2.1.1.5.0
                lstVariablebindings.Add(0x05); //NULL值 查询时Value为空
                lstVariablebindings.Add(0x00); //NULL 空 

                lstVariablebindings.Insert(0, Convert.ToByte(lstVariablebindings.Count)); ////第一对键值对的长度
                lstVariablebindings.Insert(0, 0x30); //第一对键值对的类型为 SEQUENCE

                lstVariablebindings.Insert(0, Convert.ToByte(lstVariablebindings.Count)); //整个 Variable bindings 的长度
                lstVariablebindings.Insert(0, 0x30); //整个 Variable bindings的类型为  SEQUENCE

                //PDU报文部分 先添加内容,最后添加长度及类型
                List<byte> lstPdu = new List<byte>();
                lstPdu.AddRange(BitConverter.GetBytes(new Random().Next())); //请求编号 RequestID 根据该值对返回的数据进行判断

                lstPdu.Insert(0, Convert.ToByte(lstPdu.Count)); //RequestID的长度
                lstPdu.Insert(0, 0x02); //RequestID的类型为INTEGER

                //错误状态 Error Status  在请求时,默认置0
                lstPdu.Add(0x02); //INTEGER
                lstPdu.Add(0x01); //长度1
                lstPdu.Add(0x00); //值为0

                //错误索引 Error Index  在请求时,默认置0
                lstPdu.Add(0x02); //INTEGER
                lstPdu.Add(0x01); //长度1
                lstPdu.Add(0x00); //值为0

                lstPdu.AddRange(lstVariablebindings);

                lstPdu.Insert(0, Convert.ToByte(lstVariablebindings.Count)); //PDU报文部分的长度
                lstPdu.Insert(0, 0xa0); //PDU 类型 get请求 //代码含义:a0 get-request a1 get-next-request a2 get-response a3 set-request a4 trap

                //SNMP报文部分 先添加内容,最后添加长度及类型
                List<byte> lstSNMP = new List<byte>();

                lstSNMP.Add(0x02); //版本号 INTEGER类型
                lstSNMP.Add(0x01); //版本号长度1 
                lstSNMP.Add(0x01); //版本号01,即v2版本

                //Community Name //打开SNMP服务时可以设置,这里使用默认的public
                byte[] communityBytes = Encoding.ASCII.GetBytes("public");

                lstSNMP.Add(0x04); //团体名(Community)类型 OCTET STRING
                lstSNMP.Add(Convert.ToByte(communityBytes.Length));
                lstSNMP.AddRange(communityBytes);

                lstSNMP.AddRange(lstPdu);

                lstSNMP.Insert(0, Convert.ToByte(lstSNMP.Count)); //整个SNMP报文长度
                lstSNMP.Insert(0, 0x30); //整个SNMP报文类型 SEQUENCE类型

                UdpClient udpsender = new UdpClient();

                IPEndPoint ipe = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 161);

                udpsender.Connect(ipe);

                udpsender.Send(lstSNMP.ToArray(), lstSNMP.Count);

                byte[] response = udpsender.Receive(ref ipe);

                //将返回报文以字符串显示出来
                List<string> lst = new List<string>();
                foreach (byte item in response)
                    lst.Add(item.ToString("X").PadLeft(2, '0'));
                txtContent.Text = string.Join(" ", lst.ToArray());

在我的机器上,执行上述方法后,返回 30 43 02 01 01 04 06 70 75 62 6C 69 63 A2 36 02 04 7B A8 56 04 02 01 00 02 01 00 30 28 30 26 06 08 2B 06 01 02 01 01 05 00 04 1A 43 44 4F 43 30 31 30 31 34 2E 63 68 69 63 6F 6E 79 73 71 75 61 72 65 2E 63 64

这里可以简单分析下返回的SNMP报文

30 43 SEQUENCE类型,SNMP报文最开始应该都是固定的30 。 43 为整个报文长度 67个字节

02 01 01 这里是版本号,INTEGER类型 长度1个字节 ,值为1,即v2

04 06 70 75 62 6C 69 63 团体名(community name), OCTET STRING类型,长度6个字节,值为 public

A2 36 PDU类型 2 即 get-response 对get请求的应答 ,长度54字节

02 04 7B A8 56 04 RequestID ,INTEGER类型,长度4个字节,值即为刚才请求时的ID

02 01 00 错误状态,0,表示没有错误,INTEGER类型,1个长度,值为0

02 01 00 错误索引,0,表示没有错误,INTEGER类型,1个长度,值为0

30 28 PDU键值对开始的地方,SEQUENCE类型,长度40个字节

30 26 第一个键值对,SEQUENCE类型,长度38个字节

06 08 2B 06 01 02 01 01 05 00 键 OBJECT IDENTIFIER 类型,即Oid , 长度8个字节, 1.3.6.1.2.1.1.5.0

04 1A 43 44 4F 43 30 31 30 31 34 2E 63 68 69 63 6F 6E 79 73 71 75 61 72 65 2E 63 64 值 OCTET STRING类型,26个字节, 1A后面的内容即是我的计算机名,用ASCII转换出来,为CDOC01014.chiconysquare.cd。说明运行正常。

虽然现在有SnmpSharpNet这个开源项目可以直接使用,但自己从底层写一遍更有利与加深对该协议格式的理解。