【问题标题】:ICMP listener problemsICMP 侦听器问题
【发布时间】:2010-11-04 23:02:09
【问题描述】:

当一个软件按照你想要的方式运行时总是令人愉快的,但只要我不明白它为什么(或如何)运行,我认为这是一个大问题。我制作了一个 ICMP 侦听器来弥补“UdpClient”类中的一个缺陷,当远程主机意外不可用时,它不会返回由请求产生的适当 ICMP 消息。 (ICMP 类型 3,任何代码)。它没有回复 ICMP 代码,而是简单地抛出一个错误:(WSACONNRESET) 和“无法访问已处置的对象”。

我现在运行的代码使用 ManualResetEvents 作为信号,这是可以接受的。结果数据已经过仔细检查,即使在时隙和序列号级别,一切都很好。我只是不明白为什么循环的每次迭代都需要一个新的“StateObject”。 我没有理由知道当前缓冲区不能被重用。然而,如果我不每次迭代都使用一个新的,返回的缓冲区是无效的(虽然没有错误);然后缓冲区指的是从我的主机到外部目的地的数据包,而不是来自远程主机的缓冲区。因此,我看到了我的系统对回显(类型 0)请求的回复,而不是收到的实际回显请求(类型 8)(如 Wireshark 中所示)。一旦我将循环更改为使用新的“StateObject”,一切就又好了。 Microsoft 的示例没有包含一次性的“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();
            }
        }
    }
}

【问题讨论】:

    标签: c# sockets


    【解决方案1】:
    iar = icmpClient.BeginReceiveFrom(...);
    iar.AsyncWaitHandle.WaitOne();
    

    使用 BeingReceiveFrom() 然后等待异步操作完成是没有意义的。只需使用 ReceiveFrom()。现在您不再需要那个“StateObject”来让回调知道响应属于哪个请求。

    【讨论】:

    • 刚刚完成;奇迹般有效。 (而且代码更少!)谢谢。
    猜你喜欢
    • 2021-11-27
    • 2012-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多