@@ -124,12 +124,10 @@ private void ConfigureConnectedSocket(Socket socket)
124124
125125 private void Connect ( Socket socket , EndPoint endPoint , CancellationToken cancellationToken )
126126 {
127- var connected = false ;
128- var cancelled = false ;
129- var timedOut = false ;
127+ var state = 1 ; // 1 == connecting, 2 == connected, 3 == timedout, 4 == cancelled
130128
131- using ( var registration = cancellationToken . Register ( ( ) => { if ( ! connected ) { cancelled = true ; try { socket . Dispose ( ) ; } catch { } } } ) )
132- using ( var timer = new Timer ( _ => { if ( ! connected ) { timedOut = true ; try { socket . Dispose ( ) ; } catch { } } } , null , _settings . ConnectTimeout , Timeout . InfiniteTimeSpan ) )
129+ using ( new Timer ( _ => ChangeState ( 3 ) , null , _settings . ConnectTimeout , Timeout . InfiniteTimeSpan ) )
130+ using ( cancellationToken . Register ( ( ) => ChangeState ( 4 ) ) )
133131 {
134132 try
135133 {
@@ -143,43 +141,48 @@ private void Connect(Socket socket, EndPoint endPoint, CancellationToken cancell
143141 {
144142 socket . Connect ( endPoint ) ;
145143 }
146- connected = true ;
147- return ;
144+ ChangeState ( 2 ) ; // note: might not actually go to state 2 if already in state 3 or 4
148145 }
149- catch
146+ catch when ( state == 1 )
150147 {
151- if ( ! cancelled && ! timedOut )
152- {
153- try { socket . Dispose ( ) ; } catch { }
154- throw ;
155- }
148+ try { socket . Dispose ( ) ; } catch { }
149+ throw ;
150+ }
151+ catch when ( state >= 3 )
152+ {
153+ // a timeout or operation cancelled exception will be thrown instead
156154 }
157- }
158155
159- try { socket . Dispose ( ) ; } catch { }
156+ if ( state == 3 )
157+ {
158+ var message = string . Format ( "Timed out connecting to {0}. Timeout was {1}." , endPoint , _settings . ConnectTimeout ) ;
159+ throw new TimeoutException ( message ) ;
160+ }
161+ if ( state == 4 ) { throw new OperationCanceledException ( ) ; }
162+ }
160163
161- cancellationToken . ThrowIfCancellationRequested ( ) ;
162- if ( timedOut )
164+ void ChangeState ( int to )
163165 {
164- var message = string . Format ( "Timed out connecting to {0}. Timeout was {1}." , endPoint , _settings . ConnectTimeout ) ;
165- throw new TimeoutException ( message ) ;
166+ var from = Interlocked . CompareExchange ( ref state , to , 1 ) ;
167+ if ( from == 1 && to >= 3 )
168+ {
169+ try { socket . Dispose ( ) ; } catch { } // disposing the socket aborts the connection attempt
170+ }
166171 }
167172 }
168173
169174 private async Task ConnectAsync ( Socket socket , EndPoint endPoint , CancellationToken cancellationToken )
170175 {
171- var connected = false ;
172- var cancelled = false ;
173- var timedOut = false ;
176+ var state = 1 ; // 1 == connecting, 2 == connected, 3 == timedout, 4 == cancelled
174177
175- using ( var registration = cancellationToken . Register ( ( ) => { if ( ! connected ) { cancelled = true ; try { socket . Dispose ( ) ; } catch { } } } ) )
176- using ( var timer = new Timer ( _ => { if ( ! connected ) { timedOut = true ; try { socket . Dispose ( ) ; } catch { } } } , null , _settings . ConnectTimeout , Timeout . InfiniteTimeSpan ) )
178+ using ( new Timer ( _ => ChangeState ( 3 ) , null , _settings . ConnectTimeout , Timeout . InfiniteTimeSpan ) )
179+ using ( cancellationToken . Register ( ( ) => ChangeState ( 4 ) ) )
177180 {
178181 try
179182 {
180183 var dnsEndPoint = endPoint as DnsEndPoint ;
181184#if NETSTANDARD1_5 || NETSTANDARD1_6
182- await socket . ConnectAsync ( endPoint ) . ConfigureAwait ( false ) ; // TODO: honor cancellationToken
185+ await socket . ConnectAsync ( endPoint ) . ConfigureAwait ( false ) ;
183186#else
184187 if ( dnsEndPoint != null )
185188 {
@@ -191,26 +194,33 @@ private async Task ConnectAsync(Socket socket, EndPoint endPoint, CancellationTo
191194 await Task . Factory . FromAsync ( socket . BeginConnect ( endPoint , null , null ) , socket . EndConnect ) . ConfigureAwait ( false ) ;
192195 }
193196#endif
194- connected = true ;
195- return ;
197+ ChangeState ( 2 ) ; // note: might not actually go to state 2 if already in state 3 or 4
196198 }
197- catch
199+ catch when ( state == 1 )
198200 {
199- if ( ! cancelled && ! timedOut )
200- {
201- try { socket . Dispose ( ) ; } catch { }
202- throw ;
203- }
201+ try { socket . Dispose ( ) ; } catch { }
202+ throw ;
203+ }
204+ catch when ( state >= 3 )
205+ {
206+ // a timeout or operation cancelled exception will be thrown instead
204207 }
205- }
206208
207- try { socket . Dispose ( ) ; } catch { }
209+ if ( state == 3 )
210+ {
211+ var message = string . Format ( "Timed out connecting to {0}. Timeout was {1}." , endPoint , _settings . ConnectTimeout ) ;
212+ throw new TimeoutException ( message ) ;
213+ }
214+ if ( state == 4 ) { throw new OperationCanceledException ( ) ; }
215+ }
208216
209- cancellationToken . ThrowIfCancellationRequested ( ) ;
210- if ( timedOut )
217+ void ChangeState ( int to )
211218 {
212- var message = string . Format ( "Timed out connecting to {0}. Timeout was {1}." , endPoint , _settings . ConnectTimeout ) ;
213- throw new TimeoutException ( message ) ;
219+ var from = Interlocked . CompareExchange ( ref state , to , 1 ) ;
220+ if ( from == 1 && to >= 3 )
221+ {
222+ try { socket . Dispose ( ) ; } catch { } // disposing the socket aborts the connection attempt
223+ }
214224 }
215225 }
216226
0 commit comments