2012-07-12 98 views
5

我正在嘗試利用MSDN的Asynchronous Client Socket代碼示例來連接和控制某些家庭設備。據我所知,示例代碼的ReceiveCallback方法,直到該線程接收所有套接字的數據已經從所述遠程裝置發送的信號使用的EventWaitHandle ManualResetEvent的一個實例,並且該方法receiveDone.WaitOne()以保持當前線程的處理。在所有套接字的數據傳輸完畢後(套接字的數據爲空並且bytesRead = 0),Waithandle被移除並且應用程序繼續處理。異步客戶端套接字ManualResetEvent阻止執行

不幸的是,通過逐步執行代碼,看起來在客戶端最後一次從遠程設備返回數據後,ReceiveCallback從不會返回以查看數據隊列是否爲空(即bytesRead = 0),並因此將不會進入「其他」條件ReceiveCallback其中ManualResetEvent的的狀態將已被重置和應用程序將繼續進行處理。因此,由於它永不進入「else」條件,所以ManualResetEvent永遠不會被重置並且應用程序會凍結。

雖然我可以從代碼中刪除「receiveDone.WaitOne()」方法 - 允許執行,而不必等待通知所有數據都已收到;這會從通常不完整的設備返回一個數據串。

我是否錯誤地使用了此代碼示例?有沒有人看過這個或有過如何解決這個問題的經驗?

2012年7月14日 - 更新:的MSDN的Asynchronous Client Socket Example進一步測試後,人們清楚地看到ReceiveCallback實際上重新投票的端口和「bytesRead = 0」條件滿足插座僅當(即client.Shutdown(SocketShutdown.Both); client.Close();)。如果我理解正確,這意味着連接必須關閉才能通過receiveDone.WaitOne()方法。如果連接關閉以滿足WaitOne()等待句柄,則它完全違背了應用程序的目的,因爲我一直希望將連接打開,以便應用程序可以監聽持續發生的設備更新。

7/16/2012 - 更新:我已寫信給誰已經迴應說,"We're doing research on this issue. It might take some time before we get back to you."因此,它似乎不會出現這種挑戰可以得到解決微軟技術支持這次通過按摩這個代碼。

如果沒有可用的異步客戶端套接字示例代碼作爲編寫異步通信過程的基礎,請問是否有人可以請建議更可靠的替換例程?有三件設備,每件都有自己的IP地址和端口號。因此,如果可以利用一個類,那麼可以爲每個設備創建一個實例,這將是理想的。此外,該端口必須保持開放狀態,以接收設備不斷髮送的自發更新。最後,更新沒有結束字符或定義的長度,表明消息的​​傳輸已完成,因此例程必須持續輪詢端口以獲取可用數據。 任何意見或建議將不勝感激。

2012年7月18日 - 解決方法:花費試圖獲得MSDN的異步客戶端的Socket代碼示例工作相當大的時間量之後,很明顯,我將不得不尋找其他地方得到設備響應不斷被程序識別。爲了拯救別人的大腦損傷,我已經包括了我所使用的解決方法,這在目前看起來效果很好。如果有人有任何建議,請不要猶豫,加入這個問題!

// 
// ORIGINAL CODE ATTEMPT 
// 
public static Socket LutronClient; 
public static String LutronResponse = String.Empty; 
private const int LutronPort = 4999; 
private const string LutronIP = "192.168.1.71"; 
private static ManualResetEvent LutronConnectDone = new ManualResetEvent(false); 
private static ManualResetEvent LutronSendDone = new ManualResetEvent(false); 
private static ManualResetEvent LutronReceiveDone = new ManualResetEvent(false); 

