protected static void SendLoop(int connectionId, TcpClient client, SafeQueue <byte[]> sendQueue, ManualResetEvent sendPending) { NetworkStream stream = client.GetStream(); try { while (client.Connected) { sendPending.Reset(); byte[][] messages; if (sendQueue.TryDequeueAll(out messages)) { if (!SendMessagesBlocking(stream, messages)) { return; } } sendPending.WaitOne(); } } catch (ThreadAbortException) { } catch (ThreadInterruptedException) { } catch (Exception exception) { Logger.Log("SendLoop Exception: connectionId=" + connectionId + " reason: " + exception); } }
// thread send function // note: we really do need one per connection, so that if one connection // blocks, the rest will still continue to get sends protected static void SendLoop(int connectionId, TcpClient client, SafeQueue <byte[]> sendQueue, ManualResetEvent sendPending) { // get NetworkStream from client NetworkStream stream = client.GetStream(); try { // (Yurgis) while (client.Connected) // try this. client will get closed eventually. while (true) { // reset ManualResetEvent before we do anything else. this // way there is no race condition. if Send() is called again // while in here then it will be properly detected next time // -> otherwise Send might be called right after dequeue but // before .Reset, which would completely ignore it until // the next Send call. sendPending.Reset(); // WaitOne() blocks until .Set() again // dequeue all // SafeQueue.TryDequeueAll is twice as fast as // ConcurrentQueue, see SafeQueue.cs! byte[][] messages; if (sendQueue.TryDequeueAll(out messages)) { // send message (blocking) or stop if stream is closed if (!SendMessagesBlocking(stream, messages)) { // (Yurgis) if (stream.CanWrite == false) { Logger.LogWarning("SendLoop(): EXIT because...."); return; } } } // don't choke up the CPU: wait until queue not empty anymore sendPending.WaitOne(); } } catch (ThreadAbortException) { // happens on stop. don't log anything. Logger.LogWarning($"SendLoop() Conn #{connectionId}: ThreadAbortException"); } catch (ThreadInterruptedException) { // happens if receive thread interrupts send thread. Logger.LogWarning($"SendLoop() Conn #{connectionId}: ThreadInterruptedException"); } catch (Exception exception) { // something went wrong. the thread was interrupted or the // connection closed or we closed our own connection or ... // -> either way we should stop gracefully Logger.Log("SendLoop Exception: connectionId=" + connectionId + " reason: " + exception); } }
// thread send function // note: we really do need one per connection, so that if one connection // blocks, the rest will still continue to get sends protected static void SendLoop(int connectionId, TcpClient client, Stream stream, SafeQueue <byte[]> sendQueue, ManualResetEvent sendPending) { try { while (client.Connected) // try this. client will get closed eventually. { // reset ManualResetEvent before we do anything else. this // way there is no race condition. if Send() is called again // while in here then it will be properly detected next time // -> otherwise Send might be called right after dequeue but // before .Reset, which would completely ignore it until // the next Send call. sendPending.Reset(); // WaitOne() blocks until .Set() again // dequeue all // SafeQueue.TryDequeueAll is twice as fast as // ConcurrentQueue, see SafeQueue.cs! byte[][] messages; if (sendQueue.TryDequeueAll(out messages)) { // send message (blocking) or stop if stream is closed if (!SendMessagesBlocking(stream, messages)) { break; // break instead of return so stream close still happens! } } // don't choke up the CPU: wait until queue not empty anymore sendPending.WaitOne(); } } catch (ThreadAbortException) { // happens on stop. don't log anything. } catch (ThreadInterruptedException) { // happens if receive thread interrupts send thread. } catch (Exception exception) { // something went wrong. the thread was interrupted or the // connection closed or we closed our own connection or ... // -> either way we should stop gracefully Logger.Log("SendLoop Exception: connectionId=" + connectionId + " reason: " + exception); } finally { // clean up no matter what // we might get SocketExceptions when sending if the 'host has // failed to respond' - in which case we should close the connection // which causes the ReceiveLoop to end and fire the Disconnected // message. otherwise the connection would stay alive forever even // though we can't send anymore. stream.Close(); client.Close(); } }
// thread send function // note: we really do need one per connection, so that if one connection // blocks, the rest will still continue to get sends protected static void SendLoop(int connectionId, TcpClient client, SafeQueue <byte[]> sendQueue) { // get NetworkStream from client NetworkStream stream = client.GetStream(); try { while (client.Connected) // try this. client will get closed eventually. { // dequeue all byte[][] messages; if (sendQueue.TryDequeueAll(out messages)) { // send message (blocking) or stop if stream is closed if (!SendMessagesBlocking(stream, messages)) { return; } } // don't choke up the CPU: wait until queue not empty anymore sendQueue.notEmpty.WaitOne(); } } catch (ThreadAbortException) { // happens on stop. don't log anything. } catch (Exception exception) { // something went wrong. the thread was interrupted or the // connection closed or we closed our own connection or ... // -> either way we should stop gracefully Logger.Log("SendLoop Exception: connectionId=" + connectionId + " reason: " + exception); } }