2012-07-12 31 views
5

Estoy intentando utilizar el ejemplo de código Asynchronous Client Socket de MSDN para conectar y controlar algunos equipos domésticos. Según entiendo, ReceiveCallback método del código de ejemplo utiliza una instancia de la EventWaitHandle ManualResetEvent y el método receiveDone.WaitOne() para mantener el procesamiento del hilo actual hasta que el hilo recibe una señal de que todos los datos de la socket se ha transmitido desde el dispositivo remoto . Después de que se hayan transmitido todos los datos del socket (los datos del socket están vacíos y bytesRead = 0), se elimina el indicador de espera y la aplicación continúa procesando.Zócalo de cliente asíncrono ManualResetEvent retención de ejecución

Desafortunadamente, dando un paso-a través de la ejecución del código, parece que después de la última vez que el cliente devuelve datos desde el dispositivo remoto, ReceiveCallback nunca vuelve a ver si los datos-cola está vacía (es decir BytesRead = 0), y por lo tanto nunca entra en el "más" condición en ReceiveCallback donde el estado de la ManualResetEvent habría sido de reset y la aplicación habría continuado para procesar. Por lo tanto, dado que nunca entra en la condición "else", ManualResetEvent nunca se restablece y la aplicación se congela.

Aunque puedo quitar el método del código "receiveDone.WaitOne()" - que permite la ejecución sin esperar a la notificación del de ManualResetEvent que todos los datos han sido recibidos; esto devuelve una cadena de datos del equipo que generalmente es incompleta.

¿Estoy utilizando este ejemplo de código incorrectamente? ¿Alguien ha visto esto antes o tuvo alguna experiencia sobre cómo solucionar este problema?

7/14/2012 - ACTUALIZACIÓN: Después de más pruebas de la MSDN de Asynchronous Client Socket Example, se hizo evidente que ReceiveCallback realidad re-sondea el puerto y el "BytesRead = 0" La condición se cumple solamente cuando la toma se libera (es decir, client.Shutdown (SocketShutdown.Both); client.Close();). Si entiendo esto correctamente, esto significa que la conexión debe cerrarse para pasar el método receiveDone.WaitOne(). Si la conexión se cierra para satisfacer el waithand de WaitOne(), es totalmente contrario al propósito de la aplicación porque esperaba dejar la conexión abierta para que la aplicación pudiera escuchar las actualizaciones de los equipos, que ocurren continuamente.

7/16/2012 - ACTUALIZACIÓN: He escrito al apoyo Microsoft Tech que han respondido que "We're doing research on this issue. It might take some time before we get back to you." Como tal, parece que no parece que este desafío se puede resolver en esta vez a través de masajear este código.

Sin la disponibilidad del código de ejemplo del socket de cliente asíncrono como base para escribir procedimientos de comunicación asíncrona, ¿puedo preguntar si alguien puede sugerir una rutina de reemplazo que sea más confiable? Hay tres equipos, cada uno con su propia dirección IP y número de puerto. Por lo tanto, sería ideal si se pudiera utilizar una clase, donde se podría crear una instancia para cada dispositivo.Además, el puerto debe permanecer abierto para recibir actualizaciones espontáneas enviadas continuamente por el equipo. Por último, las actualizaciones no tienen un carácter final o una longitud definida que indiquen que la transmisión del mensaje se ha completado, por lo que la rutina debe sondear continuamente el puerto para conocer los datos disponibles. Cualquier consejo o sugerencia sería muy apreciada.

7/18/2012 - Solución: Después de pasar una considerable cantidad de tiempo tratando de conseguir el asíncrono socket de cliente código de ejemplo de trabajo de MSDN, se hizo evidente que iba a tener que buscar otro lugar para obtener el respuestas del dispositivo continuamente reconocidas por el programa. Con la esperanza de salvar a alguien el daño cerebral, he incluido la solución de problemas que utilicé que parece funcionar bien hasta este momento. Si alguien tiene alguna sugerencia, ¡no dude en agregar esta pregunta!

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

} 

Este es el trabajo en torno utilicé:

// 
// 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"); } 
    } 
} 

y la clase 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); } 
     } 

    } 
} 

Respuesta

4

Tuve el mismo problema, agregando un cheque Disponible al código solucionado mi problema. A continuación está el código revisado.

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

Espero que ayude.

+0

JFagan - Gracias por su ayuda. – Bill

1

estoy dejando a un lado la cuestión aquí. Intento responder lo que necesita, no lo que solicita:

Use el código síncrono. Será más fácil de entender, no necesita devoluciones de llamadas ni eventos. Además, para bajo conteo de hilos, es probable que tenga un mejor rendimiento.

También evita los errores que tenga en su código actual. Si ocurre una excepción, su cálculo nunca se completa. El código sincrónico no tiene ese problema.

Cuestiones relacionadas