TCP-IP详解卷1-07:Ping程序
2010-11-24 23:07
579 查看
TCP-IP详解卷1-07:Ping程序
Ping目的是为了测试另一台主机是否可达。该程序发送一份ICMP回显请求报文给主机,并等待返回ICMP回显应答。
Ping程序是对两个TCP/IP系统连通性进行测试的基本工具。它只利用ICMP回显请求和回显应答报文,而不用经过传输层( TCP/UDP)。Ping服务器一般在内核中实现ICMP的功能。
一:纯C#代码的Ping程序
public class IcmpPacket
{
public Byte Type; //类型
public Byte SubCode; //代码
public UInt16 CheckSum; //校验和
public UInt16 Identifier; //表示
public UInt16 SequenceNumber; //序列号
public Byte[] Data; //数据
}
//计算校验和函数
//首部检验和字段是根据IP首部计算的检验和码。它不对首部后面的数据进行计算。
//ICMP、IGMP、UDP和TCP在它们各自的首部中均含有同时覆盖首部和数据检验和码。
//为了计算一份数据报的IP检验和,
//首先把检验和字段置为0。计算完校验码后,再用计算后的校验码替代
//然后,对首部中每个16 bit进行二进制反码求和(整个首部看成是由一串16 bit的字组成),结果存在检验和字段中。
//当收到一份IP数据报后,同样对首部中每个16 bit进行二进制反码的求和。
//由于接收方在计算过程中包含了发送方存在首部中的检验和,
//因此,如果首部在传输过程中没有发生任何差错,那么接收方计算的结果应该为全1。
//如果结果不是全1(即检验和错误),那么IP就丢弃收到的数据报。但是不生成差错报文,由上层去发现丢失的数据报并进行重传。
//一般的相加肯定要溢出或进位的,那溢出的部分信息就丢掉了。
//为了保留溢出的信息,需要把进位信息保留下来,也就是移到低位上相加,而这个一般加法是很难实现的,所以需要别的改进后的加法(反码加)。
//想考虑8位时的情况:
//0xF0 + 0xF0 = 0xE0 + 溢出;
//如何保留溢出位,与最低位先加就得到:
//0xF0 + 0xF0 = 0xE1;
//上面运算是错误的,但有什么加法可以时上式成立呢???
//答案就是反码加(计算校验和):
//~0xF0 + (~0xF0) = ~0xE1; ==> (0xF0 +' 0xF0 = ~0xE1 = 0x1E), 0x1E就是校验和。 检验校验和:
//0xF0 +' 0xF0 +' 0x1E = 0xFF; 校验时同时计算校验字节,结果为0xFF,为正确。
public static UInt16 checksum(UInt16[] buffer, int size)
{
Int32 cksum = 0;
while (size > 0)
{
cksum += buffer[counter];
size -= 1;
}
cksum = (cksum >> 16) + (cksum & 0xffff); /* 高位低位相加 */
cksum += (cksum >> 16); /* 上一步溢出时,将溢出位也加到sum中 */
return (UInt16)(~cksum); /* 注意类型转换,现在的校验和为16位 */
}
//将IcmpPacket类的数据填入数组中Buffer,
public static Int32 Serialize(IcmpPacket packet,
Byte[] Buffer,
Int32 PacketSize,
Int32 PingData)
{
Int32 cbReturn = 0;
// serialize the struct into the array
int Index = 0;
Byte[] b_type = new Byte[1]; //类型
b_type[0] = (packet.Type);
Byte[] b_code = new Byte[1]; //代码
b_code[0] = (packet.SubCode);
Byte[] b_cksum = BitConverter.GetBytes(packet.CheckSum); //校验和,初始全0,计算完后,替换
Byte[] b_id = BitConverter.GetBytes(packet.Identifier); //标识符
Byte[] b_seq = BitConverter.GetBytes(packet.SequenceNumber); //流水号
Array.Copy(b_type, 0, Buffer, Index, b_type.Length);
Index += b_type.Length;
Array.Copy(b_code, 0, Buffer, Index, b_code.Length);
Index += b_code.Length;
Array.Copy(b_cksum, 0, Buffer, Index, b_cksum.Length);
Index += b_cksum.Length;
Array.Copy(b_id, 0, Buffer, Index, b_id.Length);
Index += b_id.Length;
Array.Copy(b_seq, 0, Buffer, Index, b_seq.Length);
Index += b_seq.Length;
// copy the data
Array.Copy(packet.Data, 0, Buffer, Index, PingData); //数据
Index += PingData;
if (Index != PacketSize/* sizeof(IcmpPacket) */)
{
cbReturn = -1;
return cbReturn;
}
cbReturn = Index;
return cbReturn;
}
//PING,host对方主机IP或主机名,主要的难度在于计算校验和
public string PingHost(string host)
{
IPHostEntry ServerHE, fromHE;
int nBytes = 0;
int dwStart = 0, dwStop = 0;
//初始化ICMP的Socket
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp); socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 1000); // 得到Server EndPoint
try
{
ServerHE = Dns.GetHostByName(host);
}
catch (Exception)
{
return "没有发现主机";
}
// 把 Server IP_EndPoint转换成EndPoint
IPEndPoint ipepServer = new IPEndPoint(ServerHE.AddressList[0], 0);
EndPoint epServer = (ipepServer);
// 设定客户机的接收Endpoint
fromHE = Dns.GetHostByName(Dns.GetHostName());
IPEndPoint ipEndPointFrom = new IPEndPoint(fromHE.AddressList[0], 0);
EndPoint EndPointFrom = (ipEndPointFrom);
int PacketSize = 0;
IcmpPacket packet = new IcmpPacket();
// 构建要发送的包
packet.Type = 0; //消息应答
packet.SubCode = 0;
packet.CheckSum =0;
packet.Identifier = 45;
packet.SequenceNumber = 0;
int PingData = 24; // sizeof(IcmpPacket) - 8;
packet.Data = new Byte[PingData];
// 初始化Packet.Data
for (int i = 0; i < PingData; i++)
{
packet.Data[i] = (byte)'#';
}
//Variable to hold the total Packet size
PacketSize = 32;
/*************************************中间这段都是为计算校验和****************************************/
//将类填入字节数组中
Byte[] icmp_pkt_buffer = new Byte[PacketSize];
Int32 Index = 0;
Index = Serialize(packet, icmp_pkt_buffer,PacketSize,PingData);
if (Index == -1)
{
return "Error Creating Packet";
}
// 将字节数组转换成UINT16数组
Double double_length = Convert.ToDouble(Index);
Double dtemp = Math.Ceiling(double_length / 2);
int cksum_buffer_length = Index/2;
UInt16[] cksum_buffer = new UInt16[cksum_buffer_length];
int icmp_header_buffer_index = 0;
for (int i = 0; i < cksum_buffer_length; i++)
{
cksum_buffer[i] = BitConverter.ToUInt16(icmp_pkt_buffer, icmp_header_buffer_index);
icmp_header_buffer_index += 2;
}
//计算校验和
//注意,这个代码这处是否问题的,强行进行截断,当Index为奇数时,在进行校验和计算的加和过程中,应该考虑进去
//而此处之所以能成功,主要是因为数据部分都是0,所以加和没加都是一样的
UInt16 u_cksum = checksum(cksum_buffer, cksum_buffer_length);
/*****************************************************************************/
//替换校验和
packet.CheckSum = u_cksum;
Byte[] sendbuf = new Byte[PacketSize];
//将获得校验和的类转换成数组发送
Index = Serialize( packet, sendbuf, PacketSize,PingData);
if (Index == -1)
{
return "Error Creating Packet";
}
dwStart = System.Environment.TickCount;
if ((nBytes = socket.SendTo(sendbuf, PacketSize, 0, epServer)) == SOCKET_ERROR)
{
return "Socket Error: cannot send Packet";
}
Byte[] ReceiveBuffer = new Byte[256];
nBytes = 0;
bool recd = false;
int timeout = 0;
while (!recd)
{
nBytes = socket.ReceiveFrom(ReceiveBuffer, 256, 0, ref EndPointFrom);
if (nBytes == SOCKET_ERROR)
{
return "主机没有响应";
}
else if (nBytes > 0)
{
dwStop = System.Environment.TickCount - dwStart;
return "Reply from " + epServer.ToString() + " in " + dwStop + "ms. Received: " + nBytes + " Bytes.";
}
timeout = System.Environment.TickCount - dwStart;
if (timeout > 1000)
{
return "超时";
}
}
socket.Close();
return "";
}
2: 用到了C#中Ping类的代码
Ping pingClient = new Ping();
pingClient.PingCompleted += new PingCompletedEventHandler(pingClient_PingCompleted);
//Occurs when an asynchronous operation to send an Internet Control Message Protocol (ICMP) echo message
//and receive the corresponding ICMP echo reply message completes or is canceled.
private void pingClient_PingCompleted(object sender, PingCompletedEventArgs e)
{
if (e.Error == null)
{
if (e.Cancelled)
{
pingDetailsTextBox.Text += " Ping cancelled. /r/n";
}
else
{
if (e.Reply.Status == IPStatus.Success)
{
pingDetailsTextBox.Text += " " + e.Reply.Address.ToString() + " " + e.Reply.RoundtripTime.ToString(NumberFormatInfo.CurrentInfo) + "ms" + "/r/n";
}
else
{
pingDetailsTextBox.Text += " " + GetStatusString(e.Reply.Status) + "/r/n";
}
}
}
else
{
pingDetailsTextBox.Text += " Ping error./r/n";
MessageBox.Show("An error occurred while sending this ping. " + e.Error.InnerException.Message);
}
sendButton.Enabled = true;
}
//错误码表
private string GetStatusString(IPStatus status)
{
switch (status)
{
case IPStatus.Success:
return "Success.";
case IPStatus.DestinationHostUnreachable:
return "Destination host unreachable.";
case IPStatus.DestinationNetworkUnreachable:
return "Destination network unreachable.";
case IPStatus.DestinationPortUnreachable:
return "Destination port unreachable.";
case IPStatus.DestinationProtocolUnreachable:
return "Destination protocol unreachable.";
case IPStatus.PacketTooBig:
return "Packet too big.";
case IPStatus.TtlExpired:
return "TTL expired.";
case IPStatus.ParameterProblem:
return "Parameter problem.";
case IPStatus.SourceQuench:
return "Source quench.";
case IPStatus.TimedOut:
return "Timed out.";
default:
return "Ping failed.";
}
}
private void sendButton_Click(object sender, EventArgs e)
{
addressTextBox.SelectAll();
if (addressTextBox.Text.Length != 0)
{
sendButton.Enabled = false;
pingDetailsTextBox.Text += "Pinging " + addressTextBox.Text + " . . ./r/n";
pingClient.SendAsync(addressTextBox.Text, null);
}
else
{
MessageBox.Show("Please enter an IP address or host name.");
}
}
private void cancelButton_Click(object sender, EventArgs e)
{
pingClient.SendAsyncCancel();
}
}
Ping目的是为了测试另一台主机是否可达。该程序发送一份ICMP回显请求报文给主机,并等待返回ICMP回显应答。
Ping程序是对两个TCP/IP系统连通性进行测试的基本工具。它只利用ICMP回显请求和回显应答报文,而不用经过传输层( TCP/UDP)。Ping服务器一般在内核中实现ICMP的功能。
一:纯C#代码的Ping程序
public class IcmpPacket
{
public Byte Type; //类型
public Byte SubCode; //代码
public UInt16 CheckSum; //校验和
public UInt16 Identifier; //表示
public UInt16 SequenceNumber; //序列号
public Byte[] Data; //数据
}
//计算校验和函数
//首部检验和字段是根据IP首部计算的检验和码。它不对首部后面的数据进行计算。
//ICMP、IGMP、UDP和TCP在它们各自的首部中均含有同时覆盖首部和数据检验和码。
//为了计算一份数据报的IP检验和,
//首先把检验和字段置为0。计算完校验码后,再用计算后的校验码替代
//然后,对首部中每个16 bit进行二进制反码求和(整个首部看成是由一串16 bit的字组成),结果存在检验和字段中。
//当收到一份IP数据报后,同样对首部中每个16 bit进行二进制反码的求和。
//由于接收方在计算过程中包含了发送方存在首部中的检验和,
//因此,如果首部在传输过程中没有发生任何差错,那么接收方计算的结果应该为全1。
//如果结果不是全1(即检验和错误),那么IP就丢弃收到的数据报。但是不生成差错报文,由上层去发现丢失的数据报并进行重传。
//一般的相加肯定要溢出或进位的,那溢出的部分信息就丢掉了。
//为了保留溢出的信息,需要把进位信息保留下来,也就是移到低位上相加,而这个一般加法是很难实现的,所以需要别的改进后的加法(反码加)。
//想考虑8位时的情况:
//0xF0 + 0xF0 = 0xE0 + 溢出;
//如何保留溢出位,与最低位先加就得到:
//0xF0 + 0xF0 = 0xE1;
//上面运算是错误的,但有什么加法可以时上式成立呢???
//答案就是反码加(计算校验和):
//~0xF0 + (~0xF0) = ~0xE1; ==> (0xF0 +' 0xF0 = ~0xE1 = 0x1E), 0x1E就是校验和。 检验校验和:
//0xF0 +' 0xF0 +' 0x1E = 0xFF; 校验时同时计算校验字节,结果为0xFF,为正确。
public static UInt16 checksum(UInt16[] buffer, int size)
{
Int32 cksum = 0;
while (size > 0)
{
cksum += buffer[counter];
size -= 1;
}
cksum = (cksum >> 16) + (cksum & 0xffff); /* 高位低位相加 */
cksum += (cksum >> 16); /* 上一步溢出时,将溢出位也加到sum中 */
return (UInt16)(~cksum); /* 注意类型转换,现在的校验和为16位 */
}
//将IcmpPacket类的数据填入数组中Buffer,
public static Int32 Serialize(IcmpPacket packet,
Byte[] Buffer,
Int32 PacketSize,
Int32 PingData)
{
Int32 cbReturn = 0;
// serialize the struct into the array
int Index = 0;
Byte[] b_type = new Byte[1]; //类型
b_type[0] = (packet.Type);
Byte[] b_code = new Byte[1]; //代码
b_code[0] = (packet.SubCode);
Byte[] b_cksum = BitConverter.GetBytes(packet.CheckSum); //校验和,初始全0,计算完后,替换
Byte[] b_id = BitConverter.GetBytes(packet.Identifier); //标识符
Byte[] b_seq = BitConverter.GetBytes(packet.SequenceNumber); //流水号
Array.Copy(b_type, 0, Buffer, Index, b_type.Length);
Index += b_type.Length;
Array.Copy(b_code, 0, Buffer, Index, b_code.Length);
Index += b_code.Length;
Array.Copy(b_cksum, 0, Buffer, Index, b_cksum.Length);
Index += b_cksum.Length;
Array.Copy(b_id, 0, Buffer, Index, b_id.Length);
Index += b_id.Length;
Array.Copy(b_seq, 0, Buffer, Index, b_seq.Length);
Index += b_seq.Length;
// copy the data
Array.Copy(packet.Data, 0, Buffer, Index, PingData); //数据
Index += PingData;
if (Index != PacketSize/* sizeof(IcmpPacket) */)
{
cbReturn = -1;
return cbReturn;
}
cbReturn = Index;
return cbReturn;
}
//PING,host对方主机IP或主机名,主要的难度在于计算校验和
public string PingHost(string host)
{
IPHostEntry ServerHE, fromHE;
int nBytes = 0;
int dwStart = 0, dwStop = 0;
//初始化ICMP的Socket
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp); socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 1000); // 得到Server EndPoint
try
{
ServerHE = Dns.GetHostByName(host);
}
catch (Exception)
{
return "没有发现主机";
}
// 把 Server IP_EndPoint转换成EndPoint
IPEndPoint ipepServer = new IPEndPoint(ServerHE.AddressList[0], 0);
EndPoint epServer = (ipepServer);
// 设定客户机的接收Endpoint
fromHE = Dns.GetHostByName(Dns.GetHostName());
IPEndPoint ipEndPointFrom = new IPEndPoint(fromHE.AddressList[0], 0);
EndPoint EndPointFrom = (ipEndPointFrom);
int PacketSize = 0;
IcmpPacket packet = new IcmpPacket();
// 构建要发送的包
packet.Type = 0; //消息应答
packet.SubCode = 0;
packet.CheckSum =0;
packet.Identifier = 45;
packet.SequenceNumber = 0;
int PingData = 24; // sizeof(IcmpPacket) - 8;
packet.Data = new Byte[PingData];
// 初始化Packet.Data
for (int i = 0; i < PingData; i++)
{
packet.Data[i] = (byte)'#';
}
//Variable to hold the total Packet size
PacketSize = 32;
/*************************************中间这段都是为计算校验和****************************************/
//将类填入字节数组中
Byte[] icmp_pkt_buffer = new Byte[PacketSize];
Int32 Index = 0;
Index = Serialize(packet, icmp_pkt_buffer,PacketSize,PingData);
if (Index == -1)
{
return "Error Creating Packet";
}
// 将字节数组转换成UINT16数组
Double double_length = Convert.ToDouble(Index);
Double dtemp = Math.Ceiling(double_length / 2);
int cksum_buffer_length = Index/2;
UInt16[] cksum_buffer = new UInt16[cksum_buffer_length];
int icmp_header_buffer_index = 0;
for (int i = 0; i < cksum_buffer_length; i++)
{
cksum_buffer[i] = BitConverter.ToUInt16(icmp_pkt_buffer, icmp_header_buffer_index);
icmp_header_buffer_index += 2;
}
//计算校验和
//注意,这个代码这处是否问题的,强行进行截断,当Index为奇数时,在进行校验和计算的加和过程中,应该考虑进去
//而此处之所以能成功,主要是因为数据部分都是0,所以加和没加都是一样的
UInt16 u_cksum = checksum(cksum_buffer, cksum_buffer_length);
/*****************************************************************************/
//替换校验和
packet.CheckSum = u_cksum;
Byte[] sendbuf = new Byte[PacketSize];
//将获得校验和的类转换成数组发送
Index = Serialize( packet, sendbuf, PacketSize,PingData);
if (Index == -1)
{
return "Error Creating Packet";
}
dwStart = System.Environment.TickCount;
if ((nBytes = socket.SendTo(sendbuf, PacketSize, 0, epServer)) == SOCKET_ERROR)
{
return "Socket Error: cannot send Packet";
}
Byte[] ReceiveBuffer = new Byte[256];
nBytes = 0;
bool recd = false;
int timeout = 0;
while (!recd)
{
nBytes = socket.ReceiveFrom(ReceiveBuffer, 256, 0, ref EndPointFrom);
if (nBytes == SOCKET_ERROR)
{
return "主机没有响应";
}
else if (nBytes > 0)
{
dwStop = System.Environment.TickCount - dwStart;
return "Reply from " + epServer.ToString() + " in " + dwStop + "ms. Received: " + nBytes + " Bytes.";
}
timeout = System.Environment.TickCount - dwStart;
if (timeout > 1000)
{
return "超时";
}
}
socket.Close();
return "";
}
2: 用到了C#中Ping类的代码
Ping pingClient = new Ping();
pingClient.PingCompleted += new PingCompletedEventHandler(pingClient_PingCompleted);
//Occurs when an asynchronous operation to send an Internet Control Message Protocol (ICMP) echo message
//and receive the corresponding ICMP echo reply message completes or is canceled.
private void pingClient_PingCompleted(object sender, PingCompletedEventArgs e)
{
if (e.Error == null)
{
if (e.Cancelled)
{
pingDetailsTextBox.Text += " Ping cancelled. /r/n";
}
else
{
if (e.Reply.Status == IPStatus.Success)
{
pingDetailsTextBox.Text += " " + e.Reply.Address.ToString() + " " + e.Reply.RoundtripTime.ToString(NumberFormatInfo.CurrentInfo) + "ms" + "/r/n";
}
else
{
pingDetailsTextBox.Text += " " + GetStatusString(e.Reply.Status) + "/r/n";
}
}
}
else
{
pingDetailsTextBox.Text += " Ping error./r/n";
MessageBox.Show("An error occurred while sending this ping. " + e.Error.InnerException.Message);
}
sendButton.Enabled = true;
}
//错误码表
private string GetStatusString(IPStatus status)
{
switch (status)
{
case IPStatus.Success:
return "Success.";
case IPStatus.DestinationHostUnreachable:
return "Destination host unreachable.";
case IPStatus.DestinationNetworkUnreachable:
return "Destination network unreachable.";
case IPStatus.DestinationPortUnreachable:
return "Destination port unreachable.";
case IPStatus.DestinationProtocolUnreachable:
return "Destination protocol unreachable.";
case IPStatus.PacketTooBig:
return "Packet too big.";
case IPStatus.TtlExpired:
return "TTL expired.";
case IPStatus.ParameterProblem:
return "Parameter problem.";
case IPStatus.SourceQuench:
return "Source quench.";
case IPStatus.TimedOut:
return "Timed out.";
default:
return "Ping failed.";
}
}
private void sendButton_Click(object sender, EventArgs e)
{
addressTextBox.SelectAll();
if (addressTextBox.Text.Length != 0)
{
sendButton.Enabled = false;
pingDetailsTextBox.Text += "Pinging " + addressTextBox.Text + " . . ./r/n";
pingClient.SendAsync(addressTextBox.Text, null);
}
else
{
MessageBox.Show("Please enter an IP address or host name.");
}
}
private void cancelButton_Click(object sender, EventArgs e)
{
pingClient.SendAsyncCancel();
}
}
相关文章推荐
- TCP/IP 详解卷一 - 第6、7、8章 ICMP协议和ping、traceroute程序
- TCP/IP详解之:Ping程序、Traceroute程序
- TCP/IP 第7章 Ping程序
- tcp/ip ---------- ping程序
- 《TCP/IP 详解 卷一》读书笔记-----Ping&Traceroute
- 【TCP/TP详解】第7章 Ping程序
- TCP/IP 详解 卷1 ch7 Ping
- TCP-IP详解卷1-08:Traceroute程序
- TCP IP 详解 1: 笔记 icmp ping traceout
- TCP/IP之ICMP(Internet控制报文协议),Ping程序和Traceroute程序
- 【TCP/IP】C语言实现Ping小程序
- Ping程序在TCP/IP中的应用
- 路由表和三层转发(ping过程)详解(TCP/IP)
- TCP/IP之四:ping程序
- TCP/IP 详解7 Ping指令
- TCP/IP之四:ping程序
- TCP-IP详解卷1-16:BOOTP(Bootstrap Protocol):引导程序协议
- ping源码代阅读(三)面向连接tcp/ip程序
- 【TCP/IP】C语言实现Ping小程序
- TCP/IP数据包结构详解(转载)