protected override void ProcessPackage(PendingPackage package)
        {
            // Locked by TcpConnection.

            if (!_connected && package.Type != PackageType.Handshake)
            {
                SendError(ProtocolError.InvalidPackageType);
                return;
            }

            switch (package.Type)
            {
                case PackageType.Handshake:
                    ProcessHandshake(package);
                    break;

                default:
                    base.ProcessPackage(package);
                    break;
            }
        }
        private void ProcessHandshake(PendingPackage package)
        {
            // Receive the handshake response.

            var response = (Messages.HandshakeResponse)ReadMessage(
                TypeModel, typeof(Messages.HandshakeResponse), (int)package.Length
            );

            // Validate the protocol number.

            int protocolNumber = (int)response.Protocol;

            if (
                protocolNumber < _host.Configuration.MinimumProtocolNumber ||
                protocolNumber > _host.Configuration.MaximumProtocolNumber
            ) {
                SendError(ProtocolError.InvalidProtocol);
            }
            else
            {
                // Else, we've got a valid connection and can proceed with
                // creating the service client.

                // Create the callback channel so we can provide it with the
                // operation context.

                if (_host.Service.CallbackContractType != null)
                {
                    // Else, if we have a callback contract, we create the
                    // channel.

                    CallbackChannel = (ProtoCallbackChannel)Activator.CreateInstance(
                        _host.Service.CallbackContractType
                    );

                    CallbackChannel.Connection = this;
                }

                using (OperationContext.SetScope(new OperationContext(this, CallbackChannel)))
                {
                    Client = _host.RaiseClientConnected(this, protocolNumber);
                }

                if (Client == null)
                {
                    // When creating the client failed, we shut down because there's
                    // nothing more to do.

                    Dispose();
                }
            }
        }
        private void ProcessHandshake(PendingPackage package)
        {
            // Receive the handshake request.

            var request = (Messages.HandshakeRequest)ReadMessage(
                TypeModel, typeof(Messages.HandshakeRequest), (int)package.Length
            );

            // Ask the client which protocol we're going to connect with.

            int protocol = _client.ChooseProtocol((int)request.ProtocolMin, (int)request.ProtocolMax);

            // Push our response

            long packageStart = BeginSendPackage();

            WriteMessage(TypeModel, new Messages.HandshakeResponse
            {
                Protocol = (uint)protocol
            });

            EndSendPackage(PackageType.Handshake, packageStart);

            _connected = true;

            // Once we're connected, we can go into async mode.

            IsAsync = true;

            Read();
        }
        protected override void ProcessPackage(PendingPackage package)
        {
            // Locked by TcpConnection.

            switch (_state)
            {
                case State.Authenticating:
                case State.ReceivingProlog:
                    SendError(ProtocolError.InvalidPackageType);
                    return;

                case State.ReceivingHandshake:
                    if (package.Type != PackageType.Handshake)
                    {
                        SendError(ProtocolError.InvalidPackageType);
                        return;
                    }
                    break;

                case State.Connected:
                    break;

                default:
                    throw new NotSupportedException("Invalid state");
            }

            switch (package.Type)
            {
                case PackageType.Handshake:
                    ProcessHandshake(package);
                    break;

                default:
                    base.ProcessPackage(package);
                    break;
            }
        }
        private void ProcessStreamPackage(PendingPackage package)
        {
            if (package.Length < 4)
            {
                SendError(ProtocolError.InvalidPackageLength);
                return;
            }

            // Parse the header.

            byte[] buffer = new byte[4];

            buffer[0] = 0;

            Read(buffer, 0, buffer.Length);

            uint header = BitConverterEx.ToNetworkUInt32(buffer, 0);

            // Get the details from the header.

            uint streamPackageTypeNumber = header & 0x7;
            int associationId = (int)(header >> 3);

            // Process the stream package.

            switch ((StreamPackageType)streamPackageTypeNumber)
            {
                case StreamPackageType.StartStream:
                    ProcessStartStreamPackage(associationId, (int)package.Length - 4);
                    break;

                case StreamPackageType.AcceptStream:
                    ProcessAcceptStreamPackage(associationId);
                    break;

                case StreamPackageType.RejectStream:
                    ProcessRejectStreamPackage(associationId);
                    break;

                case StreamPackageType.StreamData:
                    ProcessStreamDataPackage(associationId, (int)package.Length - 4);
                    break;

                case StreamPackageType.EndStream:
                case StreamPackageType.StreamFailed:
                    ProcessEndStreamPackage(associationId, (StreamPackageType)streamPackageTypeNumber != StreamPackageType.StreamFailed);
                    break;

                default:
                    SendError(ProtocolError.InvalidStreamPackageType);
                    break;
            }
        }
        private void ProcessMessagePackage(PendingPackage package)
        {
            // We need at least three bytes for a valid message.

            if (package.Length < 4)
            {
                SendError(ProtocolError.InvalidPackageLength);
                return;
            }

            uint length = package.Length - 4;

            byte[] buffer = new byte[4];

            buffer[0] = 0;

            Read(buffer, 0, buffer.Length);

            uint header = BitConverterEx.ToNetworkUInt32(buffer, 0);

            uint messageKindNumber = header & 0x3;
            uint messageType = header >> 2;

            // Validate the message kind.

            if (messageKindNumber == 3)
            {
                SendError(ProtocolError.InvalidMessageKind);
                return;
            }

            var messageKind = (MessageKind)messageKindNumber;
            uint associationId = 0;

            if (messageKind != MessageKind.OneWay)
            {
                // Verify that there is an association ID in the request.

                if (package.Length < 5)
                {
                    SendError(ProtocolError.InvalidPackageLength);
                    return;
                }

                length -= 2;

                Read(buffer, 0, 2);

                associationId = BitConverterEx.ToNetworkUInt16(buffer, 0);
            }

            ProcessMessage(messageKind, messageType, length, associationId);
        }
        private void ProcessErrorPackage(PendingPackage package)
        {
            var error = (Messages.Error)ReadMessage(
                TypeModel, typeof(Messages.Error), (int)package.Length
            );

            var exception = new ProtoChannelException((ProtocolError)error.ErrorNumber);

            _messageManager.SetError(exception);
            _receiveStreamManager.SetError(exception);

            RaiseUnhandledException(exception);

            Dispose();
        }
        protected virtual void ProcessPackage(PendingPackage package)
        {
            switch (package.Type)
            {
                case PackageType.Error:
                    ProcessErrorPackage(package);
                    break;

                case PackageType.NoOp:
                case PackageType.Pong:
                    // No-op's and pongs are just dropped.
                    break;

                case PackageType.Message:
                    ProcessMessagePackage(package);
                    break;

                case PackageType.Stream:
                    ProcessStreamPackage(package);
                    break;

                case PackageType.Ping:
                    EndSendPackage(PackageType.Pong, BeginSendPackage());
                    break;

                default:
                    throw new NotSupportedException("Invalid package type");
            }
        }