private static void StartLutronClient() 
    { 
     try 
     { 
      var lutronIPAddress = IPAddress.Parse(LutronIP); 
      var lutronRemoteEP = new IPEndPoint(lutronIPAddress, LutronPort); 
      LutronClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
      LutronClient.BeginConnect(lutronRemoteEP, LutronConnectCallback, LutronClient); 
      LutronConnectDone.WaitOne(); 

      LutronSend(LutronClient, "sdl,14,100,0,S2\x0d"); 
      LutronSendDone.WaitOne(); 
      LutronReceive(LutronClient); 
      LutronReceiveDone.WaitOne(new TimeSpan(5000)); 
      MessageBox.Show("Response received from Lutron: " + LutronResponse); 
      txtBoxLutron.Text = LutronResponse; 

      LutronClient.Shutdown(SocketShutdown.Both); 
      LutronClient.Close(); 
     } 
     catch (Exception e) { MessageBox.Show(e.ToString()); } 
    } 

    private static void LutronConnectCallback(IAsyncResult lutronAr) 
    { 
     try 
     { 
      var lutronClient = (Socket)lutronAr.AsyncState; 
      lutronClient.EndConnect(lutronAr); 
      LutronConnectDone.Set(); 
     } 
     catch (Exception e) { MessageBox.Show(e.ToString()); } 
    } 

    private static void LutronReceive(Socket lutronClient) 
    { 
     try 
     { 
      var lutronState = new LutronStateObject { LutronWorkSocket = lutronClient }; 
      lutronClient.BeginReceive(lutronState.LutronBuffer, 0, LutronStateObject.BufferSize, 0, new AsyncCallback(LutronReceiveCallback), lutronState); 
     } 
     catch (Exception e) { MessageBox.Show(e.ToString()); } 
    } 

    private static void LutronReceiveCallback(IAsyncResult lutronAR) 
    { 
     try 
     { 
      var lutronState = (LutronStateObject)lutronAR.AsyncState; 
      var lutronClient = lutronState.LutronWorkSocket; 
      var bytesRead = lutronClient.EndReceive(lutronAR); 
      if (bytesRead > 0) 
      { 
       lutronState.LutronStringBuilder.AppendLine(Encoding.ASCII.GetString(lutronState.LutronBuffer, 0, bytesRead)); 
       lutronClient.BeginReceive(lutronState.LutronBuffer, 0, LutronStateObject.BufferSize, 0, new AsyncCallback(LutronReceiveCallback), lutronState); 
      } 
      else 
      { 
       if (lutronState.LutronStringBuilder.Length > 0) { LutronResponse = lutronState.LutronStringBuilder.ToString(); } 
       LutronReceiveDone.Set(); 
      } 
     } 
     catch (Exception e) { MessageBox.Show(e.ToString()); } 
    } 

    public static void LutronSend(Socket client, String data) 
    { 
     var byteData = Encoding.ASCII.GetBytes(data); 
     client.BeginSend(byteData, 0, byteData.Length, 0, LutronSendCallback, client); 
    } 

    private static void LutronSendCallback(IAsyncResult ar) 
    { 
     try 
     { 
      var client = (Socket)ar.AsyncState; 
      var bytesSent = client.EndSend(ar); 
      LutronSendDone.Set(); 
     } 
     catch (Exception e) { MessageBox.Show(e.ToString()); } 
    } 
    public class LutronStateObject 
    { 
     public Socket LutronWorkSocket; 
     public const int BufferSize = 256; 
     public byte[] LutronBuffer = new byte[BufferSize]; 
     public StringBuilder LutronStringBuilder = new StringBuilder(); 
    } 

} 

這是工作圍繞我用:

// 
// WORK-AROUND 
// 
using System; 
using System.Windows.Forms; 

namespace _GlobalCacheInterface 
{ 
    public partial class GlobalCacheDataScreen : Form 
    { 

     //Interface objects 
     private static GC_Interface _lutronInterface; 
     private const int LutronPort = 4999; 
     private const string LutronIP = "192.168.1.71"; 
     delegate void ThreadSafeLutronCallback(string text); 

     private static GC_Interface _elanInterface; 
     private const int ElanPort = 4998; 
     private const string ElanIP = "192.168.1.70"; 
     delegate void ThreadSafeElanCallback(string text); 

     private static GC_Interface _tuneSuiteInterface; 
     private const int TuneSuitePort = 5000; 
     private const string TuneSuiteIP = "192.168.1.70"; 
     delegate void ThreadSafeTuneSuiteCallback(string text); 

     public GlobalCacheDataScreen() 
     { 
       InitializeComponent(); 

       _lutronInterface = new GC_Interface(LutronIP, LutronPort); 
       _elanInterface = new GC_Interface(ElanIP, ElanPort); 
       _tuneSuiteInterface = new GC_Interface(TuneSuiteIP, TuneSuitePort); 

      // Create event handlers to notify application of available updated information. 
      _lutronInterface.DataAvailable += (s, e) => ThreadSafeTxtBoxLutron(_lutronInterface._returnString); 
      _elanInterface.DataAvailable += (s, e) => ThreadSafeTxtBoxElan(_elanInterface._returnString); 
      _tuneSuiteInterface.DataAvailable += (s, e) => ThreadSafeTxtBoxTuneSuite(_tuneSuiteInterface._returnString); 
      _lutronInterface.Connected += (s, e) => UpdateUI(); 
      _elanInterface.Connected += (s, e) => UpdateUI(); 
      _tuneSuiteInterface.Connected += (s, e) => UpdateUI(); 

      UpdateUI(); 
     } 

     private void UpdateUI() 
     { 
      _buttonConnectToLutron.Enabled = !_lutronInterface._isConnected; 
      _buttonConnectToElan.Enabled = !_elanInterface._isConnected; 
      _buttonConnectToTuneSuite.Enabled = !_tuneSuiteInterface._isConnected; 
      _buttonDisconnectFromLutron.Enabled = _lutronInterface._isConnected; 
      _buttonDisconnectFromElan.Enabled = _elanInterface._isConnected; 
      _buttonDisconnectFromTuneSuite.Enabled = _tuneSuiteInterface._isConnected; 
      string connectLutronStatus = _lutronInterface._isConnected ? "Connected" : "Not Connected"; 
      string connectElanStatus = _elanInterface._isConnected ? "Connected" : "Not Connected"; 
      string connectTuneSuiteStatus = _tuneSuiteInterface._isConnected ? "Connected" : "Not Connected"; 
      _textBoxLutronConnectStatus.Text = connectLutronStatus; 
      _textBoxElanConnectStatus.Text = connectElanStatus; 
      _textBoxTuneSuiteConnectStatus.Text = connectTuneSuiteStatus; 
     } 


     private void ThreadSafeTxtBoxLutron(string message) { if (_lutronRichTextRxMessage.InvokeRequired) { var d = new ThreadSafeLutronCallback(ThreadSafeTxtBoxLutron); _lutronRichTextRxMessage.Invoke(d, new object[] { message }); } else { _lutronRichTextRxMessage.Text = message; } }  
     private void ThreadSafeTxtBoxElan(string message) { if (_elanRichTextRxMessage.InvokeRequired) { var d = new ThreadSafeElanCallback(ThreadSafeTxtBoxElan); _elanRichTextRxMessage.Invoke(d, new object[] { message }); } else { _elanRichTextRxMessage.Text = message; if (message.EndsWith("\r")) { MessageBoxEx.Show(message, "Message from Lutron Elan", 1000); } } } 
     private void ThreadSafeTxtBoxTuneSuite(string message) { if (_tuneSuiteRichTextRxMessage.InvokeRequired) { var d = new ThreadSafeTuneSuiteCallback(ThreadSafeTxtBoxTuneSuite); _tuneSuiteRichTextRxMessage.Invoke(d, new object[] { message }); } else { _tuneSuiteRichTextRxMessage.Text = message; if (message.EndsWith("\r")) { MessageBoxEx.Show(message, "Message from TuneSuite", 1000); } } } 

     private void _buttonConnectToLutron_Click(object sender, EventArgs e) { _lutronInterface.Connect(); } 
     private void _buttonDisconnectFromLutron_Click(object sender, EventArgs e) { _lutronInterface.Disconnect(); } 
     private void _buttonConnectToElan_Click(object sender, EventArgs e) { _elanInterface.Connect(); } 
     private void _buttonDisconnectFromElan_Click(object sender, EventArgs e) { _elanInterface.Disconnect(); } 
     private void _buttonConnectToTuneSuite_Click(object sender, EventArgs e) { _tuneSuiteInterface.Connect(); } 
     private void _buttonDisconnectFromTuneSuite_Click(object sender, EventArgs e) { _tuneSuiteInterface.Disconnect(); } 
     private void _buttonLutronSendMessage_Click(object sender, EventArgs e) { _lutronInterface.SendCommand(_lutronRichTextTxMessage.Text); } 
     private void _buttonElanSendMessage_Click(object sender, EventArgs e) { _elanInterface.SendCommand(_elanRichTextTxMessage.Text); } 
     private void _buttonTuneSuiteSendMessage_Click(object sender, EventArgs e) { _tuneSuiteInterface.SendCommand(_elanRichTextTxMessage.Text); } 
     private void _buttonLightOn_Click(object sender, EventArgs e) { _lutronInterface.SendCommand("sdl,14,100,0,S2"); } 
     private void _buttonLightOff_Click(object sender, EventArgs e) { _lutronInterface.SendCommand("sdl,14,0,0,S2"); } 
     private void _buttonStereoOnOff_Click(object sender, EventArgs e) { _elanInterface.SendCommand("sendir,4:3,1,40000,4,1,21,181,21,181,21,181,21,181,21,181,21,181,21,181,21,181,21,181,21,181,21,181,21,800"); } 
     private void _button30_Click(object sender, EventArgs e) { _tuneSuiteInterface.SendCommand("\xB8\x4D\xB5\x33\x30\x00\x30\x21\xB8"); } 
     private void _button31_Click(object sender, EventArgs e) { _tuneSuiteInterface.SendCommand("\xB8\x4D\xB5\x33\x31\x00\x30\x21\xB8"); } 
     private void _button26_Click(object sender, EventArgs e) { _tuneSuiteInterface.SendCommand("\xB8\x4D\xB5\x32\x36\x00\x30\x21\xB8"); } 
    } 
} 

和GC_Interface類:

using System; 
using System.Net; 
using System.Net.Sockets; 
using System.Text; 
using System.Windows.Forms; 

namespace _GlobalCacheInterface 
{ 
    class GC_Interface 
    { 
     // Declare an event handler to notify when updates are available. 
     public event EventHandler<EventArgs> DataAvailable; 
     public string _returnString = ""; 

     // Declare an event handler to notify status of connection. 
     public event EventHandler<EventArgs> Connected; 
     public bool _isConnected; 

     public AsyncCallback ReceiveCallback; 
     public Socket Client; 
     private string _ipAddress; 
     private int _port; 
     private bool _waitForEndCharacter; 
     private byte _endCharacter; 
     byte[] m_DataBuffer = new byte[10]; 
     IAsyncResult m_Result; 

     public GC_Interface(string ipAddress, int port) { Init(ipAddress, port, false, 0); } 

     private void Init(string ipAddress, int port, bool waitForEndCharacter, byte endCharacter) 
     { 
      _ipAddress = ipAddress; 
      _port = port; 
      _waitForEndCharacter = waitForEndCharacter; 
      _endCharacter = endCharacter; 
      _isConnected = false; 
     } 

     public bool Connect() 
     { 
      try 
      { 
       // Create a TCP/IP socket. 
       Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 

       // Establish the remote endpoint for the socket. 
       var address = IPAddress.Parse(_ipAddress); 
       var remoteEP = new IPEndPoint(address, _port); 

       // Connect to the remote endpoint. 
       Client.Connect(remoteEP); 
       if (Client.Connected) 
       { 
        _isConnected = true; 
        ConnectedEventHandler(); 
        WaitForData(); 
       } 
       return true; 
      } 
      catch (SocketException se) { MessageBox.Show("\n connection failed, is the server running?\n" + se.Message); return false; } 
     } 
     public bool SendCommand(string command) 
     { 
      try 
      { 
       // Convert the string data to byte data using ASCII encoding. 
       var byteData = Encoding.Default.GetBytes(command); 
       // Add a carraige-return to the end. 
       var newArray = new byte[byteData.Length + 1]; 
       byteData.CopyTo(newArray, 0); 
       newArray[newArray.Length - 1] = 13; 
       if (Client == null) { return false; } 
       Client.Send(newArray); 
       return true; 
      } 
      catch (SocketException se) { MessageBox.Show(se.Message); return false; } 
     } 
     public void WaitForData() 
     { 
      try 
      { 
       if (ReceiveCallback == null) { ReceiveCallback = new AsyncCallback(OnDataReceived); } 
       var theSocPkt = new SocketPacket { thisSocket = Client }; 
       m_Result = Client.BeginReceive(theSocPkt.DataBuffer, 0, theSocPkt.DataBuffer.Length, SocketFlags.None, ReceiveCallback, theSocPkt); 
      } 
      catch (SocketException se) { MessageBox.Show(se.Message); } 
     } 
     public class SocketPacket 
     { 
      public System.Net.Sockets.Socket thisSocket; 
      public byte[] DataBuffer = new byte[1]; 
     } 
     public void OnDataReceived(IAsyncResult asyn) 
     { 
      try 
      { 
        SocketPacket theSockId = (SocketPacket)asyn.AsyncState; 
       var iRx = theSockId.thisSocket.EndReceive(asyn); 
       char[] Chars = new char[iRx + 1]; 
       System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder(); 
       int CharLen = d.GetChars(theSockId.DataBuffer, 0, iRx, Chars, 0); 
       System.String szData = new System.String(Chars); 
       _returnString = _returnString + szData.Replace("\0", ""); 
       // When an update is received, raise DataAvailable event 
       DataAvailableEventHandler(); 
       WaitForData(); 
      } 
      catch (ObjectDisposedException) { System.Diagnostics.Debugger.Log(0, "1", "\nOnDataReceived: Socket has been closed\n"); } 
      catch (SocketException se) { MessageBox.Show(se.Message); } 
     } 
     public bool Disconnect() 
     { 
       try 
       { 
        if (Client == null) { return false; } 
        Client.Close(); 
        Client = null; 
        _isConnected = false; 
        return true; 
       } 
       catch (Exception) { return false; } 
     } 
     protected virtual void DataAvailableEventHandler() 
     { 
      var handler = DataAvailable; 
      if (handler != null) { handler(this, EventArgs.Empty); } 
     } 
     protected virtual void ConnectedEventHandler() 
     { 
      var handler = Connected; 
      if (handler != null) { handler(this, EventArgs.Empty); } 
     } 

    } 
} 

回答

4

我有同樣的問題,添加一個可用的檢查到代碼解決了我的問題。以下是修改後的代碼。

private static void ReceiveCallback(IAsyncResult ar) { 
     try { 
      StateObject state = (StateObject) ar.AsyncState; 
      Socket client = state.workSocket; 

      int bytesRead = client.EndReceive(ar); 
      if (bytesRead > 0) { 
       state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead)); 
       // Check if there is anymore data on the socket 
       if (client.Available > 0) { 
        client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state); 
       } 
      } 

      if (bytesRead == 0 || client.Available == 0) { 
       if (state.sb.Length > 1) { 
        response = state.sb.ToString(); 
       } 
       receiveDone.Set(); 
      } 
     } catch (Exception e) { 
      Console.WriteLine(e.ToString()); 
     } 
    } 

希望有所幫助。

+0

JFagan - 感謝您的協助。 – Bill 2012-07-24 14:51:46

1

我在這裏迴避的問題。我試着回答你需要什麼,而不是你所要求的:

使用同步代碼。這將更容易理解,你不需要回調或事件。此外,對於低線程數量,它可能表現更好。

您還避免了當前代碼中的錯誤。如果發生異常,您的計算永遠不會完成。同步代碼沒有這個問題。