private int HandleAccept(TSocket tacceptSocket) { var type = tacceptSocket.Type; int clientFd = -1; PosixResult result; if (type == SocketFlags.TypeAccept) { // TODO: should we handle more than 1 accept? If we do, we shouldn't be to eager // as that might give the kernel the impression we have nothing to do // which could interfere with the SO_REUSEPORT load-balancing. result = tacceptSocket.TryAccept(out clientFd, blocking: false); } else { result = tacceptSocket.TryReceiveSocket(out clientFd, blocking: false); if (result.Value == 0) { // The socket passing us file descriptors has closed. // We dispose our end so we get get removed from the epoll. tacceptSocket.Close(); return(0); } } if (result.IsSuccess) { TSocket tsocket; try { SocketFlags flags = SocketFlags.TypeClient | (tacceptSocket.IsDeferSend ? SocketFlags.DeferSend : SocketFlags.None); tsocket = new TSocket(this, clientFd, flags) { ZeroCopyThreshold = tacceptSocket.ZeroCopyThreshold }; bool ipSocket = !object.ReferenceEquals(tacceptSocket.LocalAddress, NotIPSocket); // Store the last LocalAddress on the tacceptSocket so we might reuse it instead // of allocating a new one for the same address. IPEndPointStruct localAddress = default(IPEndPointStruct); IPEndPointStruct remoteAddress = default(IPEndPointStruct); if (ipSocket && tsocket.TryGetLocalIPAddress(out localAddress, tacceptSocket.LocalAddress)) { tsocket.LocalAddress = localAddress.Address; tsocket.LocalPort = localAddress.Port; if (tsocket.TryGetPeerIPAddress(out remoteAddress)) { tsocket.RemoteAddress = remoteAddress.Address; tsocket.RemotePort = remoteAddress.Port; } } else { // This is not an IP socket. tacceptSocket.LocalAddress = NotIPSocket; ipSocket = false; } if (ipSocket) { tsocket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, 1); } } catch { IOInterop.Close(clientFd); return(0); } tsocket.MiddlewareTask = _connectionDispatcher.OnConnection(tsocket); lock (_sockets) { _sockets.Add(clientFd, tsocket); } bool dataMayBeAvailable = tacceptSocket.IsDeferAccept; tsocket.Start(dataMayBeAvailable); return(1); } else { return(0); } }
private TSocket CreateAcceptSocket(IPEndPoint endPoint, SocketFlags flags) { int acceptSocketFd = -1; int port = endPoint.Port; try { bool ipv4 = endPoint.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork; SocketInterop.Socket(ipv4 ? AF_INET : AF_INET6, SOCK_STREAM, IPPROTO_TCP, blocking: false, out acceptSocketFd).ThrowOnError(); TSocket acceptSocket = new TSocket(this, acceptSocketFd, flags); if (!ipv4) { // Kestrel does mapped ipv4 by default. acceptSocket.SetSocketOption(SOL_IPV6, IPV6_V6ONLY, 0); } if (_transportOptions.ReceiveOnIncomingCpu) { if (_transportThread.CpuId != -1) { if (!acceptSocket.TrySetSocketOption(SOL_SOCKET, SO_INCOMING_CPU, _transportThread.CpuId)) { _logger.LogWarning($"Cannot enable SO_INCOMING_CPU for {endPoint}"); } } } // Linux: allow bind during linger time acceptSocket.SetSocketOption(SOL_SOCKET, SO_REUSEADDR, 1); // Linux: allow concurrent binds and let the kernel do load-balancing acceptSocket.SetSocketOption(SOL_SOCKET, SO_REUSEPORT, 1); if ((flags & SocketFlags.DeferAccept) != 0) { // Linux: wait up to 1 sec for data to arrive before accepting socket acceptSocket.SetSocketOption(SOL_TCP, TCP_DEFER_ACCEPT, 1); } acceptSocket.ZeroCopyThreshold = LinuxTransportOptions.NoZeroCopy; if (_transportOptions.ZeroCopy && _transportOptions.ZeroCopyThreshold != LinuxTransportOptions.NoZeroCopy) { if (acceptSocket.TrySetSocketOption(SOL_SOCKET, SO_ZEROCOPY, 1)) { acceptSocket.ZeroCopyThreshold = _transportOptions.ZeroCopyThreshold; } } acceptSocket.Bind(endPoint); if (port == 0) { // When testing we want the OS to select a free port port = acceptSocket.GetLocalIPAddress().Port; } acceptSocket.Listen(ListenBacklog); endPoint.Port = port; return(acceptSocket); } catch { if (acceptSocketFd != -1) { IOInterop.Close(acceptSocketFd); } throw; } }
private TSocket CreateAcceptSocket(IPEndPoint endPoint, SocketFlags flags) { int acceptSocketFd = -1; int port = endPoint.Port; try { bool ipv4 = endPoint.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork; SocketInterop.Socket(ipv4 ? AddressFamily.InterNetwork : AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp, blocking: false, out acceptSocketFd).ThrowOnError(); TSocket acceptSocket = new TSocket(this, acceptSocketFd, flags); if (!ipv4) { // Kestrel does mapped ipv4 by default. acceptSocket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0); } if (_transportOptions.ReceiveOnIncomingCpu) { if (_transportThread.CpuId != -1) { if (!acceptSocket.TrySetSocketOption(SocketOptionLevel.Socket, SocketOptionName.IncomingCpu, _transportThread.CpuId)) { _logger.LogWarning($"Cannot enable nameof{SocketOptionName.IncomingCpu} for {endPoint}"); } } } // Linux: allow bind during linger time acceptSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1); // Linux: allow concurrent binds and let the kernel do load-balancing acceptSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReusePort, 1); if ((flags & SocketFlags.DeferAccept) != 0) { // Linux: wait up to 1 sec for data to arrive before accepting socket acceptSocket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.DeferAccept, 1); } acceptSocket.ZeroCopyThreshold = LinuxTransportOptions.NoZeroCopy; if (_transportOptions.ZeroCopy && _transportOptions.ZeroCopyThreshold != LinuxTransportOptions.NoZeroCopy) { if (acceptSocket.TrySetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ZeroCopy, 1)) { acceptSocket.ZeroCopyThreshold = _transportOptions.ZeroCopyThreshold; } } acceptSocket.Bind(endPoint); if (port == 0) { // When testing we want the OS to select a free port port = acceptSocket.GetLocalIPAddress().Port; } acceptSocket.Listen(ListenBacklog); endPoint.Port = port; return(acceptSocket); } catch { if (acceptSocketFd != -1) { IOInterop.Close(acceptSocketFd); } throw; } }
private int HandleAccept(TSocket tacceptSocket) { var type = tacceptSocket.Type; int clientFd = -1; PosixResult result; if (type == SocketFlags.TypeAccept) { // TODO: should we handle more than 1 accept? If we do, we shouldn't be to eager // as that might give the kernel the impression we have nothing to do // which could interfere with the SO_REUSEPORT load-balancing. result = tacceptSocket.TryAccept(out clientFd, blocking: false); } else { result = tacceptSocket.TryReceiveSocket(out clientFd, blocking: false); if (result.Value == 0) { // The socket passing us file descriptors has closed. // We dispose our end so we get get removed from the epoll. tacceptSocket.Close(); return(0); } } if (result.IsSuccess) { TSocket tsocket; try { SocketFlags flags = SocketFlags.TypeClient | (tacceptSocket.IsDeferSend ? SocketFlags.DeferSend : SocketFlags.None); tsocket = new TSocket(this, clientFd, flags, _transportOptions) { ZeroCopyThreshold = tacceptSocket.ZeroCopyThreshold }; var localIpEndPoint = tacceptSocket.LocalEndPoint as IPEndPoint; bool ipSocket = !object.ReferenceEquals(localIpEndPoint?.Address, NotIPSocket); // Store the last LocalAddress on the tacceptSocket so we might reuse it instead // of allocating a new one for the same address. IPEndPointStruct localAddress = default(IPEndPointStruct); IPEndPointStruct remoteAddress = default(IPEndPointStruct); if (ipSocket && tsocket.TryGetLocalIPAddress(out localAddress, localIpEndPoint?.Address)) { tsocket.LocalEndPoint = new IPEndPoint(localAddress.Address, localAddress.Port); if (tsocket.TryGetPeerIPAddress(out remoteAddress)) { tsocket.RemoteEndPoint = new IPEndPoint(remoteAddress.Address, remoteAddress.Port); } } else { // This is not an IP socket. // REVIEW: Should LocalEndPoint be null instead? Some other EndPoint type? tacceptSocket.LocalEndPoint = new IPEndPoint(NotIPSocket, 0); ipSocket = false; } if (ipSocket) { tsocket.SetSocketOption(SOL_TCP, TCP_NODELAY, 1); } } catch { IOInterop.Close(clientFd); return(0); } bool accepted = _acceptQueue.Writer.TryWrite(tsocket); Debug.Assert(accepted, "The connection was not written to the channel!"); lock (_sockets) { _sockets.Add(clientFd, tsocket); } bool dataMayBeAvailable = tacceptSocket.IsDeferAccept; tsocket.Start(dataMayBeAvailable); return(1); } else { return(0); } }