/// <inheritdoc /> public async Task <Stream> ConnectAsync(MultiAddress address, CancellationToken cancel = default) { var port = address.Protocols .Where(p => p.Name == "tcp") .Select(p => int.Parse(p.Value)) .First(); var ip = address.Protocols .FirstOrDefault(p => p.Name == "ip4" || p.Name == "ip6"); if (ip == null) { throw new ArgumentException($"Missing IP address in '{address}'.", nameof(address)); } var socket = new Socket( ip.Name == "ip4" ? AddressFamily.InterNetwork : AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp); var latency = MinReadTimeout; // keep compiler happy var start = DateTime.Now; try { Log.Trace("connecting to " + address); // Handle cancellation of the connect attempt by disposing // of the socket. This will force ConnectAsync to return. await using (var _ = cancel.Register(() => { if (socket != null) { socket?.Dispose(); } socket = null; })) { var ipaddr = IPAddress.Parse(ip.Value); await socket.ConnectAsync(ipaddr, port).ConfigureAwait(false); } latency = DateTime.Now - start; Log.Trace($"connected to {address} in {latency.TotalMilliseconds} ms"); } catch (Exception) when(cancel.IsCancellationRequested) { // eat it, the caller has cancelled and doesn't care. } catch (Exception) { latency = DateTime.Now - start; Log.Trace($"failed to {address} in {latency.TotalMilliseconds} ms"); socket?.Dispose(); throw; } if (cancel.IsCancellationRequested) { Log.Trace("cancel " + address); socket?.Dispose(); cancel.ThrowIfCancellationRequested(); } var timeout = (int)Math.Max(MinReadTimeout.TotalMilliseconds, latency.TotalMilliseconds * 3); socket.LingerState = new LingerOption(false, 0); socket.ReceiveTimeout = timeout; socket.SendTimeout = timeout; Stream stream = new NetworkStream(socket, true); stream.ReadTimeout = timeout; stream.WriteTimeout = timeout; stream = new DuplexBufferedStream(stream); if (!cancel.IsCancellationRequested) { return(stream); } Log.Trace("cancel " + address); await stream.DisposeAsync(); cancel.ThrowIfCancellationRequested(); return(stream); }
private static void ProcessConnection(Socket socket, MultiAddress address, Action <Stream, MultiAddress, MultiAddress> handler, CancellationToken cancel) { Log.Debug("listening on " + address); // Handle cancellation of the listener cancel.Register(() => { Log.Debug("Got cancel on " + address); try { if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { socket.Shutdown(SocketShutdown.Both); socket.Dispose(); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { socket.Shutdown(SocketShutdown.Receive); } else // must be windows { socket.Dispose(); } } catch (Exception e) { Log.Warn($"Cancelling listener: {e.Message}"); } finally { socket = null; } }); try { while (!cancel.IsCancellationRequested) { var conn = socket.Accept(); MultiAddress remote = null; if (conn.RemoteEndPoint is IPEndPoint endPoint) { var s = new StringBuilder(); s.Append(endPoint.AddressFamily == AddressFamily.InterNetwork ? "/ip4/" : "/ip6/"); s.Append(endPoint.Address); s.Append("/tcp/"); s.Append(endPoint.Port); remote = new MultiAddress(s.ToString()); Log.Debug("connection from " + remote); } conn.NoDelay = true; Stream peer = new NetworkStream(conn, true); peer = new DuplexBufferedStream(peer); try { handler(peer, address, remote); } catch (Exception e) { Log.Error("listener handler failed " + address, e); peer.Dispose(); } } } catch (Exception) when(cancel.IsCancellationRequested) { // ignore } catch (Exception e) { Console.WriteLine(e.Message); Log.Error("listener failed " + address, e); } finally { socket?.Dispose(); } Log.Debug("stop listening on " + address); }
public void Setup() { _random = new Random(); _inner = new MemoryStream(); _stream = new DuplexBufferedStream(_inner, 16); }
void ProcessConnection(Socket socket, MultiAddress address, Action <Stream, MultiAddress, MultiAddress> handler, CancellationToken cancel) { log.Debug("listening on " + address); // Handle cancellation of the listener cancel.Register(() => { log.Debug("Got cancel on " + address); try { // .Net Standard on Unix neeeds this to cancel the Accept #if !NET461 if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { socket.Shutdown(SocketShutdown.Both); socket.Dispose(); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { socket.Shutdown(SocketShutdown.Receive); } else // must be windows { socket.Dispose(); } #else socket.Dispose(); #endif } catch (Exception e) { log.Warn($"Cancelling listener: {e.Message}"); } finally { socket = null; } }); try { while (!cancel.IsCancellationRequested) { Socket conn = socket.Accept(); if (conn == null) { log.Warn("Null socket from Accept"); continue; } MultiAddress remote = null; var endPoint = conn.RemoteEndPoint as IPEndPoint; if (endPoint != null) { var s = new StringBuilder(); s.Append(endPoint.AddressFamily == AddressFamily.InterNetwork ? "/ip4/" : "/ip6/"); s.Append(endPoint.Address.ToString()); s.Append("/tcp/"); s.Append(endPoint.Port); remote = new MultiAddress(s.ToString()); log.Debug("connection from " + remote); } conn.NoDelay = true; Stream peer = new NetworkStream(conn, ownsSocket: true); #if !NETSTANDARD14 // BufferedStream not available in .Net Standard 1.4 peer = new DuplexBufferedStream(peer); #endif try { handler(peer, address, remote); } catch (Exception e) { log.Error("listener handler failed " + address, e); peer.Dispose(); } } } catch (Exception) when(cancel.IsCancellationRequested) { // eat it } catch (Exception e) { Console.WriteLine(e.Message); log.Error("listener failed " + address, e); // eat it and give up } finally { if (socket != null) { socket.Dispose(); } } log.Debug("stop listening on " + address); }
/// <inheritdoc /> public async Task <Stream> ConnectAsync(MultiAddress address, CancellationToken cancel = default(CancellationToken)) { var port = address.Protocols .Where(p => p.Name == "tcp") .Select(p => Int32.Parse(p.Value)) .First(); var ip = address.Protocols .Where(p => p.Name == "ip4" || p.Name == "ip6") .FirstOrDefault(); if (ip == null) { throw new ArgumentException($"Missing IP address in '{address}'.", "address"); } var socket = new Socket( ip.Name == "ip4" ? AddressFamily.InterNetwork : AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp); TimeSpan latency = MinReadTimeout; // keep compiler happy try { log.Trace("connecting to " + address); var start = DateTime.Now; // Handle cancellation of the connect attempt by disposing // of the socket. This will force ConnectAsync to return. using (var _ = cancel.Register(() => { socket?.Dispose(); socket = null; })) { await socket.ConnectAsync(ip.Value, port); }; latency = DateTime.Now - start; log.Trace($"connected to {address} in {latency.TotalMilliseconds} ms"); } catch (Exception) when(cancel.IsCancellationRequested) { // eat it, the caller has cancelled and doesn't care. } catch (Exception) { socket?.Dispose(); throw; } if (cancel.IsCancellationRequested) { log.Trace("cancel " + address); socket?.Dispose(); cancel.ThrowIfCancellationRequested(); } var timeout = (int)Math.Max(MinReadTimeout.TotalMilliseconds, latency.TotalMilliseconds * 3); socket.LingerState = new LingerOption(false, 0); socket.ReceiveTimeout = timeout; socket.SendTimeout = timeout; Stream stream = new NetworkStream(socket, ownsSocket: true); stream.ReadTimeout = timeout; stream.WriteTimeout = timeout; #if !NETSTANDARD14 // BufferedStream not available in .Net Standard 1.4 stream = new DuplexBufferedStream(stream); #endif if (cancel.IsCancellationRequested) { log.Trace("cancel " + address); stream.Dispose(); cancel.ThrowIfCancellationRequested(); } return(stream); }