Improve behavior of TcpClient Dispose concurrent with ConnectAsync
TcpClient.Dispose is not meant to be used concurrently with other operations on the instance, but some code does do so as a way to provide a cancellation mechanism. There are two easily hit issues with this:
1. On Unix, the ConnectAsync operation doesn't publish the actual Socket on which a connection was made until after the connection is established, as it needs to use temporary sockets to try each potential target address, and publishing it before connecting could end up publishing a Socket that won't end up being the actual one used. As such, if a Dispose occurs during the ConnectAsync operation, it won't end up disposing the socket being used to make the connection, such that the connection won't be canceled.
2. On all platforms, Dispose nulls out the client socket field. When the connection then subsequently completes, it hits a NullReferenceException while trying to dereference that field.
This commit addresses both issues:
a. When the client is disposed, on Unix we cancel a CancellationTokenSource, and each Socket we create is registered with that source to dispose the socket. That way, we dispose of each socket even if it hasn't been published onto the instance yet.
b. We grab the Socket from the field and check for null prior to dereferencing it.
Commit migrated from https://github.com/dotnet/corefx/commit/
e3e7ca7630c486f35bf7eac330dedcf729c74e71