ICMP 类(参考文章http://dev.21tx.com/2006/02/23/10773_1.html修改的)
using System;
using System.Collections.Generic;
using System.Text;
namespace ICMPCreate
{
class ICMPPacket
{
private byte _type;//类型
private byte _code;//代码;
private UInt16 _checkSum;//校验和;
private UInt16 _identifiter;//标识符
private UInt16 _seqNumber;//序列号
private byte[] _data;//选项数据
private byte[] bt_buffer;//要发送的数据,构造完成的数据包。
private bool isError = false;//是否出现错误,没有出现错误时为false;
//返回要发送的数据报文;
public byte[] BT_Buffer
{
get { return bt_buffer; }
}
/// <summary>
///在构造icmp的过程中是否出现错误;
/// </summary>
public bool ISError
{
get { return isError; }
}
/// <summary>
/// icmpPacket
/// </summary>
/// <param name="type">icmp类型</param>
/// <param name="code">icmp代码</param>
/// <param name="checkSum">icmp校验和(置为零即可,程序中会计算校验和)</param>
/// <param name="identifier">icmp标志符,自己设定,标志本icmp进程</param>
/// <param name="seqNum">发送icmp的顺序号,在使用ping的时候,第一个为0,第二个可以设置为1...</param>
/// <param name="dataSize">发送选项数据量的大小(偶数奇数均可)</param>
public ICMPPacket(byte type,byte code,UInt16 checkSum,UInt16 identifier,UInt16 seqNum,int dataSize)
{
//初始化数据
_type = type;
_code = code;
_checkSum = checkSum;
_identifiter = identifier;
seqNum = _seqNumber;
_data = new byte [dataSize];
//填充选项数据;
if (dataSize % 2 != 0)
dataSize++;
for (int i = 0; i < dataSize;i++ )
{
_data[i] = (byte)'#';
}
int sendLength = 8 + _data.Length;//icmp头部长度(字节)加上datasize;
bt_buffer = new byte [sendLength];
if (sendLength != CountByte(bt_buffer))
{
isError = true;//出现错误设定为true
}
_checkSum = SumOfCheck(bt_buffer);//计算完校验和了
if (sendLength != CountByte(bt_buffer))
{
isError = true;//出现错误设定为true
}
}
private int CountByte(byte []buffer)
{
byte[] b_type = new byte[1] { _type};
byte[] b_code = new byte[1] { _code};
byte[] b_ckSum = BitConverter.GetBytes (_checkSum);
byte[] b_id = BitConverter.GetBytes(_identifiter);
byte[] b_seqNum = BitConverter.GetBytes(_seqNumber);
int i = 0;
Buffer.BlockCopy(b_type, 0, buffer, i, b_type.Length); //在这里使用Buffer.BlockCopy比Array.Copy好
i += b_type.Length;
Buffer.BlockCopy(b_code, 0, buffer, i, b_code.Length);
i += b_code.Length;
Buffer.BlockCopy(b_ckSum,0,buffer,i,b_ckSum.Length);
i += b_ckSum.Length;
Buffer.BlockCopy(b_id, 0, buffer, i, b_id.Length);
i += b_id.Length;
Buffer.BlockCopy(b_seqNum, 0, buffer, i, b_seqNum.Length);
i += b_seqNum.Length;
Buffer.BlockCopy(_data, 0, buffer, i, _data.Length);
i += _data.Length;
return i;
}
/// <summary>
/// 求校验和,将_buffer中的每两个字节组合成一个16位的数字,放到一个数组中。之后把这个数组中
/// 的数先转换成int类型,再全部相加到sum中。对于产生的余数在sum的高16位,再把sum高16位和低十六
/// 位相加。这里还可能会再次产生余数因此需要再次把sum的第十六位和高十六位再相加一次。最后求反即可。
/// </summary>
/// <param name="_buffer">要计算的数组</param>
/// <returns>校验和结果</returns>
public UInt16 SumOfCheck(byte[] _buffer)
{
int ckSum = 0;//最终存放校验和的变量
int buffer_length = _buffer.Length;
int UintLength = (int)Math.Ceiling((double)buffer_length / 2);//转换成16进制的数组长度
UInt16[] byteToUint16 = new UInt16[UintLength];//存放的位置;
int i = 0, j = 0, highIndex = buffer_length - 2;
if (BitConverter.IsLittleEndian)//littleendian格式,低地址低字节
{
while (i < highIndex)
{
byteToUint16[j] = (UInt16)(_buffer[i]| _buffer[++i]<<8 );//这里在移位后会返回一个int类型的值
j++;
i++;
}
if (buffer_length % 2 == 0)
{
//buffer的长度为偶数时,循环会剩下两个字节
byteToUint16[j] = (UInt16)(_buffer[i] | _buffer[++i]<< 8);
}
else
{
//buffer的长度不为奇数时,循环会剩下一个字节
byteToUint16[j] = (UInt16)(_buffer[i] | (byte)0<<8);
}
}
else
{
while (i < highIndex)//低地址高字节
{
byteToUint16[j] = (UInt16)(_buffer[i]<<8| _buffer[++i] );//这里在移位后会返回一个int类型的值
j++;
i++;
}
if (buffer_length % 2 == 0)
{
//buffer的长度为偶数时,循环会剩下两个字节
byteToUint16[j] = (UInt16)(_buffer[i] << 8 | _buffer[++i]);
}
else
{
//buffer的长度不为奇数时,循环会剩下一个字节
byteToUint16[j] = (UInt16)(_buffer[i] << 8 | (byte)0);
}
}
for (int k = 0; k < UintLength; k++)
{
ckSum += (Int32)byteToUint16[k];//所有数相加;
}
ckSum = (ckSum >> 16) + (ckSum & 0xffff);//把余数也加进去,加两次即可把所有可能的余数加完。可简单证明。
ckSum += ckSum >> 16;//这里就不用管高十六位了,因为下一步会把高十六位给截断。
//求反
return (UInt16)(~ckSum);
}
}
}
测试(对于发回来的数据包暂时还没有处理。)
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace ICMPCreate
{
class Program
{
static void Main(string[] args)
{
ICMPPacket packet = new ICMPPacket(8, 0, 0, 45, 2, 4);
if (packet.ISError == true)
{
//出现错误
Console.WriteLine("Error in create icmp packet!");
Console.Read();
return;
}
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp);
IPHostEntry remote = Dns.GetHostEntry("192.168.2.1");
EndPoint epRemote = (EndPoint)new IPEndPoint(remote.AddressList[0], 0);//远程端点:
byte[] receiveDate = new byte[100];
int startTime = Environment.TickCount;
if (s.SendTo(packet.BT_Buffer, epRemote) == -1)
{
Console.WriteLine("Error in send icmp packet");
Console.Read();
s.Close();
return;
}
int rByte = s.ReceiveFrom(receiveDate, SocketFlags.None, ref epRemote);
int timeout = Environment.TickCount - startTime;
if (rByte == -1)
{
Console.WriteLine("主机没有响应!");
Console.Read();
s.Close();
return;
}
s.Close();
}
}
}