6

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

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

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

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

2012 年 7 月 14 日 - 更新: MSDN のAsynchronous Client Socket Exampleをさらにテストした結果、ReceiveCallbackが実際にポートを再ポーリングし、"bytesRead = 0" 条件が満たされるのはソケットが解放されたとき(クライアント. Shutdown(SocketShutdown.Both); client.Close(); )。これを正しく理解している場合、これは、 receiveDone.WaitOne() メソッドを通過するには接続を閉じる必要があることを意味します。WaitOne() Waithandle を満たすために接続が閉じられた場合、接続を開いたままにして、アプリケーションが継続的に発生する機器の更新をリッスンできるようにすることを望んでいたという点で、アプリケーションの目的を完全に無効にします。

2012 年 7月 16 日 - 更新:マイクロソフト テクニカル サポートに返信したところ、「この問題について調査を行っています。ご連絡するまでに時間がかかる場合があります。」 そのため、現時点では、このコードを操作することでこの問題を解決できるとは思えません。

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

2012 年 7 月 18 日 - 回避策: MSDN の Asynchronous Client 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

2 に答える 2

5

コードに Available チェックを追加すると、問題が解決しました。以下は修正後のコードです。

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());
        }
    }

それが役立つことを願っています。

于 2012-07-23T12:57:10.057 に答える