예제 #1
0
        private async Task <object> SendMethodCall(MethodCache methodCache, object[] parameters)
        {
            if (methodCache == null)
            {
                throw new ArgumentException("The parameter cannot be null.", nameof(methodCache));
            }
            if (methodCache == null)
            {
                throw new ArgumentException("The parameter cannot be null.", nameof(parameters));
            }

            //PROTOCOL
            //CALL:
            //HEAD      - 4 bytes                   - Identifier, ASCII (NTC1)
            //HEAD      - integer                   - callback identifier
            //HEAD      - uinteger                  - The method identifier
            //HEAD      - integer * parameters      - the length of each parameter
            //--------------------------------------------------------------------------
            //DATA      - length of the parameters  - serialized parameters

            var callbackId = (uint)Interlocked.Increment(ref _callIdCounter);

            var buffer = new byte[CustomOffset /* user offset */ + 4 /* Header */ + 4 /* Callback id */ +
                                  4 /* method id */ + parameters.Length * 4 /* parameter meta */ +
                                  EstimatedDataPerParameter * parameters.Length /* parameter data */];
            var bufferOffset = CustomOffset + 12 + parameters.Length * 4;

            for (var i = 0; i < parameters.Length; i++)
            {
                var metaOffset      = CustomOffset + 12 + i * 4;
                var parameterLength = _serializer.Serialize(methodCache.ParameterTypes[i],
                                                            ref buffer, bufferOffset, parameters[i]);
                Buffer.BlockCopy(BitConverter.GetBytes(parameterLength), 0, buffer, metaOffset, 4);

                bufferOffset += parameterLength;
            }

            //write header
            buffer[CustomOffset]     = CallProtocolInfo.Header1;
            buffer[CustomOffset + 1] = CallProtocolInfo.Header2;
            buffer[CustomOffset + 2] = CallProtocolInfo.Header3Call;
            buffer[CustomOffset + 3] = CallProtocolInfo.Header4;

            //write callback id
            Buffer.BlockCopy(BitConverter.GetBytes(callbackId), 0, buffer, CustomOffset + 4, 4);

            //method identifier
            Buffer.BlockCopy(BitConverter.GetBytes(methodCache.MethodId), 0, buffer, CustomOffset + 8, 4);

            var callback     = new ResultCallback();
            var callbackWait = callback.Wait(WaitTimeout);

            _callbacks.TryAdd(callbackId, callback);                               //impossible that this goes wrong

            OnSendData(new ArraySegment <byte>(buffer, 0, bufferOffset)).Forget(); //no need to await that

            using (callback)
            {
                if (!await callbackWait.ConfigureAwait(false))
                {
                    _callbacks.TryRemove(callbackId, out var _);
                    throw new TimeoutException("The method call timed out, no response received.");
                }

                switch (callback.ResponseType)
                {
                case CallTransmissionResponseType.MethodExecuted:
                    return(null);

                case CallTransmissionResponseType.ResultReturned:
                    return(_serializer.Deserialize(methodCache.ReturnType, callback.Data,
                                                   callback.Offset));

                case CallTransmissionResponseType.Exception:
                    var up = _serializer.DeserializeException(callback.Data, callback.Offset);
                    throw up;

                case CallTransmissionResponseType.MethodNotImplemented:
                    throw new NotImplementedException("The remote method is not implemented.");

                default:
                    throw new ArgumentOutOfRangeException();
                }
            }
        }
예제 #2
0
        /// <summary>
        ///     Called when data was received by the client side
        /// </summary>
        /// <param name="buffer">The array of unsigned bytes which contains the information to execute the method.</param>
        /// <param name="offset">The index into buffer at which the data begins</param>
        /// <returns>Returns the answer which should be sent back to the client</returns>
        public async Task <BufferSegment> ReceiveData(byte[] buffer, int offset)
        {
            //PROTOCOL
            //CALL:
            //HEAD      - 4 bytes                   - Identifier, ASCII (NTC1)
            //HEAD      - integer                   - callback identifier
            //HEAD      - uinteger                  - The method identifier
            //HEAD      - integer * parameters      - the length of each parameter
            //--------------------------------------------------------------------------
            //DATA      - length of the parameters  - serialized parameters
            //
            //RETURN:
            //HEAD      - 4 bytes                   - Identifier, ASCII (NTR1)
            //HEAD      - integer                   - callback identifier
            //HEAD      - 1 byte                    - the response type (0 = executed, 1 = result returned, 2 = exception, 3 = not implemented)
            //(BODY     - return object length      - the serialized return object)

            if (buffer[offset++] != CallProtocolInfo.Header1 || buffer[offset++] != CallProtocolInfo.Header2 ||
                buffer[offset++] != CallProtocolInfo.Header3Call)
            {
                throw new ArgumentException("Invalid package format. Invalid header.");
            }

            if (buffer[offset++] != 1)
            {
                throw new NotSupportedException($"The version {buffer[offset - 1]} is not supported.");
            }

            var id = BitConverter.ToUInt32(buffer, offset + 4);

            void WriteResponseHeader(byte[] data)
            {
                data[CustomOffset]     = CallProtocolInfo.Header1;
                data[CustomOffset + 1] = CallProtocolInfo.Header2;
                data[CustomOffset + 2] = CallProtocolInfo.Header3Return;
                data[CustomOffset + 3] = CallProtocolInfo.Header4;
                Buffer.BlockCopy(buffer, offset, data, CustomOffset + 4, 4); //copy callback id
            }

            //method not found/implemented
            if (!Cache.MethodInvokers.TryGetValue(id, out var methodInvoker))
            {
                var responseLength = CustomOffset /* user offset */ + 8 /* Header */ + 1 /* response type */;
                var response       = Cache.BufferManager.TakeBuffer(responseLength);
                WriteResponseHeader(response);
                response[CustomOffset + 8] = (byte)CallTransmissionResponseType.MethodNotImplemented;

                return(new BufferSegment(response, 0, responseLength, Cache.BufferManager));
            }

            var parameters      = new object[methodInvoker.ParameterCount];
            var parameterOffset = offset + 8 + parameters.Length * 4;

            for (var i = 0; i < methodInvoker.ParameterCount; i++)
            {
                var type            = methodInvoker.ParameterTypes[i];
                var parameterLength = BitConverter.ToInt32(buffer, offset + 8 + i * 4);

                parameters[i]    = _serializer.Deserialize(type, buffer, parameterOffset);
                parameterOffset += parameterLength;
            }

            Task task;

            try
            {
                task = methodInvoker.Invoke(_interfaceImplementation, parameters);
                await task.ConfigureAwait(false);
            }
            catch (Exception e)
            {
                var responseLength = CustomOffset /* user offset */ + 8 /* Header */ + 1 /* response type */ +
                                     EstimatedResultBufferSize /* exception */;
                var takenBuffer = Cache.BufferManager.TakeBuffer(responseLength);
                var response    = takenBuffer;

                var length = _serializer.SerializeException(ref response, CustomOffset + 9, e);

                WriteResponseHeader(response);
                response[CustomOffset + 8] = (byte)CallTransmissionResponseType.Exception;

                if (takenBuffer == response)
                {
                    return(new BufferSegment(response, 0, length + 9 + CustomOffset, Cache.BufferManager));
                }

                Cache.BufferManager.ReturnBuffer(takenBuffer);
                return(new BufferSegment(response, 0, length + 9 + CustomOffset));
            }

            if (methodInvoker.ReturnsResult)
            {
                var result = methodInvoker.TaskReturnPropertyInfo.GetValue(task);

                var takenBuffer = Cache.BufferManager.TakeBuffer(CustomOffset + EstimatedResultBufferSize);
                var response    = takenBuffer;

                WriteResponseHeader(response);
                response[CustomOffset + 8] = (byte)CallTransmissionResponseType.ResultReturned;

                var responseLength = _serializer.Serialize(methodInvoker.ReturnType, ref response, CustomOffset + 9,
                                                           result);

                if (takenBuffer == response)
                {
                    return(new BufferSegment(response, 0, responseLength + CustomOffset + 9, Cache.BufferManager));
                }

                Cache.BufferManager.ReturnBuffer(takenBuffer);
                return(new BufferSegment(response, 0, responseLength + CustomOffset + 9));
            }
            else
            {
                var responseLength = CustomOffset /* user offset */ + 8 /* Header */ + 1 /* response type */;
                var response       = Cache.BufferManager.TakeBuffer(responseLength);
                WriteResponseHeader(response);
                response[CustomOffset + 8] = (byte)CallTransmissionResponseType.MethodExecuted;
                return(new BufferSegment(response, 0, responseLength, Cache.BufferManager));
            }
        }