static void ConfigureSocketKeepAlive( Socket socket, EpoxyTransport.TimeoutConfig timeoutConfig, Logger logger) { if (timeoutConfig.KeepAliveTime != TimeSpan.Zero && timeoutConfig.KeepAliveInterval != TimeSpan.Zero) { // Socket.IOControl for IOControlCode.KeepAliveValues is expecting a structure like // the following on Windows: // // struct tcp_keepalive // { // u_long onoff; // 0 for off, non-zero for on // u_long keepalivetime; // milliseconds // u_long keepaliveinterval; // milliseconds // }; // // On some platforms this gets mapped to the relevant OS structures, but on other // platforms, this may fail with a PlatformNotSupportedException. UInt32 keepAliveTimeMillis = checked ((UInt32)timeoutConfig.KeepAliveTime.TotalMilliseconds); UInt32 keepAliveIntervalMillis = checked ((UInt32)timeoutConfig.KeepAliveInterval.TotalMilliseconds); var keepAliveVals = new byte[sizeof(UInt32) * 3]; keepAliveVals[0] = 1; keepAliveVals[4] = (byte)(keepAliveTimeMillis & 0xff); keepAliveVals[5] = (byte)((keepAliveTimeMillis >> 8) & 0xff); keepAliveVals[6] = (byte)((keepAliveTimeMillis >> 16) & 0xff); keepAliveVals[7] = (byte)((keepAliveTimeMillis >> 24) & 0xff); keepAliveVals[8] = (byte)(keepAliveIntervalMillis & 0xff); keepAliveVals[9] = (byte)((keepAliveIntervalMillis >> 8) & 0xff); keepAliveVals[10] = (byte)((keepAliveIntervalMillis >> 16) & 0xff); keepAliveVals[11] = (byte)((keepAliveIntervalMillis >> 24) & 0xff); try { socket.IOControl(IOControlCode.KeepAliveValues, keepAliveVals, null); } catch (ObjectDisposedException) { // Oh well: the connection went down before we could configure it. Nothing to be // done, except to wait for the next socket operation to fail and let normal // clean up take over. } catch (Exception ex) when(ex is SocketException || ex is PlatformNotSupportedException) { logger.Site().Warning(ex, "Socket keep-alive could not be configured"); } } }
/// <summary> /// Creates an EpoxyNetworkStream from a TCP socket, handling failures that may occur during /// TCP/TLS connection establishment. /// </summary> /// <remarks> /// This is a helper function that centralizes error handling during connection creation. /// </remarks> /// <param name="socketFunc">A function to invoke to acquire a socket.</param> /// <param name="streamFunc"> /// A function to invoke to wrap a socket in a network stream. /// </param> /// <param name="timeoutConfig">The timeout config to use for this stream.</param> /// <param name="logger">The logger.</param> /// <returns>A connected EpoxyNetworkStream.</returns> public static async Task <EpoxyNetworkStream> MakeAsync( Func <Task <Socket> > socketFunc, Func <Socket, Task <EpoxyNetworkStream> > streamFunc, EpoxyTransport.TimeoutConfig timeoutConfig, Logger logger) { Socket socket = null; try { socket = await socketFunc(); ConfigureSocketKeepAlive(socket, timeoutConfig, logger); return(await streamFunc(socket)); } catch (Exception) { SafeShutdownSocket(socket, logger); throw; } }
public EpoxyListener( EpoxyTransport parentTransport, IPEndPoint listenEndpoint, EpoxyServerTlsConfig tlsConfig, EpoxyTransport.TimeoutConfig timeoutConfig, Logger logger, Metrics metrics) : base(logger, metrics) { Debug.Assert(parentTransport != null); Debug.Assert(listenEndpoint != null); this.parentTransport = parentTransport; // will be null if not using TLS this.tlsConfig = tlsConfig; this.timeoutConfig = timeoutConfig; listener = new TcpListener(listenEndpoint); serviceHost = new ServiceHost(logger); connections = new HashSet <EpoxyConnection>(); shutdownTokenSource = new CancellationTokenSource(); ListenEndpoint = listenEndpoint; }