/// <summary>
        /// Initializes a new instance of the <see cref="PeerCommunicator" /> class.
        /// </summary>
        /// <param name="throttlingManager">The throttling manager.</param>
        /// <param name="tcp">The TCP.</param>
        public PeerCommunicator(ThrottlingManager throttlingManager, TcpClient tcp)
        {
            throttlingManager.CannotBeNull();
            tcp.CannotBeNull();

            this.PieceData = null;

            this.tm  = throttlingManager;
            this.tcp = tcp;

            var data = new AsyncReadData(this.tcp.ReceiveBufferSize);

            this.stream = this.tcp.GetStream();
            this.stream.BeginRead(data.Buffer, data.OffsetStart, data.Buffer.Length, this.Receive, data);

            this.Endpoint = this.tcp.Client.RemoteEndPoint as IPEndPoint;
        }
        /// <summary>
        /// Asynchronous write callback.
        /// </summary>
        /// <param name="ar">The async result.</param>
        private void Receive(IAsyncResult ar)
        {
            AsyncReadData data      = ar.AsyncState as AsyncReadData;
            int           bytesRead = 0;
            int           offset    = data.OffsetStart;
            PeerMessage   message;
            PieceMessage  pieceMessage;
            bool          isIncomplete;

            lock (this.locker)
            {
                if (!this.IsDisposed)
                {
                    try
                    {
                        // read data
                        bytesRead = this.stream.EndRead(ar);

                        if (bytesRead > 0)
                        {
                            data.OffsetEnd += bytesRead;

                            // walk through the array and try to decode messages
                            while (offset <= data.OffsetEnd)
                            {
                                if (PieceMessage.TryDecode(data.Buffer, ref offset, data.OffsetEnd, out pieceMessage, out isIncomplete, this.PieceData))
                                {
                                    // successfully decoded message
                                    this.OnMessageReceived(this, new PeerMessgeReceivedEventArgs((PeerMessage)pieceMessage));

                                    // remember where we left off
                                    data.OffsetStart = offset;
                                }
                                else if (PeerMessage.TryDecode(data.Buffer, ref offset, data.OffsetEnd, out message, out isIncomplete))
                                {
                                    // successfully decoded message
                                    this.OnMessageReceived(this, new PeerMessgeReceivedEventArgs(message));

                                    // remember where we left off
                                    data.OffsetStart = offset;
                                }
                                else if (isIncomplete)
                                {
                                    // message of variable length is present but incomplete -> stop advancing
                                    break;
                                }
                                else
                                {
                                    // move to next byte
                                    offset++;
                                }
                            }

                            if (data.OffsetStart == data.OffsetEnd)
                            {
                                // reset offset
                                data.OffsetStart = 0;
                                data.OffsetEnd   = 0;
                            }
                            else if (data.OffsetStart > 0 &&
                                     data.OffsetStart < data.OffsetEnd)
                            {
                                // move data to beginning of the buffere
                                for (int i = data.OffsetStart; i < data.OffsetEnd; i++)
                                {
                                    data.Buffer[i - data.OffsetStart] = data.Buffer[i];
                                }

                                // reset offset
                                data.OffsetEnd   = data.OffsetEnd - data.OffsetStart;
                                data.OffsetStart = 0;
                            }
                            else if (data.OffsetStart > data.OffsetEnd)
                            {
                                throw new PeerWireProtocolException("Invalid data.");
                            }

                            this.tm.Read(bytesRead);

                            // resume reading
                            if (!this.IsDisposed)
                            {
                                this.stream.BeginRead(data.Buffer, data.OffsetEnd, data.Buffer.Length - data.OffsetEnd, this.Receive, data);
                            }
                        }
                        else
                        {
                            // we received no data
                            Debug.WriteLine($"received no data from {this.Endpoint}");
                        }
                    }
                    catch (IOException ex)
                    {
                        Debug.WriteLine($"could not read data from {this.Endpoint}: {ex.Message}");

                        this.OnCommunicationError(this, new CommunicationErrorEventArgs(ex.Message));
                    }
                }
            }
        }