Skip to content

Equivalent usages section in TCP #32106

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Nov 10, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
186 changes: 186 additions & 0 deletions docs/fundamentals/networking/sockets/tcp-classes.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,196 @@ The preceding C# code:
- Writes the sent message to the console.
- Finally, calls the <xref:System.Net.Sockets.TcpListener.Stop%2A> 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_](<xref:System.Net.Sockets.Socket.DualMode>) via the [Socket(SocketType, ProtocolType)](xref:System.Net.Sockets.Socket.%23ctor%2A) constructor. This constructor creates a dual-stack socket if [IPv6 is supported](<xref:System.Net.Sockets.Socket.OSSupportsIPv6>), otherwise, it falls back to IPv4.

Consider the following TCP client code:

```csharp
using var client = new TcpClient();
```

The preceding TCP client code is functionally equivalent to the following socket code:

```csharp
using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
```

#### The <xref:System.Net.Sockets.TcpClient.%23ctor(System.Net.Sockets.AddressFamily)> constructor

This constructor accepts only three `AddressFamily` values, otherwise it will throw a <xref:System.ArgumentException>. Valid values are:

- [AddressFamily.InterNetwork](xref:System.Net.Sockets.AddressFamily.InterNetwork): for IPv4 socket.
- [AddressFamily.InterNetworkV6](xref:System.Net.Sockets.AddressFamily.InterNetworkV6): for IPv6 socket.
- [AddressFamily.Unknown](xref:System.Net.Sockets.AddressFamily.Unknown): this will attempt to create a dual-stack socket, similarly to the default constructor.

Consider the following TCP client code:

```csharp
using var client = new TcpClient(AddressFamily.InterNetwork);
```

The preceding TCP client code is functionally equivalent to the following socket code:

```csharp
using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
```

#### The <xref:System.Net.Sockets.TcpClient.%23ctor(System.Net.IPEndPoint)> constructor

Upon creating the socket, this constructor will also **bind** to the provided **local** `IPEndPoint`. The <xref:System.Net.IPEndPoint.AddressFamily?displayProperty=nameWithType> property is used to determine the address family of the socket.

Consider the following TCP client code:

```csharp
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:

```csharp
// 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);
```

#### The <xref:System.Net.Sockets.TcpClient.%23ctor(System.String,System.Int32)> constructor

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:

```csharp
using var client = new TcpClient("www.example.com", 80);
```

The preceding TCP client code is functionally equivalent to the following socket code:

```csharp
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:

```csharp
using var client = new TcpClient();
client.Connect("www.example.com", 80);
```

The above `TcpClient` code is equivalent to the following socket code:

```csharp
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)`.

```csharp
var listener = new TcpListener(IPAddress.Loopback, 5000);
```

The preceding TCP listener code is functionally equivalent to the following socket code:

```csharp
var ep = new IPEndPoint(IPAddress.Loopback, 5000);
using var socket = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
```

### Start listening on the server

The <xref:System.Net.Sockets.TcpListener.Start> method is a wrapper combining `Socket`'s <xref:System.Net.Sockets.Socket.Bind%2A> and <xref:System.Net.Sockets.Socket.Listen> functionality.

Consider the following TCP listener code:

```csharp
var listener = new TcpListener(IPAddress.Loopback, 5000);
listener.Start(10);
```

The preceding TCP listener code is functionally equivalent to the following socket code:

```csharp
var endPoint = new IPEndPoint(IPAddress.Loopback, 5000);
using var socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(endPoint);
try
{
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 <xref:System.Net.Sockets.Socket> instance directly (via <xref:System.Net.Sockets.TcpListener.AcceptSocket> or <xref:System.Net.Sockets.TcpListener.AcceptSocketAsync>) or it can accept a <xref:System.Net.Sockets.TcpClient> (via <xref:System.Net.Sockets.TcpListener.AcceptTcpClient> and <xref:System.Net.Sockets.TcpListener.AcceptTcpClientAsync>).

Consider the following `TcpListener` code:

```csharp
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:

```csharp
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 <xref:System.Net.Sockets.NetworkStream> with the <xref:System.Net.Sockets.TcpClient.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:

```csharp
using var client = new TcpClient();
using NetworkStream stream = client.GetStream();
```

Which is equivalent to the following socket code:

```csharp
using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);

// Be aware that transfering the ownership means that closing/disposing the stream will also close the underlying socket.
using var stream = new NetworkStream(socket, ownsSocket: true);
```

> [!TIP]
> If your code doesn't need to work with a <xref:System.IO.Stream> instance, you can rely on `Socket`'s Send/Receive methods (<xref:System.Net.Sockets.Socket.Send%2A>, <xref:System.Net.Sockets.Socket.SendAsync%2A>, <xref:System.Net.Sockets.Socket.Receive%2A> and <xref:System.Net.Sockets.Socket.ReceiveAsync%2A>) directly instead of creating a <xref:System.Net.Sockets.NetworkStream>.

## See also

- [Use Sockets to send and receive data over TCP](socket-services.md)
- [Networking in .NET](../overview.md)
- <xref:System.Net.Sockets.Socket>
- <xref:System.Net.Sockets.TcpClient>
- <xref:System.Net.Sockets.TcpListener>
- <xref:System.Net.Sockets.NetworkStream>