发信人: SharperC.bbs@bbs.sjtu.edu.cn (Wind 疾风步 Walker), 信区: DotNet
标 题: 【原创】c#多线程ping实战
发信站: 饮水思源 (Thu May 5 14:03:22 2005)
转信站: USTC!bbsnews.sdu!SJTU
终于还是弄出来了
关于发ICMP包进行ping,以检测网络各主机的连接状况
网上已有相关的C#程序
但是给出的代码是不能正常工作的,原因在于阻塞式socket的
sendto方法,当目标机不存在时,会挂起ping线程
第二,多线程下,对于收到的packet没有检查sequence,这样
就可能把ping主机a的ECHO当作ping主机b的ECHO,起不到监视
指定网络连接的作用
参考C/C++中的socket编程,作了如下改动,
1)通过SetSocketOption函数设置发送、接收超时
2)对于收到的字节流,提取其中的sequence,并与发送该包的sequence
作比较,以判断是否对应;具体在我的程序中,是添加了
public UInt16 GetSequenceNum(Byte[] buffer)函数。
测试该程序,(ping函数返回-1表示链路不通,0表示通畅)
开了8个线程ping,可以正常工作,完整代码如下:
【附C#实现ping的其他方法及优缺点】
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace pingConsole
{
/// <summary>
/// Class1 的摘要说明。
/// </summary>
class Class1
{
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main(string[] args)
{
//
// TODO: 在此处添加代码以启动应用程序
//
string[] target1 = {"127.0.0.1"};
string[] target2 = {"10.253.0.100"};
// string[] target3 = {"127.0.0.1"};
// string[] target4 = {"10.254.0.100"};
// string[] target5 = {"127.0.0.1"};
// string[] target6 = {"10.254.0.100"};
ThreadPing tp1, tp2, tp3, tp4, tp5, tp6, tp7, tp8;
tp1 = new ThreadPing(target1, 400);
tp2 = new ThreadPing(target2, 400);
tp3 = new ThreadPing(target1, 400);
tp4 = new ThreadPing(target2, 400);
tp5 = new ThreadPing(target1, 400);
tp6 = new ThreadPing(target2, 400);
tp7 = new ThreadPing(target1, 400);
tp8 = new ThreadPing(target2, 400);
new Thread(new ThreadStart(tp1.PingWorker)).Start();
new Thread(new ThreadStart(tp2.PingWorker)).Start();
new Thread(new ThreadStart(tp3.PingWorker)).Start();
new Thread(new ThreadStart(tp4.PingWorker)).Start();
new Thread(new ThreadStart(tp5.PingWorker)).Start();
new Thread(new ThreadStart(tp6.PingWorker)).Start();
new Thread(new ThreadStart(tp7.PingWorker)).Start();
new Thread(new ThreadStart(tp8.PingWorker)).Start();
}
}
public class ThreadPing
{
private string[] _target;
private int _timeOutVal;
public ThreadPing(string[] target, int timeOutVal)
{
_target = target;
_timeOutVal = timeOutVal;
}
public void PingWorker()
{
for(;;)
{
int retval= IcmpPacket.PingHost(_target, _timeOutVal);
Console.WriteLine(_target[0] + " status:" +
retval.ToString());
Thread.Sleep(500);
}
}
}
public class IcmpPacket
{
private Byte _type;
private Byte _subCode;
private UInt16 _checkSum;
private UInt16 _identifier;
private UInt16 _sequenceNumber;
private Byte[] _data;
private static UInt16 UInt16_Seq = 0;
public IcmpPacket(Byte type, Byte subCode, UInt16 checkSum, UInt16
identifier,
UInt16 sequenceNumber, int dataSize)
{
_type = type;
_subCode = subCode;
_checkSum = checkSum;
_identifier = identifier;
_sequenceNumber = sequenceNumber;
_data = new Byte[dataSize];
for (int i=0; i<dataSize; i++)
{
_data[i] = (Byte) '#';
}
}
public UInt16 CheckSum
{
get
{
return _checkSum;
}
set
{
_checkSum = value;
}
}
public int ConverToByte(Byte[] buffer)
{
Byte[] b_type = new Byte[1] {_type};
Byte[] b_subCode = new Byte[1]{_subCode};
Byte[] b_cksum = BitConverter.GetBytes(_checkSum);
Byte[] b_id = BitConverter.GetBytes(_identifier);
Byte[] b_seq = BitConverter.GetBytes(_sequenceNumber);
int i = 0;
Array.Copy(b_type, 0, buffer, i, b_type.Length);
i += b_type.Length;
Array.Copy(b_subCode, 0, buffer, i, b_subCode.Length);
i += b_subCode.Length;
Array.Copy(b_cksum, 0, buffer, i, b_cksum.Length);
i += b_cksum.Length;
Array.Copy(b_id, 0, buffer, i, b_id.Length);
i += b_id.Length;
Array.Copy(b_seq, 0, buffer, i, b_seq.Length);
i += b_seq.Length;
Array.Copy(_data, 0, buffer, i, _data.Length);
i += _data.Length;
return i;
}
public UInt16 GetSequenceNum(Byte[] buffer)
{
UInt16 i_seq = 0;
try
{
i_seq = BitConverter.ToUInt16(buffer, 26);
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
}
return i_seq;
}
public static UInt16 SumofCheck (UInt16[] buffer)
{
int sum = 0;
for (int i=0; i<buffer.Length; i++)
sum += (int)buffer[i];
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
return (UInt16) (~sum);
}
public static int PingHost(string[] hostclient, int timeOutVal)
{
const int ICMP_ECHO = 8;
const int PING_ERR = -1;
const int TIME_OUT = 200;
int retval = 0;
Socket sock = new Socket(AddressFamily.InterNetwork,
SocketType.Raw, ProtocolType.Icmp);
sock.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.SendTimeout, TIME_OUT);
sock.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.ReceiveTimeout, TIME_OUT);
IPHostEntry hostInfo = null;
try
{
IPAddress ipaddr = IPAddress.Parse(hostclient[0]);
hostInfo = Dns.GetHostByName(hostclient[0]);
}
catch(Exception)
{
retval = PING_ERR;
goto exit;
}
EndPoint hostPoint = (EndPoint) new
IPEndPoint(hostInfo.AddressList[0], 0);
IPHostEntry clientInfo = null;
try
{
if (hostclient.Length > 1)
clientInfo = Dns.GetHostByAddress(hostclient[1]);
else
clientInfo = Dns.GetHostByName(Dns.GetHostName());
}
catch (Exception)
{
retval = PING_ERR;
goto exit;
}
EndPoint clientPoint = (EndPoint) new
IPEndPoint(clientInfo.AddressList[0], 0);
int dataSize = 32;
int packetSize = dataSize + 8;
IcmpPacket packet = new IcmpPacket(ICMP_ECHO, 0, 0, 45,
UInt16_Seq++, dataSize);
Byte[] buffer = new Byte[packetSize];
int index = packet.ConverToByte(buffer);
if (index != packetSize)
{
retval = PING_ERR;
goto exit;
}
int count = (int)Math.Ceiling( (Double)index/2);
UInt16[] buffer2 = new UInt16[count];
index = 0;
for (int i=0; i<count; i++)
{
buffer2[i] = BitConverter.ToUInt16(buffer, index);
index += 2;
}
packet.CheckSum = IcmpPacket.SumofCheck(buffer2);
Byte[] sendData = new Byte[packetSize];
index = packet.ConverToByte(sendData);
if (index != packetSize)
{
retval = PING_ERR;
goto exit;
}
for (int i=0; i<5; i++)
{
int nBytes = 0;
int startTime = Environment.TickCount;
try
{
if ((nBytes = sock.SendTo(sendData, packetSize,
SocketFlags.None, (EndPoint)hostPoint)) == -1)
{
retval = PING_ERR;
goto exit;
}
// Console.WriteLine(nBytes.ToString());
}
catch(Exception ex)
{
Console.WriteLine("Send time out");
return -1;
}
Byte[] receiveData = new Byte[256];
nBytes = 0;
int timeout = 0;
int timeConsume = 0;
while (true)
{
try
{
nBytes = sock.ReceiveFrom(receiveData, 256,
SocketFlags.None, ref (EndPoint)clientPoint);
if (nBytes > 0)
{
if (packet._sequenceNumber ==
packet.GetSequenceNum(receiveData))
{
retval = 0;
goto exit;
}
}
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
return -1;
}
if (nBytes == -1)
{
retval = PING_ERR;
goto exit;
}
else if (nBytes > 0)
{
timeConsume = System.Environment.TickCount -
startTime;
retval = timeConsume;
}
timeout = System.Environment.TickCount - startTime;
if (timeout > timeOutVal)
{
retval = PING_ERR;
goto exit;
}
}
}
exit: sock.Close();
return retval;
}
}
}
【C#实现ping的其他方法】
1。调用cmd
这个比较简单,调用系统的command中ping.exe
输入输出重定向,以获取结果字符流,并进行分析
但是由于操作中每次ping需要开关进程操作,运行时间长后
系统资源不足,最终会出现无法进一步开启cmd的局面
2。用c/c++写dll,c#调用
原理也是通过ICMP包收发,只是存在指针,
对包头的数据结构操作比较方便
--
/ _.-. .-/,___| _-| / / //|_/ | `-._
`-' f/ | / __/ /__ / |__/ |
`-'
※ 来源:·饮水思源 bbs.sjtu.edu.cn·[FROM: 211.80.41.99]
|