@@ -124,12 +124,10 @@ private void ConfigureConnectedSocket(Socket socket)
124
124
125
125
private void Connect ( Socket socket , EndPoint endPoint , CancellationToken cancellationToken )
126
126
{
127
- var connected = false ;
128
- var cancelled = false ;
129
- var timedOut = false ;
127
+ var state = 1 ; // 1 == connecting, 2 == connected, 3 == timedout, 4 == cancelled
130
128
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 ) ) )
133
131
{
134
132
try
135
133
{
@@ -143,43 +141,48 @@ private void Connect(Socket socket, EndPoint endPoint, CancellationToken cancell
143
141
{
144
142
socket . Connect ( endPoint ) ;
145
143
}
146
- connected = true ;
147
- return ;
144
+ ChangeState ( 2 ) ; // note: might not actually go to state 2 if already in state 3 or 4
148
145
}
149
- catch
146
+ catch when ( state == 1 )
150
147
{
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
156
154
}
157
- }
158
155
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
+ }
160
163
161
- cancellationToken . ThrowIfCancellationRequested ( ) ;
162
- if ( timedOut )
164
+ void ChangeState ( int to )
163
165
{
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
+ }
166
171
}
167
172
}
168
173
169
174
private async Task ConnectAsync ( Socket socket , EndPoint endPoint , CancellationToken cancellationToken )
170
175
{
171
- var connected = false ;
172
- var cancelled = false ;
173
- var timedOut = false ;
176
+ var state = 1 ; // 1 == connecting, 2 == connected, 3 == timedout, 4 == cancelled
174
177
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 ) ) )
177
180
{
178
181
try
179
182
{
180
183
var dnsEndPoint = endPoint as DnsEndPoint ;
181
184
#if NETSTANDARD1_5 || NETSTANDARD1_6
182
- await socket . ConnectAsync ( endPoint ) . ConfigureAwait ( false ) ; // TODO: honor cancellationToken
185
+ await socket . ConnectAsync ( endPoint ) . ConfigureAwait ( false ) ;
183
186
#else
184
187
if ( dnsEndPoint != null )
185
188
{
@@ -191,26 +194,33 @@ private async Task ConnectAsync(Socket socket, EndPoint endPoint, CancellationTo
191
194
await Task . Factory . FromAsync ( socket . BeginConnect ( endPoint , null , null ) , socket . EndConnect ) . ConfigureAwait ( false ) ;
192
195
}
193
196
#endif
194
- connected = true ;
195
- return ;
197
+ ChangeState ( 2 ) ; // note: might not actually go to state 2 if already in state 3 or 4
196
198
}
197
- catch
199
+ catch when ( state == 1 )
198
200
{
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
204
207
}
205
- }
206
208
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
+ }
208
216
209
- cancellationToken . ThrowIfCancellationRequested ( ) ;
210
- if ( timedOut )
217
+ void ChangeState ( int to )
211
218
{
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
+ }
214
224
}
215
225
}
216
226
0 commit comments