public ConnectionState(TcpClient client, int MaxMessageSize) { this.client = client; // create send pipe with max message size for pooling sendPipe = new MagnificentSendPipe(MaxMessageSize); }
// 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 public static void SendLoop(int connectionId, TcpClient client, MagnificentSendPipe sendPipe, ManualResetEvent sendPending) { // get NetworkStream from client NetworkStream stream = client.GetStream(); // avoid payload[packetSize] allocations. size increases dynamically as // needed for batching. // // IMPORTANT: DO NOT make this a member, otherwise every connection // on the server would use the same buffer simulatenously byte[] payload = null; 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 & serialize all // a locked{} TryDequeueAll is twice as fast as // ConcurrentQueue, see SafeQueue.cs! if (sendPipe.DequeueAndSerializeAll(ref payload, out int packetSize)) { // send messages (blocking) or stop if stream is closed if (!SendMessagesBlocking(stream, payload, packetSize)) { // break instead of return so stream close still happens! break; } } // 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 Log.Info("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(); } }