private void OnAcceptted(ListeningContext context) { if (context.AcceptSocket == null || context.AcceptSocket.RemoteEndPoint == null) { // Canceled due to shutdown. return; } #if !API_SIGNATURE_TEST MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.EndAccept, "Accept. {{ \"Socket\" : 0x{0:X}, \"RemoteEndPoint\" : \"{1}\", \"LocalEndPoint\" : \"{2}\" }}", ServerTransport.GetHandle(context.AcceptSocket), ServerTransport.GetRemoteEndPoint(context.AcceptSocket, context), ServerTransport.GetLocalEndPoint(context.AcceptSocket) ); #endif Contract.Assert(context.BytesTransferred == 0, context.BytesTransferred.ToString()); var transport = this.GetTransport(context.AcceptSocket); context.AcceptSocket = null; this.Accept(context); transport.Receive(this.GetRequestContext(transport)); }
/// <summary> /// Unpack request/notification message array header. /// </summary> /// <param name="context">Context information.</param> /// <returns> /// <c>true</c>, if the pipeline is finished; /// <c>false</c>, the pipeline is interruppted because extra data is needed. /// </returns> internal bool UnpackRequestHeader(ServerRequestContext context) { Contract.Assert(context != null); if (context.RootUnpacker == null) { context.UnpackingBuffer = new ByteArraySegmentStream(context.ReceivedData); context.RootUnpacker = Unpacker.Create(context.UnpackingBuffer, false); Interlocked.Increment(ref this._processing); context.RenewSessionId(); if (this._manager.Server.Configuration.ReceiveTimeout != null) { context.Timeout += this.OnReceiveTimeout; context.StartWatchTimeout(this._manager.Server.Configuration.ReceiveTimeout.Value); } if (MsgPackRpcServerProtocolsTrace.ShouldTrace(MsgPackRpcServerProtocolsTrace.NewSession)) { MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.NewSession, "New session is created. {{ \"SessionID\" : {0} }}", context.SessionId ); } } if (!context.ReadFromRootUnpacker()) { MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.NeedRequestHeader, "Array header is needed. {{ \"SessionID\" : {0} }}", context.SessionId ); return(false); } if (!context.RootUnpacker.IsArrayHeader) { this.HandleDeserializationError(context, "Invalid request/notify message stream. Message must be array.", () => context.UnpackingBuffer.ToArray()); return(true); } if (context.RootUnpacker.ItemsCount != 3 && context.RootUnpacker.ItemsCount != 4) { this.HandleDeserializationError( context, String.Format( CultureInfo.CurrentCulture, "Invalid request/notify message stream. Message must be valid size array. Actual size is {0}.", context.RootUnpacker.ItemsCount ), () => context.UnpackingBuffer.ToArray() ); return(true); } context.HeaderUnpacker = context.RootUnpacker.ReadSubtree(); context.NextProcess = UnpackMessageType; return(context.NextProcess(context)); }
private void OnCompleted(object sender, SocketAsyncEventArgs e) { var socket = sender as Socket; if (!this.HandleSocketError(socket, e)) { return; } switch (e.LastOperation) { case SocketAsyncOperation.Accept: { var context = e as ListeningContext; Contract.Assert(context != null); this.OnAcceptted(context); break; } default: { #if !API_SIGNATURE_TEST MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.UnexpectedLastOperation, "Unexpected operation. {{ \"Socket\" : 0x{0:X}, \"RemoteEndPoint\" : \"{1}\", \"LocalEndPoint\" : \"{2}\", \"LastOperation\" : \"{3}\" }}", ServerTransport.GetHandle(socket), ServerTransport.GetRemoteEndPoint(socket, e), ServerTransport.GetLocalEndPoint(socket), e.LastOperation ); #endif break; } } }
/// <summary> /// Unpack Message ID part on request message. /// </summary> /// <param name="context">Context information.</param> /// <returns> /// <c>true</c>, if the pipeline is finished; /// <c>false</c>, the pipeline is interruppted because extra data is needed. /// </returns> private bool UnpackMessageId(ServerRequestContext context) { Contract.Assert(context != null); if (!context.ReadFromHeaderUnpacker()) { MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.NeedMessageId, "Message ID is needed. {{ \"SessionID\" : {0} }}", context.SessionId ); return(false); } try { context.MessageId = unchecked (( int )context.HeaderUnpacker.LastReadData.AsUInt32()); } catch (InvalidOperationException) { this.HandleDeserializationError( context, "Invalid request message stream. ID must be UInt32 compatible integer.", () => context.UnpackingBuffer.ToArray() ); return(true); } context.NextProcess = this.UnpackMethodName; return(context.NextProcess(context)); }
/// <summary> /// Unpack array elements of Arguments part on request/notification message. /// </summary> /// <param name="context">Context information.</param> /// <returns> /// <c>true</c>, if the pipeline is finished; /// <c>false</c>, the pipeline is interruppted because extra data is needed. /// </returns> private bool UnpackArguments(ServerRequestContext context) { Contract.Assert(context != null); while (context.UnpackedArgumentsCount < context.ArgumentsCount) { if (!context.ReadFromArgumentsBufferUnpacker()) { MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.NeedArgumentsElement, "Arguments array element is needed. {0}/{1} {{ \"SessionID\" : {2} }}", context.UnpackedArgumentsCount, context.ArgumentsCount, context.SessionId ); return(false); } context.ArgumentsBufferPacker.Pack(context.ArgumentsBufferUnpacker.LastReadData); context.UnpackedArgumentsCount++; } context.ArgumentsBuffer.Position = 0; context.ArgumentsUnpacker = Unpacker.Create(context.ArgumentsBuffer, false); context.NextProcess = this.Dispatch; return(context.NextProcess(context)); }
/// <summary> /// Unpack Method Name part on request/notification message. /// </summary> /// <param name="context">Context information.</param> /// <returns> /// <c>true</c>, if the pipeline is finished; /// <c>false</c>, the pipeline is interruppted because extra data is needed. /// </returns> private bool UnpackMethodName(ServerRequestContext context) { Contract.Assert(context != null); if (!context.ReadFromHeaderUnpacker()) { MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.NeedMethodName, "Method Name is needed. {{ \"SessionID\" : {0} }}", context.SessionId ); return(false); } try { context.MethodName = context.HeaderUnpacker.LastReadData.AsString(); } catch (InvalidOperationException) { this.HandleDeserializationError( context, "Invalid request/notify message stream. Method name must be UTF-8 string.", () => context.UnpackingBuffer.ToArray() ); return(true); } context.NextProcess = this.UnpackArgumentsHeader; return(context.NextProcess(context)); }
/// <summary> /// Unpack Message Type part on request/notification message. /// </summary> /// <param name="context">Context information.</param> /// <returns> /// <c>true</c>, if the pipeline is finished; /// <c>false</c>, the pipeline is interruppted because extra data is needed. /// </returns> private bool UnpackMessageType(ServerRequestContext context) { Contract.Assert(context != null); if (!context.ReadFromHeaderUnpacker()) { MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.NeedMessageType, "Message Type is needed. {{ \"SessionID\" : {0} }}", context.SessionId ); return(false); } int numericType; try { numericType = context.HeaderUnpacker.LastReadData.AsInt32(); } catch (InvalidOperationException) { this.HandleDeserializationError(context, "Invalid request/notify message stream. Message Type must be Int32 compatible integer.", () => context.UnpackingBuffer.ToArray()); return(true); } MessageType type = ( MessageType )numericType; context.MessageType = type; switch (type) { case MessageType.Request: { context.NextProcess = this.UnpackMessageId; break; } case MessageType.Notification: { context.NextProcess = this.UnpackMethodName; break; } default: { this.HandleDeserializationError( context, String.Format(CultureInfo.CurrentCulture, "Unknown message type '{0:x8}'", numericType), () => context.UnpackingBuffer.ToArray() ); return(true); } } return(context.NextProcess(context)); }
/// <summary> /// Handles the socket error as server error. /// </summary> /// <param name="socket">The <see cref="Socket"/> caused error.</param> /// <param name="context">The <see cref="SocketAsyncEventArgs"/> instance containing the asynchronous operation data.</param> /// <returns> /// <c>true</c>, if the error can be ignore, it is in shutdown which is initiated by another thread, for example; otherwise, <c>false</c>. /// </returns> /// <exception cref="ArgumentNullException"> /// <paramref name="socket"/> is <c>null</c>. /// Or <paramref name="context"/> is <c>null</c>. /// </exception> /// <remarks> /// When this method returns <c>false</c>, <see cref="RpcServer.ServerError"/> event will be also ocurred. /// </remarks> protected internal bool HandleSocketError(Socket socket, SocketAsyncEventArgs context) { if (socket == null) { throw new ArgumentNullException("socket"); } if (context == null) { throw new ArgumentNullException("context"); } Contract.EndContractBlock(); bool?isError = context.SocketError.IsError(); if (isError == null) { MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.IgnoreableError, "Ignoreable error. {{ \"Socket\" : 0x{0:X}, \"RemoteEndpoint\" : \"{1}\", \"LocalEndpoint\" : \"{2}\", \"LastOperation\" : \"{3}\", \"SocketError\" : \"{4}\", \"ErrorCode\" : 0x{5:X} }}", ServerTransport.GetHandle(socket), ServerTransport.GetRemoteEndPoint(socket, context), ServerTransport.GetLocalEndPoint(socket), context.LastOperation, context.SocketError, ( int )context.SocketError ); return(true); } else if (isError.GetValueOrDefault()) { var errorDetail = String.Format( CultureInfo.CurrentCulture, "Socket error. {{ \"Socket\" : 0x{0:X}, \"RemoteEndpoint\" : \"{1}\", \"LocalEndpoint\" : \"{2}\", \"LastOperation\" : \"{3}\", \"SocketError\" : \"{4}\", \"ErrorCode\" : 0x{5:X} }}", ServerTransport.GetHandle(socket), ServerTransport.GetRemoteEndPoint(socket, context), ServerTransport.GetLocalEndPoint(socket), context.LastOperation, context.SocketError, ( int )context.SocketError ); MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.SocketError, errorDetail ); this.RaiseServerError(new RpcTransportException(context.SocketError.ToRpcError(), "Socket error.", errorDetail, new SocketException(( int )context.SocketError))); return(false); } return(true); }
/// <summary> /// Releases unmanaged and - optionally - managed resources /// </summary> /// <param name="disposing"> /// <c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources. /// </param> private void DisposeOnce(bool disposing) { if (Interlocked.CompareExchange(ref this._isDisposed, 1, 0) == 0) { MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.DisposeManager, "Dispose. {{ \"Manager\" : \"{0}\" }}", this ); this.Dispose(disposing); } }
/// <summary> /// Unpack array header of Arguments part on request/notification message. /// </summary> /// <param name="context">Context information.</param> /// <returns> /// <c>true</c>, if the pipeline is finished; /// <c>false</c>, the pipeline is interruppted because extra data is needed. /// </returns> private bool UnpackArgumentsHeader(ServerRequestContext context) { Contract.Assert(context != null); if (!context.ReadFromHeaderUnpacker()) { MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.NeedArgumentsArrayHeader, "Arguments array header is needed. {{ \"SessionID\" : {0} }}", context.SessionId ); return(false); } if (!context.HeaderUnpacker.IsArrayHeader) { this.HandleDeserializationError( context, "Invalid request/notify message stream. Arguments must be array.", () => context.UnpackingBuffer.ToArray() ); return(true); } // TODO: Avoid actual unpacking to improve performance. context.ArgumentsBufferUnpacker = context.HeaderUnpacker.ReadSubtree(); if (Int32.MaxValue < context.ArgumentsBufferUnpacker.ItemsCount) { this.HandleDeserializationError( context, RpcError.MessageTooLargeError, "Too many arguments.", context.ArgumentsBufferUnpacker.ItemsCount.ToString("#,0", CultureInfo.CurrentCulture), () => context.UnpackingBuffer.ToArray() ); return(true); } context.ArgumentsCount = unchecked (( int )(context.ArgumentsBufferUnpacker.ItemsCount)); context.ArgumentsBufferPacker = Packer.Create(context.ArgumentsBuffer, false); context.ArgumentsBufferPacker.PackArrayHeader(context.ArgumentsCount); context.UnpackedArgumentsCount = 0; context.NextProcess = this.UnpackArguments; return(context.NextProcess(context)); }
/// <summary> /// Initiates server shutdown process. /// </summary> /// <returns> /// If shutdown process is initiated, then <c>true</c>. /// If shutdown is already initiated or completed, then <c>false</c>. /// </returns> /// <remarks> /// To observe shutdown completion, subscribe <see cref="ShutdownCompleted"/> event. /// </remarks> public bool BeginShutdown() { if (Interlocked.Exchange(ref this._isInShutdown, 1) == 0) { MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.BeginShutdownManager, "Begin shutdown. {{ \"Manager\" : \"{0}\" }}", this ); this.BeginShutdownCore(); return(true); } else { return(false); } }
/// <summary> /// When overridden in derived class, releases unmanaged and - optionally - managed resources /// </summary> /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> protected override void Dispose(bool disposing) { try { this.IsActive = false; base.Dispose(disposing); this._listeningSocket.Close(); } finally { if (this.Configuration.UdpListenerThreadExitTimeout == null) { this._listeningThread.Join(); } else { if (!this._listeningThread.Join(this.Configuration.UdpListenerThreadExitTimeout.Value)) { this._listeningThread.Interrupt(); if (!this._listeningThread.Join(this.Configuration.UdpListenerThreadExitTimeout.Value)) { // Suspend is needed to capture stack trace. #pragma warning disable 0612,0618 this._listeningThread.Suspend(); #pragma warning restore 0612,0618 #if !API_SIGNATURE_TEST MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.FailedToStopUdpServerManagerListenerThread, "Failed to stop the UDP server manager listener thread. Stack trace: {0}{1}", Environment.NewLine, new StackTrace(this._listeningThread, true) ); #endif // Suspend was needed to capture stack trace. #pragma warning disable 0612,0618 this._listeningThread.Resume(); #pragma warning restore 0612,0618 } } } } }
/// <summary> /// Raises <see cref="ShutdownCompleted"/> event. /// </summary> /// <param name="e">The <see cref="MsgPack.Rpc.Protocols.ShutdownCompletedEventArgs"/> instance containing the event data.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="e"/> is <c>null</c>. /// </exception> protected virtual void OnShutdownCompleted(ShutdownCompletedEventArgs e) { if (e == null) { throw new ArgumentNullException("e"); } Contract.EndContractBlock(); MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.ManagerShutdownCompleted, "Manager shutdown is completed. {{ \"Manager\" : \"{0}\" }}", this ); var handler = Interlocked.CompareExchange(ref this._shutdownCompleted, null, null); if (handler != null) { handler(this, e); } }
private void Accept(ListeningContext context) { // Ensure buffers are cleared to avoid unepxected data feeding on Accept context.SetBuffer(null, 0, 0); context.BufferList = null; try { if (this.IsInShutdown) { return; } #if !API_SIGNATURE_TEST MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.BeginAccept, "Wait for connection. {{ \"Socket\" : 0x{0:X}, \"LocalEndPoint\" : \"{1}\" }}", this._listeningSocket.Handle, this._listeningSocket.LocalEndPoint ); #endif if (!this._listeningSocket.AcceptAsync(context)) { // Avoid recursive acceptance and the subsequent request delay. // Task is bit heavy here. ThreadPool.QueueUserWorkItem(_ => this.OnAcceptted(context)); } } catch (ObjectDisposedException) { if (!this.IsDisposed) { throw; } } }
/// <summary> /// Initializes a new instance of the <see cref="UdpServerTransportManager"/> class. /// </summary> /// <param name="server">The server which will host this instance.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="server"/> is <c>null</c>. /// </exception> public UdpServerTransportManager(RpcServer server) : base(server) { #if !API_SIGNATURE_TEST base.SetTransportPool(server.Configuration.UdpTransportPoolProvider(() => new UdpServerTransport(this), server.Configuration.CreateTransportPoolConfiguration())); #endif var addressFamily = (server.Configuration.PreferIPv4 || !Socket.OSSupportsIPv6) ? AddressFamily.InterNetwork : AddressFamily.InterNetworkV6; var bindingEndPoint = this.Configuration.BindingEndPoint; #if !API_SIGNATURE_TEST if (bindingEndPoint == null) { bindingEndPoint = new IPEndPoint( #if MONO this.Configuration.PreferIPv4 && Socket.SupportsIPv4 #else this.Configuration.PreferIPv4 && Socket.OSSupportsIPv4 #endif ? IPAddress.Any : IPAddress.IPv6Any, 57319 // arbitrary number ); MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.DefaultEndPoint, "Default end point is selected. {{ \"EndPoint\" : \"{0}\", \"AddressFamily\" : {1}, \"PreferIPv4\" : {2}, \"OSSupportsIPv6\" : {3} }}", bindingEndPoint, addressFamily, server.Configuration.PreferIPv4, Socket.OSSupportsIPv6 ); } #endif this._bindingEndPoint = bindingEndPoint; this._listeningEndPoint = new IPEndPoint(addressFamily == AddressFamily.InterNetworkV6 ? IPAddress.IPv6Any : IPAddress.Any, 0); this._listeningSocket = new Socket( this._bindingEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp ); if (!this.Configuration.PreferIPv4 && Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version.Major >= 6 #if MONO && Socket.SupportsIPv6 #else && Socket.OSSupportsIPv6 #endif ) { // Listen both of IPv4 and IPv6 this._listeningSocket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0); } this._listeningSocket.Bind(this._bindingEndPoint); this._listeningThread = new Thread(this.PollArrival) { IsBackground = true, Name = "UdpListeningThread#" + this.GetHashCode() }; this.IsActive = true; this._listeningThread.Start(); }
private void PollArrival() { #if !API_SIGNATURE_TEST MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.StartListen, "Start listen. {{ \"Socket\" : 0x{0:X}, \"EndPoint\" : \"{1}\", \"ListenBackLog\" : {2} }}", this._listeningSocket.Handle, this._bindingEndPoint, this.Server.Configuration.ListenBackLog ); #endif try { var transport = this.GetTransport(this._listeningSocket); while (this.IsActive) { ServerRequestContext context; try { // CancellationToken is useless... context = this.GetRequestContext(transport); } catch (ThreadInterruptedException) { this.ReturnTransport(transport); if (this.IsActive) { throw; } else { return; } } catch (ObjectDisposedException) { this.ReturnTransport(transport); if (this.IsActive) { throw; } else { return; } } context.RemoteEndPoint = this._listeningEndPoint; if (!this.IsActive) { this.ReturnRequestContext(context); this.ReturnTransport(transport); return; } transport.Receive(context); } } #if !API_SIGNATURE_TEST catch (Exception ex) { MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.UdpServerManagerListenerThreadCrash, "The exception has been occurred in the UDP server manager listener thread. {0}", ex ); } #else catch { } #endif }
/// <summary> /// Initializes a new instance of the <see cref="TcpServerTransportManager"/> class. /// </summary> /// <param name="server">The server which will host this instance.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="server"/> is <c>null</c>. /// </exception> public TcpServerTransportManager(RpcServer server) : base(server) { #if !API_SIGNATURE_TEST base.SetTransportPool(server.Configuration.TcpTransportPoolProvider(() => new TcpServerTransport(this), server.Configuration.CreateTransportPoolConfiguration())); #endif this._listeningContextPool = server.Configuration.ListeningContextPoolProvider(() => new ListeningContext(), server.Configuration.CreateListeningContextPoolConfiguration()); var addressFamily = (server.Configuration.PreferIPv4 || !Socket.OSSupportsIPv6) ? AddressFamily.InterNetwork : AddressFamily.InterNetworkV6; var bindingEndPoint = this.Configuration.BindingEndPoint; #if !API_SIGNATURE_TEST if (bindingEndPoint == null) { bindingEndPoint = new IPEndPoint( #if MONO this.Configuration.PreferIPv4 && Socket.SupportsIPv4 #else this.Configuration.PreferIPv4 && Socket.OSSupportsIPv4 #endif ? IPAddress.Any : IPAddress.IPv6Any, 57129 // arbitrary number ); MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.DefaultEndPoint, "Default end point is selected. {{ \"EndPoint\" : \"{0}\", \"AddressFamily\" : {1}, \"PreferIPv4\" : {2}, \"OSSupportsIPv6\" : {3} }}", bindingEndPoint, addressFamily, server.Configuration.PreferIPv4, Socket.OSSupportsIPv6 ); } #endif this._listeningSocket = new Socket( bindingEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp ); #if !MONO if (!this.Configuration.PreferIPv4 && Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version.Major >= 6 && Socket.OSSupportsIPv6 ) { // Listen both of IPv4 and IPv6 this._listeningSocket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0); } #endif // !MONO this._listeningSocket.Bind(bindingEndPoint); this._listeningSocket.Listen(server.Configuration.ListenBackLog); #if !API_SIGNATURE_TEST MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.StartListen, "Start listen. {{ \"Socket\" : 0x{0:X}, \"EndPoint\" : \"{1}\", \"ListenBackLog\" : {2} }}", this._listeningSocket.Handle, bindingEndPoint, server.Configuration.ListenBackLog ); #endif this.StartAccept(); }