55using System ;
66using System . IO ;
77using System . IO . Pipes ;
8+ using System . Net ;
89using System . Net . Sockets ;
910using System . Runtime . InteropServices ;
1011using System . Threading ;
@@ -17,18 +18,30 @@ internal abstract class IpcServerTransport : IDisposable
1718 private IIpcServerTransportCallbackInternal _callback ;
1819 private bool _disposed ;
1920
20- public static IpcServerTransport Create ( string transportPath , int maxConnections )
21+ public static IpcServerTransport Create ( string address , int maxConnections , bool enableTcpIpProtocol , IIpcServerTransportCallbackInternal transportCallback = null )
2122 {
22- if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
23+ if ( ! enableTcpIpProtocol || ! IpcTcpSocketEndPoint . IsTcpIpEndPoint ( address ) )
2324 {
24- return new WindowsPipeServerTransport ( transportPath , maxConnections ) ;
25+ if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
26+ {
27+ return new IpcWindowsNamedPipeServerTransport ( address , maxConnections , transportCallback ) ;
28+ }
29+ else
30+ {
31+ return new IpcUnixDomainSocketServerTransport ( address , maxConnections , transportCallback ) ;
32+ }
2533 }
2634 else
2735 {
28- return new UnixDomainSocketServerTransport ( transportPath , maxConnections ) ;
36+ return new IpcTcpSocketServerTransport ( address , maxConnections , transportCallback ) ;
2937 }
3038 }
3139
40+ protected IpcServerTransport ( IIpcServerTransportCallbackInternal transportCallback = null )
41+ {
42+ _callback = transportCallback ;
43+ }
44+
3245 public void Dispose ( )
3346 {
3447 if ( ! _disposed )
@@ -45,18 +58,10 @@ protected virtual void Dispose(bool disposing)
4558
4659 public abstract Task < Stream > AcceptAsync ( CancellationToken token ) ;
4760
48- public static int MaxAllowedConnections
49- {
61+ public static int MaxAllowedConnections {
5062 get
5163 {
52- if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
53- {
54- return NamedPipeServerStream . MaxAllowedServerInstances ;
55- }
56- else
57- {
58- return ( int ) SocketOptionName . MaxConnections ;
59- }
64+ return - 1 ;
6065 }
6166 }
6267
@@ -73,13 +78,13 @@ internal void SetCallback(IIpcServerTransportCallbackInternal callback)
7378 _callback = callback ;
7479 }
7580
76- protected void OnCreateNewServer ( )
81+ protected void OnCreateNewServer ( EndPoint localEP )
7782 {
78- _callback ? . CreatedNewServer ( ) ;
83+ _callback ? . CreatedNewServer ( localEP ) ;
7984 }
8085 }
8186
82- internal sealed class WindowsPipeServerTransport : IpcServerTransport
87+ internal sealed class IpcWindowsNamedPipeServerTransport : IpcServerTransport
8388 {
8489 private const string PipePrefix = @"\\.\pipe\" ;
8590
@@ -89,11 +94,12 @@ internal sealed class WindowsPipeServerTransport : IpcServerTransport
8994 private readonly string _pipeName ;
9095 private readonly int _maxInstances ;
9196
92- public WindowsPipeServerTransport ( string pipeName , int maxInstances )
97+ public IpcWindowsNamedPipeServerTransport ( string pipeName , int maxInstances , IIpcServerTransportCallbackInternal transportCallback = null )
98+ : base ( transportCallback )
9399 {
94- _maxInstances = maxInstances ;
100+ _maxInstances = maxInstances != MaxAllowedConnections ? maxInstances : NamedPipeServerStream . MaxAllowedServerInstances ;
95101 _pipeName = pipeName . StartsWith ( PipePrefix ) ? pipeName . Substring ( PipePrefix . Length ) : pipeName ;
96- CreateNewPipeServer ( ) ;
102+ _stream = CreateNewNamedPipeServer ( _pipeName , _maxInstances ) ;
97103 }
98104
99105 protected override void Dispose ( bool disposing )
@@ -113,50 +119,50 @@ public override async Task<Stream> AcceptAsync(CancellationToken token)
113119 VerifyNotDisposed ( ) ;
114120
115121 using var linkedSource = CancellationTokenSource . CreateLinkedTokenSource ( token , _cancellation . Token ) ;
116-
117- NamedPipeServerStream connectedStream ;
118122 try
119123 {
124+ // Connect client to named pipe server stream.
120125 await _stream . WaitForConnectionAsync ( linkedSource . Token ) . ConfigureAwait ( false ) ;
121126
122- connectedStream = _stream ;
127+ // Transfer ownership of connected named pipe.
128+ var connectedStream = _stream ;
129+
130+ // Setup new named pipe server stream used in upcomming accept calls.
131+ _stream = CreateNewNamedPipeServer ( _pipeName , _maxInstances ) ;
132+
133+ return connectedStream ;
123134 }
124- finally
135+ catch ( Exception )
125136 {
126- if ( ! _cancellation . IsCancellationRequested )
137+ // Keep named pipe server stream when getting any kind of cancel request.
138+ // Cancel happens when complete transport is about to disposed or caller
139+ // cancels out specific accept call, no need to recycle named pipe server stream.
140+ // In all other exception scenarios named pipe server stream will be re-created.
141+ if ( ! linkedSource . IsCancellationRequested )
127142 {
128- CreateNewPipeServer ( ) ;
143+ _stream . Dispose ( ) ;
144+ _stream = CreateNewNamedPipeServer ( _pipeName , _maxInstances ) ;
129145 }
146+ throw ;
130147 }
131- return connectedStream ;
132148 }
133149
134- private void CreateNewPipeServer ( )
150+ private NamedPipeServerStream CreateNewNamedPipeServer ( string pipeName , int maxInstances )
135151 {
136- _stream = new NamedPipeServerStream (
137- _pipeName ,
138- PipeDirection . InOut ,
139- _maxInstances ,
140- PipeTransmissionMode . Byte ,
141- PipeOptions . Asynchronous ) ;
142- OnCreateNewServer ( ) ;
152+ var stream = new NamedPipeServerStream ( pipeName , PipeDirection . InOut , maxInstances , PipeTransmissionMode . Byte , PipeOptions . Asynchronous ) ;
153+ OnCreateNewServer ( null ) ;
154+ return stream ;
143155 }
144156 }
145157
146- internal sealed class UnixDomainSocketServerTransport : IpcServerTransport
158+ internal abstract class IpcSocketServerTransport : IpcServerTransport
147159 {
148160 private readonly CancellationTokenSource _cancellation = new CancellationTokenSource ( ) ;
149- private readonly int _backlog ;
150- private readonly string _path ;
161+ protected IpcSocket _socket ;
151162
152- private UnixDomainSocket _socket ;
153-
154- public UnixDomainSocketServerTransport ( string path , int backlog )
163+ protected IpcSocketServerTransport ( IIpcServerTransportCallbackInternal transportCallback = null )
164+ : base ( transportCallback )
155165 {
156- _backlog = backlog ;
157- _path = path ;
158-
159- CreateNewSocketServer ( ) ;
160166 }
161167
162168 protected override void Dispose ( bool disposing )
@@ -187,33 +193,96 @@ public override async Task<Stream> AcceptAsync(CancellationToken token)
187193 using var linkedSource = CancellationTokenSource . CreateLinkedTokenSource ( token , _cancellation . Token ) ;
188194 try
189195 {
190- Socket socket = await _socket . AcceptAsync ( linkedSource . Token ) . ConfigureAwait ( false ) ;
196+ // Accept next client socket.
197+ var socket = await _socket . AcceptAsync ( linkedSource . Token ) . ConfigureAwait ( false ) ;
198+
199+ // Configure client socket based on transport type.
200+ OnAccept ( socket ) ;
191201
192202 return new ExposedSocketNetworkStream ( socket , ownsSocket : true ) ;
193203 }
194204 catch ( Exception )
195205 {
196- // Recreate socket if transport is not disposed.
197- if ( ! _cancellation . IsCancellationRequested )
206+ // Keep server socket when getting any kind of cancel request.
207+ // Cancel happens when complete transport is about to disposed or caller
208+ // cancels out specific accept call, no need to recycle server socket.
209+ // In all other exception scenarios server socket will be re-created.
210+ if ( ! linkedSource . IsCancellationRequested )
198211 {
199- CreateNewSocketServer ( ) ;
212+ _socket = CreateNewSocketServer ( ) ;
200213 }
201214 throw ;
202215 }
203216 }
204217
205- private void CreateNewSocketServer ( )
218+ internal abstract bool OnAccept ( Socket socket ) ;
219+
220+ internal abstract IpcSocket CreateNewSocketServer ( ) ;
221+ }
222+
223+ internal sealed class IpcTcpSocketServerTransport : IpcSocketServerTransport
224+ {
225+ private readonly int _backlog ;
226+ private readonly IpcTcpSocketEndPoint _endPoint ;
227+
228+ public IpcTcpSocketServerTransport ( string address , int backlog , IIpcServerTransportCallbackInternal transportCallback = null )
229+ : base ( transportCallback )
230+ {
231+ _endPoint = new IpcTcpSocketEndPoint ( address ) ;
232+ _backlog = backlog != MaxAllowedConnections ? backlog : 100 ;
233+ _socket = CreateNewSocketServer ( ) ;
234+ }
235+
236+ internal override bool OnAccept ( Socket socket )
237+ {
238+ socket . NoDelay = true ;
239+ return true ;
240+ }
241+
242+ internal override IpcSocket CreateNewSocketServer ( )
243+ {
244+ var socket = new IpcSocket ( SocketType . Stream , ProtocolType . Tcp ) ;
245+ if ( _endPoint . DualMode )
246+ socket . DualMode = _endPoint . DualMode ;
247+ socket . Bind ( _endPoint ) ;
248+ socket . Listen ( _backlog ) ;
249+ socket . LingerState . Enabled = false ;
250+ OnCreateNewServer ( socket . LocalEndPoint ) ;
251+ return socket ;
252+ }
253+ }
254+
255+ internal sealed class IpcUnixDomainSocketServerTransport : IpcSocketServerTransport
256+ {
257+ private readonly int _backlog ;
258+ private readonly IpcUnixDomainSocketEndPoint _endPoint ;
259+
260+ public IpcUnixDomainSocketServerTransport ( string path , int backlog , IIpcServerTransportCallbackInternal transportCallback = null )
261+ : base ( transportCallback )
262+ {
263+ _backlog = backlog != MaxAllowedConnections ? backlog : ( int ) SocketOptionName . MaxConnections ;
264+ _endPoint = new IpcUnixDomainSocketEndPoint ( path ) ;
265+ _socket = CreateNewSocketServer ( ) ;
266+ }
267+
268+ internal override bool OnAccept ( Socket socket )
269+ {
270+ return true ;
271+ }
272+
273+ internal override IpcSocket CreateNewSocketServer ( )
206274 {
207- _socket = new UnixDomainSocket ( ) ;
208- _socket . Bind ( _path ) ;
209- _socket . Listen ( _backlog ) ;
210- _socket . LingerState . Enabled = false ;
211- OnCreateNewServer ( ) ;
275+ var socket = new IpcUnixDomainSocket ( ) ;
276+ socket . Bind ( _endPoint ) ;
277+ socket . Listen ( _backlog ) ;
278+ socket . LingerState . Enabled = false ;
279+ OnCreateNewServer ( null ) ;
280+ return socket ;
212281 }
213282 }
214283
215284 internal interface IIpcServerTransportCallbackInternal
216285 {
217- void CreatedNewServer ( ) ;
286+ void CreatedNewServer ( EndPoint localEP ) ;
218287 }
219288}
0 commit comments