/// <summary> /// Sends the request and handles any responses /// </summary> /// <param name="mb">The <see cref="MessageBuilder"/> used to contruct the request</param> /// <param name="del">The <see cref="ResponseReceivedEventHandler"/> for handling the response</param> /// <param name="waitForCallback"><c>true</c> to wait for a callback;<c>false</c> otherwise</param> /// <param name="state">An optional state object that will be passed into the response events associated with this request</param> protected void Send(MessageBuilder mb, ResponseReceivedEventHandler del, bool waitForCallback, object state) { // do some of this *before* we spin up a new thread so we can just throw an exception if the error occurs // *before* we even send the request (like when generating the message bytes) bool doSend = this.OnBeforeSend(mb); if (doSend) { byte[] bytes = mb.GetBytes(); mb = null; // start a new thread for the network connection stuff ConnectionState cs = new ConnectionState(bytes, del, waitForCallback, state); ParameterizedThreadStart pts = new ParameterizedThreadStart(SendAsync); Thread t = new Thread(pts); t.Start(cs); } }
/// <summary> /// Creates a new instance of the class. /// </summary> /// <param name="bytes">The request bytes to be written</param> /// <param name="del">The <see cref="ResponseReceivedEventHandler"/> method to call to handle the response</param> /// <param name="waitForCallback"><c>true</c> if the connection should wait for a callback;<c>false</c> otherwise</param> /// <param name="state">An optional state object that will be passed into the response events associated with this request</param> public ConnectionState(byte[] bytes, ResponseReceivedEventHandler del, bool waitForCallback, object state) { this.Bytes = bytes; this.Delegate = del; this.WaitForCallback = waitForCallback; this.UserState = state; }
/// <summary> /// Sends the request on a background thread. /// </summary> /// <param name="obj">The obj.</param> /// <remarks> /// Using the built-in async methods (Begin*/End*) results in flakey behavior. /// Using the synchronous methods in another thread avoids the issue. /// </remarks> private void SendAsync(object obj) { TcpClient client = null; NetworkStream stream = null; try { ConnectionState cs = (ConnectionState)obj; byte[] bytes = cs.Bytes; ResponseReceivedEventHandler del = cs.Delegate; bool waitForCallback = cs.WaitForCallback; // connect try { client = new TcpClient(); client.Connect(this.hostname, this.port); } catch { OnCommunicationFailure(new Response(ErrorCode.NETWORK_FAILURE, ErrorDescription.CONNECTION_FAILURE), cs.UserState); } // write try { stream = client.GetStream(); stream.Write(bytes, 0, bytes.Length); } catch { OnCommunicationFailure(new Response(ErrorCode.NETWORK_FAILURE, ErrorDescription.WRITE_FAILURE), cs.UserState); } // read try { string response = String.Empty; byte[] buffer = new byte[4096]; while (!response.EndsWith(EOM, StringComparison.InvariantCulture)) { int length = stream.Read(buffer, 0, buffer.Length); if (length > 0) { response += System.Text.Encoding.UTF8.GetString(buffer, 0, length); } else { break; } } del(response, cs.UserState); // wait for callback if (waitForCallback) { response = String.Empty; buffer = new byte[4096]; while (!response.EndsWith(EOM, StringComparison.InvariantCulture)) { int length = stream.Read(buffer, 0, buffer.Length); if (length > 0) { response += System.Text.Encoding.UTF8.GetString(buffer, 0, length); } else { break; } } del(response, cs.UserState); } } catch { OnCommunicationFailure(new Response(ErrorCode.NETWORK_FAILURE, ErrorDescription.READ_FAILURE), cs.UserState); } } catch (Exception ex) { Growl.CoreLibrary.DebugInfo.WriteLine(ex.ToString()); } finally { try { if (client != null && client.Client != null) { client.Client.Blocking = true; try { client.Client.Shutdown(SocketShutdown.Both); } catch { } client.Client.Close(); client.Close(); } if (stream != null) { stream.Close(); stream.Dispose(); stream = null; } client = null; } catch { // suppress } } }