您的位置:首页 > 理论基础 > 计算机网络

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();

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: