2012-07-12 20 views
5

MSDNのAsynchronous Client Socketコードサンプルを使用して家庭機器を接続して制御しようとしています。私は理解するように、サンプルコードのReceiveCallback方法は、スレッドがソケットのすべてのデータがリモートデバイスから送信されたという信号を受信するまで、現在のスレッドの処理を保持するEventWaitHandle ManualResetEventのインスタンス及びメソッドreceiveDone.WaitOne()を使用します。すべてのソケットのデータが送信された後(ソケットのデータが空で、bytesRead = 0)、Waithandleは削除され、アプリケーションは処理を続行します。非同期クライアントソケットManualResetEvent実行保留

残念ながら、ステッピング-を通じてコードの実行により、すなわちbytesRead(クライアントがリモートデバイスからのデータを返すことを最後の時間の後に、ReceiveCallbackは、データ・キューが空であるかどうかを確認するために返すことはありませんように見えます= 0)、したがって、決してManualResetEventの状態がリセットされているだろうと、アプリケーションが処理し続けているでしょう「他」条件ReceiveCallbackに入ります。したがって、「else」の状態になることはないため、ManualResetEventは決してリセットされず、アプリケーションはフリーズします。

コードからreceive_one.WaitOne()メソッドを削除することはできますが、ManualResetEventの通知がすべて受信されたことを待たずに実行を許可します。これは通常、不完全な機器からのデータ文字列を返します。

私はこのコードサンプルを間違って使用していますか?この問題を以前に見たことがある人や、この問題を回避する方法を経験した人はいますか?

2012年7月14日 - UPDATE:MSDNのAsynchronous Client Socket Exampleの更なるテストの後、それはReceiveCallbackが実際に再世論調査ポートと「bytesRead = 0」の条件が満たされていることのみ時にソケットが明らかになりました(すなわち、クライアント。シャットダウン(SocketShutdown.Both);クライアント。閉じる();)。これが正しく理解されていれば、receiveDone.WaitOne()メソッドを通過するために接続を閉じる必要があることを意味します。 WaitOne()Waithandleを満たすために接続が閉じられていれば、接続を開いたままにして、アプリケーションが機器の更新を聴くことができるようにしていたという点で、アプリケーションの目的を完全に破っています。

2012年7月16日 - UPDATE:私はそのようなものとして"We're doing research on this issue. It might take some time before we get back to you."は、この課題はで解決できることが表示されないようだと回答しているマイクロソフトテクニカルサポートに書かれている今度はこのコードをマッサージします。

非同期通信手順の作成の基礎としての非同期クライアントソケットの例コードがない場合、誰かがより信頼性の高い置換ルーチンを提案できますか? 3つの機器があり、それぞれに独自のIPアドレスとポート番号があります。したがって、クラスが利用されることができれば理想的であり、各デバイスに対してインスタンスを作成することができる。さらに、ポートは、装置が継続的に送信する自発的な更新を受信するために開いたままでなければなりません。最後に、更新は、メッセージの送信が完了したことを知らせる終了文字または定義された長さを持たないため、ルーチンは利用可能なデータについてポートを継続的にポーリングする必要があります。 アドバイスやご提案をいただければ幸いです。

2012年7月18日 - 回避策:MSDNの非同期クライアントソケットコードサンプルの作業を取得しようとするとかなりの時間を過ごした後、それは私が取得するために他の場所で見ていることが明らかになったプログラムによって継続的に認識されるデバイス応答。脳の損傷を他の誰かに救いたいという希望で、私はこの点でうまくいくと思われる回避策を含めました。誰か提案があれば、この質問に追加することをためらってください!

// 
// 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クラス:私はここに質問をsidesteppingてる

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

1

を。

同期コードを使用してください。理解しやすく、コールバックやイベントは必要ありません。また、スレッド数が少ない場合、パフォーマンスが向上する可能性があります。

また、現在のコードにあるバグを避けることもできます。例外が発生した場合、計算は完了しません。同期コードにはそのような問題はありません。