ICMP监听器问题

时间:2010-11-04 13:34:45

标签: c# sockets

当一个软件以你想要的方式工作时,它总是令人愉悦,但只要我不明白为什么(或如何)它的工作原理,我认为这是一个大问题。我创建了一个ICMP监听器来补偿'UdpClient'类中的缺陷,该缺陷不会返回由于远程主机意外地变得不可用时发出的请求而产生的相应ICMP消息。 (ICMP类型3,任何代码)。它不会回复ICMP代码,而只是抛出一个错误:(WSACONNRESET)和'无法访问已处置的对象'。

我现在运行的代码现在使用ManualResetEvents作为信令,这是可以接受的。即使在时间段和序列号级别,也仔细检查了结果数据,一切都很好。我只是不明白为什么循环的每次迭代都需要一个新的'StateObject'。 我没有理由知道当前缓冲区无法重复使用的状态。然而,如果我每次迭代都不使用新的,则返回的缓冲区无效(尽管没有错误);然后,缓冲区引用从主机到外部目标的数据包,而不是来自远程主机的缓冲区。因此,我看到我的系统回复了一个echo(Type 0)请求,而不是收到的实际echo请求(Type 8)(如Wireshark所示)。一旦我改变循环以使用新的“StateObject”,一切都很好。微软的例子没有包含一次性的“StateObject”,所以为了这个目的,我创建了一个继承自IDisposable的新类。

此外,当从ManualResetEvent信号切换到“AsyncWaitHandle”信令时,该过程仅在使用对回调委托的递归调用时才有效。如果不这样做,'IASyncResult''IsCompleted'并不总是被设置(即使所有缓冲区都是相同的60字节),所以它将无限期地等待句柄。

长篇故事(以及很多代码),但我希望有人能够对这些问题有所了解。

    using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Linq;
using System.Text;
using System.Threading;
using log4net;

namespace ICMPTest
{
    public class ICMPCheck
    {
        private static ManualResetEvent gotMessage;
        private static IPAddress ipAddress;
        private static IntPtr stateHandle; // Dont know what it is for, or what to do with it
        private static Disposables.StateObject so = null;
        private static Socket icmpClient;
        private static EndPoint remoteRawEndPoint = (EndPoint)new IPEndPoint(IPAddress.Any, 0);
        public static Queue<byte[]> m_icmpQueue = new Queue<byte[]>();
        public static object m_syncLock = new object();
        private static IPEndPoint NIC = null;
        private static int Queued = 0;
        private static int DeQueued = 0;
        private static int CallCount = 0;
        public static IAsyncResult iar;

        public static void Start()
        {
            try
            {
                using (icmpClient = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp))
                {
                    IPHostEntry hostEntry = Dns.GetHostEntry(Dns.GetHostName());
                    IPHostEntry hostInfo = Dns.Resolve(Dns.GetHostName());
                    IPAddress[] address = hostInfo.AddressList;
                    ipAddress = address[0];
                    NIC = new IPEndPoint(ipAddress, 0);
                    icmpClient.Bind(NIC); // Bind to localhost, port any
                    byte[] inBytes = new byte[] { 1, 0, 0, 0 };
                    byte[] outBytes = new byte[] { 0, 0, 0, 0 };
                    icmpClient.IOControl(IOControlCode.ReceiveAll, inBytes, outBytes); //only outgoing packets
                    icmpClient.ReceiveBufferSize = 1024;

                    while (true)
                    {
                        //gotMessage = new ManualResetEvent(false);
                        using (so = new Disposables.StateObject(stateHandle))
                        {

                            so.workSocket = icmpClient;

                            iar = icmpClient.BeginReceiveFrom(so.buffer, 0, Disposables.StateObject.BUFFER_SIZE, 0, ref remoteRawEndPoint, new AsyncCallback(ReceiveFromCallback), so); //blocking

                            iar.AsyncWaitHandle.WaitOne(); //gotMessage.WaitOne(); //iar.AsyncWaitHandle.WaitOne(); // seems to be unreliable

                            for (int i = DeQueued; i < Queued; i++)
                            {
                                //DequeueParse.DequeueAndParse(ref so);
                                Interlocked.Increment(ref DeQueued);
                                ICMPProgram.logger.Debug("ICMPCheck-0: Signal + Message received: " + remoteRawEndPoint.ToString() + " Queue: " + m_icmpQueue.Count.ToString() + " " + Queued.ToString() + " " + DeQueued.ToString());
                            }
                        } // using StateObject
                        //gotMessage.Dispose();
                    }// while
                }//using Socket
            } // try
            catch (Exception excp)
            {
                ICMPProgram.logger.Error("ICMPCheck: Exception Mainblock. " + excp.Message);
            }
            return;
        }

        private static void ReceiveFromCallback(IAsyncResult iar) 
        { 
            Interlocked.Increment(ref CallCount);
            try
            {
                if (ICMPProgram.stopRequest) return;
                Disposables.StateObject state = (Disposables.StateObject)iar.AsyncState;
                Socket client = ((Disposables.StateObject)iar.AsyncState).workSocket;
                EndPoint tempRemoteEP = (EndPoint)new IPEndPoint(IPAddress.Any, 0);

                int bytesRead = client.EndReceiveFrom(iar, ref tempRemoteEP);

                if (bytesRead > 0)
                {
                    if (!(((IPEndPoint)tempRemoteEP).Address).Equals(NIC.Address)) // ignore messages from local host
                    {
                        byte[] _icmpData = new byte[bytesRead];
                        byte[] icmpType = new byte[1];
                        Buffer.BlockCopy(state.buffer, 20, icmpType, 0, 1);

                        //if (((int)icmpType[0] == 3)) // only type 3
                        if (true) // all tyoes for now
                        {
                            Buffer.BlockCopy(state.buffer, 0, _icmpData, 0, bytesRead); 
                            lock (m_syncLock)
                            {
                                m_icmpQueue.Enqueue(_icmpData);
                                Interlocked.Increment(ref Queued);
                            }
                        }
                        // the next callback is required when using AsyncWaitHandle.WaitOne signaling, not required (but useful for high volume) for ManualResetEvents
                        client.BeginReceiveFrom(state.buffer, 0, Disposables.StateObject.BUFFER_SIZE, 0, ref tempRemoteEP, new AsyncCallback(ReceiveFromCallback), state); // suitable for high volume
                        remoteRawEndPoint = tempRemoteEP;
                        //ICMPProgram.logger.Debug("ICMPCheck: Bytes: " + bytesRead.ToString() + ", Type: " + icmpType[0].ToString() + " " + tempRemoteEP.ToString() + " " + m_icmpQueue.Count.ToString() + " " + Queued.ToString() + " " + CallCount.ToString() + " " + iar.IsCompleted.ToString());
                    }
                }
                else
                {
                    ICMPProgram.logger.Debug("ICMPCheck: bytesRead = 0 ");
                }
            }
            catch (Exception excp)
            {
                ICMPProgram.logger.Debug("ICMPCheck:ReceiveFromCallback main " + excp.Message);
            }
            finally
            {
                //gotMessage.Set();
            }
        }
    }
}

1 个答案:

答案 0 :(得分:0)

iar = icmpClient.BeginReceiveFrom(...);
iar.AsyncWaitHandle.WaitOne();

使用BeingReceiveFrom()并等待异步操作完成是没有意义的。只需使用ReceiveFrom()。现在,您不再需要'StateObject'来让回调知道响应所属的请求。