.net core中有Ping类可以实现Ping功能;Ping.SendPingAsync();
构建ICMP协议数据包:
/// <summary>
/// 构建ICMP Ping包
/// </summary>
byte[] BuildIcmpPacket(string dataMsg="hello")
{
var dataBody = Encoding.ASCII.GetBytes(dataMsg);
var fixHeadBytes=new byte[] {
0x08, //Type: 回送请求和回送回答报文: 请求报文:回复Type =0
0x00, //SubCode:固定=0
0x00,//检验和(2字节)
0x00,
0x00,//标识符
0x01,
0x00,//序号
0x01
};
//最后构成icmp包
byte[] icmpPacket = new byte[fixHeadBytes.Length + dataBody.Length];
Array.Copy(fixHeadBytes, 0, icmpPacket, 0, fixHeadBytes.Length);
Array.Copy(dataBody, 0, icmpPacket, fixHeadBytes.Length, dataBody.Length);
//计算校验和
byte[] chkSumBytes = IcmpCheckSum(icmpPacket);
Array.Copy(chkSumBytes, 0, icmpPacket,2, chkSumBytes.Length);
return icmpPacket;
}
/// <summary>
/// ICMP 协议中的检验和
/// </summary>
/// <returns>返回2个字节长度的检验和,低位在前,高位在后</returns>
/// <remarks>
/// 1:) 将每两个字节(16位)相加,若出现最后还剩一个字节继续与前面结果相加
/// 2:) 将高16位与低16位相加,直到高16位为0为止。最后的结果取反
/// </remarks>
byte[] IcmpCheckSum(byte[] packets)
{
#region 每两个字节一组
int dataIndex = 0;
int targetIndex = packets.Length / 2;
int sumLength= (int)Math.Ceiling(packets.Length / 2d);
uint[] sumBytes = new uint[sumLength];// 用于计算校验和
for (int i = 0; i < targetIndex; i++)
{
sumBytes[i] = BitConverter.ToUInt16(packets, dataIndex);
dataIndex += 2;
}
#endregion
int checkSumResult = 0;
for (int i = 0; i < sumBytes.Length; i++)
{
checkSumResult += (int)sumBytes[i];
}
//如果数据包是奇数
if (packets.Length % 2 > 0)
{
checkSumResult += (int)packets[packets.Length - 1];
}
// 高16位与低16位相加
int calSumResult = (checkSumResult >> 16) + (checkSumResult & 0xffff);
// 如果高于16位还有值,继续与低16位相加
calSumResult += (calSumResult >> 16);
uint finnalResult= (uint)(~calSumResult & 0xffff);
var chkSumBytes = new byte[]
{
(byte)(finnalResult & 0xFF),
(byte)(finnalResult>>8 & 0xFF)
};
return chkSumBytes;
}
ICMP协议数据包收发
#region 发送Ping 包
string pingMsg = "abcdefghijklmnopqrstuvwabcdefghi";//可自定义的数据包
var icmpPacket = BuildIcmpPacket(pingMsg);
//ICMP
using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp);
int sendTag = await socket.SendToAsync(icmpPacket, SocketFlags.None, targetServer, tokenSource.Token);
if (sendTag == -1)
{
return;
}
#endregion
#region 收数据包
byte[] receiveBuffer = new byte[256];
//接收超时
using var timeoutCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(tokenSource.Token);
timeoutCancellationTokenSource.CancelAfter(TimeSpan.FromMilliseconds(timeout));
var receiveFromResult = await socket.ReceiveFromAsync(receiveBuffer, SocketFlags.Peek, targetServer, timeoutCancellationTokenSource.Token);
int receiveLen = receiveFromResult.ReceivedBytes;
int ipFixHeadLen = 20;//IP 包头部固定20个字节
int icmpFixHeadLen = 8;// ICMP包头固定长度=8;
if (receiveLen <= (ipFixHeadLen + icmpFixHeadLen))
{
return;
}
int dataLeng = receiveLen - ipFixHeadLen - icmpFixHeadLen;
var icmpReplyBuffer = new byte[dataLeng];
Array.Copy(receiveBuffer, ipFixHeadLen + icmpFixHeadLen, icmpReplyBuffer, 0, dataLeng);
#endregion
// 验证校验 可选
string reply = Encoding.ASCII.GetString(icmpReplyBuffer);
if (reply.Contains(pingMsg))
{
socket.Close();
}
注意:linux环境下运行:var socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp);
可能会因为权限不够,报错:临时解决办法可以使用sudio 提升运行权限,或者给当前用户socket权限;
版权声明:本文为elie_yang原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。