當一款軟件按照您的想法運行時,它總是令人滿意,但只要我不明白爲什麼(或如何)運行,我認爲這是一個大問題。我做了一個ICMP監聽器來彌補'UdpClient'類中的一個缺陷,即當遠程主機意外地變得不可用時,不會返回由請求產生的適當的ICMP消息。 (ICMP類型3,任何代碼)。它只是拋出一個錯誤:(WSACONNRESET)和'無法訪問已處理的對象',而不是使用ICMP代碼進行回覆。ICMP監聽器問題
我現在運行的代碼現在使用ManualResetEvents作爲信號,這是可以接受的。結果數據經過仔細檢查,即使是在時隙和序列號級別,一切都很好。我只是不明白爲什麼每次迭代循環都需要一個新的'StateObject'。 我沒有理由說明當前的緩衝區不能被重用。不過,如果每次迭代都不使用新的緩衝區,則返回的緩衝區無效(儘管如此)。然後緩衝區引用從我的主機到外部目標的數據包,而不是來自遠程主機的緩衝區。因此,我看到我的系統回覆了一個迴應(類型0)請求,而不是實際的迴應請求(類型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();
}
}
}
}
剛剛完成;奇蹟般有效。 (並用很少的代碼!)謝謝。 – Nebbukadnezzar 2010-11-04 15:26:25