| File: System\Net\Sockets\TCPListener.cs | Web Access |
| Project: src\runtime\src\libraries\System.Net.Sockets\src\System.Net.Sockets.csproj (System.Net.Sockets) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; using System.Runtime.Versioning; using System.Threading; using System.Threading.Tasks; namespace System.Net.Sockets { // The System.Net.Sockets.TcpListener class provide TCP services at a higher level of abstraction // than the System.Net.Sockets.Socket class. System.Net.Sockets.TcpListener is used to create a // host process that listens for connections from TCP clients. public class TcpListener : IDisposable { private readonly IPEndPoint _serverSocketEP; private Socket? _serverSocket; private bool _active; private bool _exclusiveAddressUse; private bool? _allowNatTraversal; // Initializes a new instance of the TcpListener class with the specified local end point. public TcpListener(IPEndPoint localEP) { if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, localEP); ArgumentNullException.ThrowIfNull(localEP); _serverSocketEP = localEP; _serverSocket = new Socket(_serverSocketEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp); } // Initializes a new instance of the TcpListener class that listens to the specified IP address // and port. public TcpListener(IPAddress localaddr, int port) { if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, localaddr); ArgumentNullException.ThrowIfNull(localaddr); if (!TcpValidationHelpers.ValidatePortNumber(port)) { throw new ArgumentOutOfRangeException(nameof(port)); } _serverSocketEP = new IPEndPoint(localaddr, port); _serverSocket = new Socket(_serverSocketEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp); } // Initiailizes a new instance of the TcpListener class that listens on the specified port. [Obsolete("This constructor has been deprecated. Use TcpListener(IPAddress localaddr, int port) instead.")] public TcpListener(int port) { if (!TcpValidationHelpers.ValidatePortNumber(port)) { throw new ArgumentOutOfRangeException(nameof(port)); } _serverSocketEP = new IPEndPoint(IPAddress.Any, port); _serverSocket = new Socket(_serverSocketEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp); } // Used by the class to provide the underlying network socket. public Socket Server { get { CreateNewSocketIfNeeded(); return _serverSocket!; } } // Used by the class to indicate that the listener's socket has been bound to a port // and started listening. protected bool Active { get { return _active; } } // Gets the m_Active EndPoint for the local listener socket. public EndPoint LocalEndpoint { get { return _active ? _serverSocket!.LocalEndPoint! : _serverSocketEP; } } public bool ExclusiveAddressUse { get { return _serverSocket != null ? _serverSocket.ExclusiveAddressUse : _exclusiveAddressUse; } set { if (_active) { throw new InvalidOperationException(SR.net_tcplistener_mustbestopped); } _serverSocket?.ExclusiveAddressUse = value; _exclusiveAddressUse = value; } } [SupportedOSPlatform("windows")] public void AllowNatTraversal(bool allowed) { if (_active) { throw new InvalidOperationException(SR.net_tcplistener_mustbestopped); } if (_serverSocket != null) { SetIPProtectionLevel(allowed); // Set it only for the current socket to preserve existing behavior } else { _allowNatTraversal = allowed; } } // Starts listening to network requests. public void Start() { Start((int)SocketOptionName.MaxConnections); } public void Start(int backlog) { ArgumentOutOfRangeException.ThrowIfNegative(backlog); // Already listening. if (_active) { return; } CreateNewSocketIfNeeded(); _serverSocket!.Bind(_serverSocketEP); try { _serverSocket.Listen(backlog); } catch (SocketException) { // When there is an exception, unwind previous actions (bind, etc). Stop(); throw; } _active = true; } // Closes the network connection. public void Stop() { _serverSocket?.Dispose(); _active = false; _serverSocket = null; } /// <summary> /// Releases all resources used by the current <see cref="TcpListener"/> instance. /// </summary> public void Dispose() => Stop(); // Determine if there are pending connection requests. public bool Pending() { if (!_active) { throw new InvalidOperationException(SR.net_stopped); } return _serverSocket!.Poll(0, SelectMode.SelectRead); } // Accept the first pending connection public Socket AcceptSocket() { if (!_active) { throw new InvalidOperationException(SR.net_stopped); } if (OperatingSystem.IsWasi() && _serverSocket!.Blocking) throw new PlatformNotSupportedException("Only use with Socket.Blocking=false on WASI"); return _serverSocket!.Accept(); } public TcpClient AcceptTcpClient() { if (!_active) { throw new InvalidOperationException(SR.net_stopped); } if (OperatingSystem.IsWasi() && _serverSocket!.Blocking) throw new PlatformNotSupportedException("Only use with Socket.Blocking=false on WASI"); Socket acceptedSocket = _serverSocket!.Accept(); return new TcpClient(acceptedSocket); } public IAsyncResult BeginAcceptSocket(AsyncCallback? callback, object? state) => TaskToAsyncResult.Begin(AcceptSocketAsync(), callback, state); public Socket EndAcceptSocket(IAsyncResult asyncResult) => EndAcceptCore<Socket>(asyncResult); public IAsyncResult BeginAcceptTcpClient(AsyncCallback? callback, object? state) => TaskToAsyncResult.Begin(AcceptTcpClientAsync(), callback, state); public TcpClient EndAcceptTcpClient(IAsyncResult asyncResult) => EndAcceptCore<TcpClient>(asyncResult); public Task<Socket> AcceptSocketAsync() => AcceptSocketAsync(CancellationToken.None).AsTask(); public ValueTask<Socket> AcceptSocketAsync(CancellationToken cancellationToken) { if (!_active) { throw new InvalidOperationException(SR.net_stopped); } return _serverSocket!.AcceptAsync(cancellationToken); } public Task<TcpClient> AcceptTcpClientAsync() => AcceptTcpClientAsync(CancellationToken.None).AsTask(); public ValueTask<TcpClient> AcceptTcpClientAsync(CancellationToken cancellationToken) { return WaitAndWrap(AcceptSocketAsync(cancellationToken)); static async ValueTask<TcpClient> WaitAndWrap(ValueTask<Socket> task) => new TcpClient(await task.ConfigureAwait(false)); } // This creates a TcpListener that listens on both IPv4 and IPv6 on the given port. public static TcpListener Create(int port) { if (!TcpValidationHelpers.ValidatePortNumber(port)) { throw new ArgumentOutOfRangeException(nameof(port)); } TcpListener listener; if (Socket.OSSupportsIPv6) { // If OS supports IPv6 use dual mode so both address families work. listener = new TcpListener(IPAddress.IPv6Any, port); if (!OperatingSystem.IsWasi()) listener.Server.DualMode = true; } else { // If not, fall-back to old IPv4. listener = new TcpListener(IPAddress.Any, port); } return listener; } [SupportedOSPlatform("windows")] private void SetIPProtectionLevel(bool allowed) => _serverSocket!.SetIPProtectionLevel(allowed ? IPProtectionLevel.Unrestricted : IPProtectionLevel.EdgeRestricted); private void CreateNewSocketIfNeeded() { _serverSocket ??= new Socket(_serverSocketEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp); if (_exclusiveAddressUse) { _serverSocket.ExclusiveAddressUse = true; } if (_allowNatTraversal != null) { Debug.Assert(OperatingSystem.IsWindows()); SetIPProtectionLevel(_allowNatTraversal.GetValueOrDefault()); _allowNatTraversal = null; // Reset value to avoid affecting more sockets } } private TResult EndAcceptCore<TResult>(IAsyncResult asyncResult) { if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 try { return TaskToAsyncResult.End<TResult>(asyncResult); } catch (SocketException) when (!_active) { // Socket.EndAccept(iar) throws ObjectDisposedException when the underlying socket gets closed. // TcpClient's documented behavior was to propagate that exception, we need to emulate it for compatibility: throw new ObjectDisposedException(typeof(Socket).FullName); } } } }