This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
Download Microsoft Edge
More info about Internet Explorer and Microsoft Edge
Important
The
Socket
class is highly recommended for advanced users, instead of
TcpClient
and
TcpListener
.
To work with Transmission Control Protocol (TCP), you have two options: either use
Socket
for maximum control and performance, or use the
TcpClient
and
TcpListener
helper classes.
TcpClient
and
TcpListener
are built on top of the
System.Net.Sockets.Socket
class and take care of the details of transferring data for ease of use.
The protocol classes use the underlying
Socket
class to provide simple access to network services without the overhead of maintaining state information or knowing the details of setting up protocol-specific sockets. To use asynchronous
Socket
methods, you can use the asynchronous methods supplied by the
NetworkStream
class. To access features of the
Socket
class not exposed by the protocol classes, you must use the
Socket
class.
TcpClient
and
TcpListener
represent the network using the
NetworkStream
class. You use the
GetStream
method to return the network stream, and then call the stream's
NetworkStream.ReadAsync
and
NetworkStream.WriteAsync
methods. The
NetworkStream
does not own the protocol classes' underlying socket, so closing it does not affect the socket.
Use
TcpClient
and
TcpListener
The
TcpClient
class requests data from an internet resource using TCP. The methods and properties of
TcpClient
abstract the details for creating a
Socket
for requesting and receiving data using TCP. Because the connection to the remote device is represented as a stream, data can be read and written with .NET Framework stream-handling techniques.
The TCP protocol establishes a connection with a remote endpoint and then uses that connection to send and receive data packets. TCP is responsible for ensuring that data packets are sent to the endpoint and assembled in the correct order when they arrive.
Create an IP endpoint
When working with
System.Net.Sockets
, you represent a network endpoint as an
IPEndPoint
object. The
IPEndPoint
is constructed with an
IPAddress
and its corresponding port number. Before you can initiate a conversation through a
Socket
, you create a data pipe between your app and the remote destination.
TCP/IP uses a network address and a service port number to uniquely identify a service. The network address identifies a specific network destination; the port number identifies the specific service on that device to connect to. The combination of network address and service port is called an endpoint, which is represented in the .NET by the
EndPoint
class. A descendant of
EndPoint
is defined for each supported address family; for the IP address family, the class is
IPEndPoint
.
The
Dns
class provides domain-name services to apps that use TCP/IP internet services. The
GetHostEntryAsync
method queries a DNS server to map a user-friendly domain name (such as "host.contoso.com") to a numeric Internet address (such as
192.168.1.1
).
GetHostEntryAsync
returns a
Task<IPHostEntry>
that when awaited contains a list of addresses and aliases for the requested name. In most cases, you can use the first address returned in the
AddressList
array. The following code gets an
IPAddress
containing the IP address for the server
host.contoso.com
.
IPHostEntry ipHostInfo = await Dns.GetHostEntryAsync("host.contoso.com");
IPAddress ipAddress = ipHostInfo.AddressList[0];
For manual testing and debugging purposes, you can typically use the GetHostEntryAsync method to get given the Dns.GetHostName() value to resolve the localhost name to an IP address.
The Internet Assigned Numbers Authority (IANA) defines port numbers for common services. For more information, see IANA: Service Name and Transport Protocol Port Number Registry). Other services can have registered port numbers in the range 1,024 to 65,535. The following code combines the IP address for host.contoso.com
with a port number to create a remote endpoint for a connection.
IPEndPoint ipEndPoint = new(ipAddress, 11_000);
After determining the address of the remote device and choosing a port to use for the connection, the app can establish a connection with the remote device.
Create a TcpClient
The TcpClient
class provides TCP services at a higher level of abstraction than the Socket
class. TcpClient
is used to create a client connection to a remote host. Knowing how to get an IPEndPoint
, let's assume you have an IPAddress
to pair with your desired port number. The following example demonstrates setting up a TcpClient
to connect to a time server on TCP port 13:
var ipEndPoint = new IPEndPoint(ipAddress, 13);
using TcpClient client = new();
await client.ConnectAsync(ipEndPoint);
await using NetworkStream stream = client.GetStream();
var buffer = new byte[1_024];
int received = await stream.ReadAsync(buffer);
var message = Encoding.UTF8.GetString(buffer, 0, received);
Console.WriteLine($"Message received: \"{message}\"");
// Sample output:
// Message received: "📅 8/22/2022 9:07:17 AM 🕛"
The preceding C# code:
Creates an IPEndPoint
from a known IPAddress
and port.
Instantiate a new TcpClient
object.
Connects the client
to the remote TCP time server on port 13 using TcpClient.ConnectAsync.
Uses a NetworkStream to read data from the remote host.
Declares a read buffer of 1_024
bytes.
Reads data from the stream
into the read buffer.
Writes the results as a string to the console.
Since the client knows that the message is small, the entire message can be read into the read buffer in one operation. With larger messages, or messages with an indeterminate length, the client should use the buffer more appropriately and read in a while
loop.
Important
When sending and receiving messages, the Encoding should be known ahead of time to both server and client. For example, if the server communicates using ASCIIEncoding but the client attempts to use UTF8Encoding, the messages will be malformed.
Create a TcpListener
The TcpListener type is used to monitor a TCP port for incoming requests and then create either a Socket
or a TcpClient
that manages the connection to the client. The Start method enables listening, and the Stop method disables listening on the port. The AcceptTcpClientAsync method accepts incoming connection requests and creates a TcpClient
to handle the request, and the AcceptSocketAsync method accepts incoming connection requests and creates a Socket
to handle the request.
The following example demonstrates creating a network time server using a TcpListener
to monitor TCP port 13. When an incoming connection request is accepted, the time server responds with the current date and time from the host server.
var ipEndPoint = new IPEndPoint(IPAddress.Any, 13);
TcpListener listener = new(ipEndPoint);
listener.Start();
using TcpClient handler = await listener.AcceptTcpClientAsync();
await using NetworkStream stream = handler.GetStream();
var message = $"📅 {DateTime.Now} 🕛";
var dateTimeBytes = Encoding.UTF8.GetBytes(message);
await stream.WriteAsync(dateTimeBytes);
Console.WriteLine($"Sent message: \"{message}\"");
// Sample output:
// Sent message: "📅 8/22/2022 9:07:17 AM 🕛"
finally
listener.Stop();
The preceding C# code:
Creates an IPEndPoint
with IPAddress.Any and port.
Instantiate a new TcpListener
object.
Calls the Start method to start listening on the port.
Uses a TcpClient
from the AcceptTcpClientAsync method to accept incoming connection requests.
Encodes the current date and time as a string message.
Uses a NetworkStream to write data to the connected client.
Writes the sent message to the console.
Finally, calls the Stop method to stop listening on the port.
Finite TCP control with the Socket
class
Both TcpClient
and TcpListener
internally rely on the Socket
class, meaning anything you can do with these classes can be achieved using sockets directly. This section demonstrates several TcpClient
and TcpListener
use cases, along with their Socket
counterpart that is functionally equivalent.
Create a client socket
TcpClient
's default constructor tries to create a dual-stack socket via the Socket(SocketType, ProtocolType) constructor. This constructor creates a dual-stack socket if IPv6 is supported, otherwise, it falls back to IPv4.
Consider the following TCP client code:
using var client = new TcpClient();
The preceding TCP client code is functionally equivalent to the following socket code:
using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
This constructor accepts only three AddressFamily
values, otherwise it will throw a ArgumentException. Valid values are:
AddressFamily.InterNetwork: for IPv4 socket.
AddressFamily.InterNetworkV6: for IPv6 socket.
AddressFamily.Unknown: this will attempt to create a dual-stack socket, similarly to the default constructor.
Consider the following TCP client code:
using var client = new TcpClient(AddressFamily.InterNetwork);
The preceding TCP client code is functionally equivalent to the following socket code:
using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Upon creating the socket, this constructor will also bind to the provided local IPEndPoint
. The IPEndPoint.AddressFamily property is used to determine the address family of the socket.
Consider the following TCP client code:
var endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5001);
using var client = new TcpClient(endPoint);
The preceding TCP client code is functionally equivalent to the following socket code:
// Example IPEndPoint object
var endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5001);
using var socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(endPoint);
This constructor will attempt to create a dual-stack similar to the default constructor and connect it to the remote DNS endpoint defined by the hostname
and port
pair.
Consider the following TCP client code:
using var client = new TcpClient("www.example.com", 80);
The preceding TCP client code is functionally equivalent to the following socket code:
using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
socket.Connect("www.example.com", 80);
Connect to server
All Connect
, ConnectAsync
, BeginConnect
and EndConnect
overloads in TcpClient
are functionally equivalent to the corresponding Socket
methods.
Consider the following TCP client code:
using var client = new TcpClient();
client.Connect("www.example.com", 80);
The above TcpClient
code is equivalent to the following socket code:
using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
socket.Connect("www.example.com", 80);
Create a server socket
Much like TcpClient
instances having functional equivalency with their raw Socket
counterparts, this section maps TcpListener
constructors to their corresponding socket code. The first constructor to consider is the TcpListener(IPAddress localaddr, int port)
.
var listener = new TcpListener(IPAddress.Loopback, 5000);
The preceding TCP listener code is functionally equivalent to the following socket code:
var ep = new IPEndPoint(IPAddress.Loopback, 5000);
using var socket = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
Start listening on the server
The Start() method is a wrapper combining Socket
's Bind and Listen() functionality.
Consider the following TCP listener code:
var listener = new TcpListener(IPAddress.Loopback, 5000);
listener.Start(10);
The preceding TCP listener code is functionally equivalent to the following socket code:
var endPoint = new IPEndPoint(IPAddress.Loopback, 5000);
using var socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(endPoint);
socket.Listen(10);
catch (SocketException)
socket.Dispose();
Accept a server connection
Under the hood, incoming TCP connections are always creating a new socket when accepted. TcpListener
can accept a Socket instance directly (via AcceptSocket() or AcceptSocketAsync()) or it can accept a TcpClient (via AcceptTcpClient() and AcceptTcpClientAsync()).
Consider the following TcpListener
code:
var listener = new TcpListener(IPAddress.Loopback, 5000);
using var acceptedSocket = await listener.AcceptSocketAsync();
// Synchronous alternative.
// var acceptedSocket = listener.AcceptSocket();
The preceding TCP listener code is functionally equivalent to the following socket code:
var endPoint = new IPEndPoint(IPAddress.Loopback, 5000);
using var socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
using var acceptedSocket = await socket.AcceptAsync();
// Synchronous alternative
// var acceptedSocket = socket.Accept();
Create a NetworkStream
to send and receive data
With TcpClient
you need to instantiate a NetworkStream with the GetStream() method to be able to send and receive data . With Socket
, you have to do the NetworkStream
creation manually.
Consider the following TcpClient
code:
using var client = new TcpClient();
using NetworkStream stream = client.GetStream();
Which is equivalent to the following socket code:
using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
// Be aware that transferring the ownership means that closing/disposing the stream will also close the underlying socket.
using var stream = new NetworkStream(socket, ownsSocket: true);
If your code doesn't need to work with a Stream instance, you can rely on Socket
's Send/Receive methods (Send, SendAsync, Receive and ReceiveAsync) directly instead of creating a NetworkStream.
See also
Use Sockets to send and receive data over TCP
Networking in .NET
Socket
TcpClient
TcpListener
NetworkStream