【发布时间】: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();
}
}
}
}
【问题讨论】: