System.Net.NetworkInformation.Ping.SendIcmpEchoRequestOverRawSocket C# (CSharp) Метод

SendIcmpEchoRequestOverRawSocket() приватный Метод

private SendIcmpEchoRequestOverRawSocket ( IPAddress address, byte buffer, int timeout, PingOptions options ) : Task
address IPAddress
buffer byte
timeout int
options PingOptions
Результат Task
        private async Task<PingReply> SendIcmpEchoRequestOverRawSocket(IPAddress address, byte[] buffer, int timeout, PingOptions options)
        {
            EndPoint endPoint = new IPEndPoint(address, 0);

            bool isIpv4 = address.AddressFamily == AddressFamily.InterNetwork;
            ProtocolType protocolType = isIpv4 ? ProtocolType.Icmp : ProtocolType.IcmpV6;

            // Use a random value as the identifier.  This doesn't need to be perfectly random
            // or very unpredictable, rather just good enough to avoid unexpected conflicts.
            Random rand = t_idGenerator ?? (t_idGenerator = new Random());
            ushort identifier = (ushort)rand.Next((int)ushort.MaxValue + 1);

            IcmpHeader header = new IcmpHeader()
            {
                Type = isIpv4 ? (byte)IcmpV4MessageType.EchoRequest : (byte)IcmpV6MessageType.EchoRequest,
                Code = 0,
                HeaderChecksum = 0,
                Identifier = identifier,
                SequenceNumber = 0,
            };

            byte[] sendBuffer = CreateSendMessageBuffer(header, buffer);

            using (Socket socket = new Socket(address.AddressFamily, SocketType.Raw, protocolType))
            {
                socket.ReceiveTimeout = timeout;
                socket.SendTimeout = timeout;
                // Setting Socket.DontFragment and .Ttl is not supported on Unix, so ignore the PingOptions parameter.

                int ipHeaderLength = isIpv4 ? IpHeaderLengthInBytes : 0;
                await socket.SendToAsync(new ArraySegment<byte>(sendBuffer), SocketFlags.None, endPoint).ConfigureAwait(false);
                byte[] receiveBuffer = new byte[ipHeaderLength + IcmpHeaderLengthInBytes + buffer.Length];

                long elapsed;
                Stopwatch sw = Stopwatch.StartNew();
                // Read from the socket in a loop. We may receive messages that are not echo replies, or that are not in response
                // to the echo request we just sent. We need to filter such messages out, and continue reading until our timeout.
                // For example, when pinging the local host, we need to filter out our own echo requests that the socket reads.
                while ((elapsed = sw.ElapsedMilliseconds) < timeout)
                {
                    Task<SocketReceiveFromResult> receiveTask = socket.ReceiveFromAsync(
                                                                    new ArraySegment<byte>(receiveBuffer),
                                                                    SocketFlags.None,
                                                                    endPoint);
                    var cts = new CancellationTokenSource();
                    Task finished = await Task.WhenAny(receiveTask, Task.Delay(timeout - (int)elapsed, cts.Token)).ConfigureAwait(false);
                    cts.Cancel();
                    if (finished != receiveTask)
                    {
                        sw.Stop();
                        return CreateTimedOutPingReply();
                    }

                    SocketReceiveFromResult receiveResult = receiveTask.GetAwaiter().GetResult();
                    int bytesReceived = receiveResult.ReceivedBytes;
                    if (bytesReceived - ipHeaderLength < IcmpHeaderLengthInBytes)
                    {
                        continue; // Not enough bytes to reconstruct IP header + ICMP header.
                    }

                    byte type, code;
                    unsafe
                    {
                        fixed (byte* bytesPtr = receiveBuffer)
                        {
                            int icmpHeaderOffset = ipHeaderLength;
                            IcmpHeader receivedHeader = *((IcmpHeader*)(bytesPtr + icmpHeaderOffset)); // Skip IP header.
                            type = receivedHeader.Type;
                            code = receivedHeader.Code;

                            if (identifier != receivedHeader.Identifier
                                || type == (byte)IcmpV4MessageType.EchoRequest
                                || type == (byte)IcmpV6MessageType.EchoRequest) // Echo Request, ignore
                            {
                                continue;
                            }
                        }
                    }

                    sw.Stop();
                    long roundTripTime = sw.ElapsedMilliseconds;
                    int dataOffset = ipHeaderLength + IcmpHeaderLengthInBytes;
                    // We want to return a buffer with the actual data we sent out, not including the header data.
                    byte[] dataBuffer = new byte[bytesReceived - dataOffset];
                    Buffer.BlockCopy(receiveBuffer, dataOffset, dataBuffer, 0, dataBuffer.Length);

                    IPStatus status = isIpv4
                                        ? IcmpV4MessageConstants.MapV4TypeToIPStatus(type, code)
                                        : IcmpV6MessageConstants.MapV6TypeToIPStatus(type, code);

                    return new PingReply(address, options, status, roundTripTime, dataBuffer);
                }

                // We have exceeded our timeout duration, and no reply has been received.
                sw.Stop();
                return CreateTimedOutPingReply();
            }
        }