コード例 #1
0
 public void Dispose()
 {
     if (Buffer != null)
     {
         ByteArrayPool.Return(Buffer);
     }
 }
コード例 #2
0
        // send message (via stream) with the <size,content> message structure
        protected static bool SendMessage(NetworkStream stream, byte[] content)
        {
            // can we still write to this socket (not disconnected?)
            if (!stream.CanWrite)
            {
                Logger.LogWarning("Send: stream not writeable: " + stream);
                return(false);
            }

            // check size
            if (content.Length > ushort.MaxValue)
            {
                Logger.LogError("Send: message too big(" + content.Length + ") max=" + ushort.MaxValue);
                return(false);
            }

            // stream.Write throws exceptions if client sends with high
            // frequency and the server stops
            try
            {
                // write header+content at once via payload array. writing
                // header,payload separately would cause 2 TCP packets to be
                // sent if nagle's algorithm is disabled(2x TCP header overhead)
                byte[] payload = ByteArrayPool.Take();
                // construct header (size)
                UShortToBytes((ushort)content.Length, payload);
                Array.Copy(content, 0, payload, 2, content.Length);
                stream.Write(payload, 0, 2 + content.Length);
                ByteArrayPool.Return(payload);
                // flush to make sure it is being sent immediately
                stream.Flush();
                return(true);
            }
            catch (Exception exception)
            {
                // log as regular message because servers do shut down sometimes
                Logger.Log("Send: stream.Write exception: " + exception);
                return(false);
            }
        }
コード例 #3
0
        // thread receive function is the same for client and server's clients
        // (static to reduce state for maximum reliability)
        protected static void ReceiveLoop(int connectionId, TcpClient client, SafeQueue <Message> messageQueue)
        {
            // get NetworkStream from client
            NetworkStream stream = client.GetStream();

            // keep track of last message queue warning
            DateTime messageQueueLastWarning = DateTime.Now;

            // absolutely must wrap with try/catch, otherwise thread exceptions
            // are silent
            try
            {
                // add connected event to queue with ip address as data in case
                // it's needed
                messageQueue.Enqueue(new Message(connectionId, EventType.Connected, null, 0));

                // let's talk about reading data.
                // -> normally we would read as much as possible and then
                //    extract as many <size,content>,<size,content> messages
                //    as we received this time. this is really complicated
                //    and expensive to do though
                // -> instead we use a trick:
                //      Read(2) -> size
                //        Read(size) -> content
                //      repeat
                //    Read is blocking, but it doesn't matter since the
                //    best thing to do until the full message arrives,
                //    is to wait.
                // => this is the most elegant AND fast solution.
                //    + no resizing
                //    + no extra allocations, just one for the content
                //    + no crazy extraction logic
                while (true)
                {
                    byte[] content = ByteArrayPool.Take();
                    // read the next message (blocking) or stop if stream closed
                    if (!ReadMessageBlocking(stream, content, out int size))
                    {
                        break;
                    }

                    // queue it
                    messageQueue.Enqueue(new Message(connectionId, EventType.Data, content, size));
                    // it is now up to the developer to Dispose the message to return the
                    // buffer to the pool.

                    // and show a warning if the queue gets too big
                    // -> we don't want to show a warning every single time,
                    //    because then a lot of processing power gets wasted on
                    //    logging, which will make the queue pile up even more.
                    // -> instead we show it every 10s, so that the system can
                    //    use most it's processing power to hopefully process it.
                    if (messageQueue.Count > MessageQueueSizeWarning)
                    {
                        TimeSpan elapsed = DateTime.Now - messageQueueLastWarning;
                        if (elapsed.TotalSeconds > 10)
                        {
                            Logger.LogWarning("ReceiveLoop: messageQueue is getting big(" + messageQueue.Count + "), try calling GetNextMessage more often. You can call it more than once per frame!");
                            messageQueueLastWarning = DateTime.Now;
                        }
                    }
                }
            }
            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("ReceiveLoop: finished receive function for connectionId=" + connectionId + " reason: " + exception);
            }

            // if we got here then either the client while loop ended, or an
            // exception happened. disconnect
            messageQueue.Enqueue(new Message(connectionId, EventType.Disconnected, null, 0));

            // clean up no matter what
            stream.Close();
            client.Close();
        }