C# network
last modified October 18, 2026
In this article we show how to create basic network programs in C#.
The System.Net namespace and its child namespaces, such as
System.Net.Sockets, System.Net.Http, and
System.Net.NetworkInformation, provide a programming interface for
many of the protocols used on networks today.
C# Uri
Uri provides an object representation of a uniform resource
identifier (URI) and easy access to the parts of the URI.
var uri = new Uri("https://user:pass@example.com:8443/users/index.html?name=Peter&age=23#info");
Console.WriteLine($"Scheme: {uri.Scheme}");
Console.WriteLine($"UserInfo: {uri.UserInfo}");
Console.WriteLine($"Host: {uri.Host}");
Console.WriteLine($"Port: {uri.Port}");
Console.WriteLine($"Authority: {uri.Authority}");
Console.WriteLine($"AbsolutePath: {uri.AbsolutePath}");
Console.WriteLine($"Query: {uri.Query}");
Console.WriteLine($"PathAndQuery: {uri.PathAndQuery}");
Console.WriteLine($"Fragment: {uri.Fragment}");
Console.WriteLine($"Segments: {string.Join(", ", uri.Segments)}");
var uri2 = new Uri("http://example.com/");
Console.WriteLine($"Default port: {uri2.Port}, IsDefaultPort: {uri2.IsDefaultPort}");
var baseUri = new Uri("https://example.com/docs/");
var combined = new Uri(baseUri, "../about.html");
Console.WriteLine($"Combined: {combined}");
Console.WriteLine(Uri.TryCreate("not a url", UriKind.Absolute, out _));
In this example, we work with the Uri class.
var uri = new Uri("https://user:pass@example.com:8443/users/index.html?name=Peter&age=23#info");
We create a Uri that contains all the common parts: a scheme, user
information, a host, a port, a path, a query, and a fragment. The constructor
throws a UriFormatException if the string is not a valid URI.
Console.WriteLine($"Scheme: {uri.Scheme}");
Console.WriteLine($"UserInfo: {uri.UserInfo}");
Console.WriteLine($"Host: {uri.Host}");
...
We print the various parts of the Uri. The Authority
is the host with the port; the port is omitted if it is the default port of the
scheme.
var uri2 = new Uri("http://example.com/");
Console.WriteLine($"Default port: {uri2.Port}, IsDefaultPort: {uri2.IsDefaultPort}");
If the port is not specified, the Port property returns the
default port of the scheme; 80 for HTTP.
var baseUri = new Uri("https://example.com/docs/");
var combined = new Uri(baseUri, "../about.html");
A relative URI is resolved against a base URI.
Console.WriteLine(Uri.TryCreate("not a url", UriKind.Absolute, out _));
The TryCreate method validates a string without throwing an
exception.
$ dotnet run Scheme: https UserInfo: user:pass Host: example.com Port: 8443 Authority: example.com:8443 AbsolutePath: /users/index.html Query: ?name=Peter&age=23 PathAndQuery: /users/index.html?name=Peter&age=23 Fragment: #info Segments: /, users/, index.html Default port: 80, IsDefaultPort: True Combined: https://example.com/about.html False
C# UriBuilder
UriBuilder provides a convenient way to build a URI or modify the
contents of a Uri instance without creating a new
Uri instance for each modification.
var builder = new UriBuilder
{
Scheme = "https",
Host = "example.com",
Path = "users",
Query = "name=Peter Novak&age=23"
};
Uri uri = builder.Uri;
Console.WriteLine(uri);
Console.WriteLine(uri.AbsoluteUri);
builder.Path = "products";
builder.Query = $"q={Uri.EscapeDataString("C# & .NET")}";
Console.WriteLine(builder.Uri.AbsoluteUri);
The example builds URIs with UriBuilder.
var builder = new UriBuilder
{
Scheme = "https",
Host = "example.com",
Path = "users",
Query = "name=Peter Novak&age=23"
};
We set the parts of the URI with an object initializer.
Uri uri = builder.Uri; Console.WriteLine(uri); Console.WriteLine(uri.AbsoluteUri);
The Uri property returns the built Uri. The
ToString method returns an unescaped, human-readable form, while
AbsoluteUri returns the escaped form, which is suitable for
sending over the network.
builder.Path = "products";
builder.Query = $"q={Uri.EscapeDataString("C# & .NET")}";
We modify the builder. The value of a query parameter must be escaped with
Uri.EscapeDataString; otherwise the # and
& characters would be interpreted as a fragment and a
parameter separator.
$ dotnet run https://example.com/users?name=Peter Novak&age=23 https://example.com/users?name=Peter%20Novak&age=23 https://example.com/products?q=C%23%20%26%20.NET
C# hostname
The Dns.GetHostName method gets the host name of the local
computer.
using System.Net;
var hostName = Dns.GetHostName();
Console.WriteLine($"Hostname: {hostName}");
The program prints the hostname of our local computer.
$ dotnet run Hostname: universum
C# Dns.GetHostAddresses
The Dns.GetHostAddresses method returns an array of
IPAddress objects that holds the IP addresses for the host. We use
its asynchronous version, GetHostAddressesAsync.
using System.Net;
var hostname = "wikipedia.org";
IPAddress[] addresses = await Dns.GetHostAddressesAsync(hostname);
foreach (IPAddress address in addresses)
{
Console.WriteLine($"{address} {address.AddressFamily}");
}
The example prints all IP addresses of a website together with their address family.
$ dotnet run 2a02:ec80:300:ed1a::1 InterNetworkV6 185.15.59.224 InterNetwork
The output consists of an IPv6 and an IPv4 address. The actual addresses depend on the DNS server and the location.
C# Dns.GetHostEntry
The Dns.GetHostEntry method resolves a host name or an IP address
to an IPHostEntry instance. Besides the addresses, the entry
contains the host name and its aliases.
using System.Net;
var name = "wikipedia.org";
IPHostEntry host = await Dns.GetHostEntryAsync(name);
Console.WriteLine($"Host name: {host.HostName}");
foreach (var address in host.AddressList)
{
Console.WriteLine(address);
}
The example prints the host name and the IP addresses of the
wikipedia.org website.
foreach (var address in host.AddressList)
{
Console.WriteLine(address);
}
The AddressList property contains a list of IP addresses
that are associated with a host.
$ dotnet run Host name: wikipedia.org 2a02:ec80:300:ed1a::1 185.15.59.224
C# Ping
Ping is a network administration utility used to test the availability of a host on an Internet Protocol (IP) network. Ping works by sending Internet Control Message Protocol (ICMP) echo request packets to the target host and waiting for an ICMP echo reply.
.NET contains the Ping class for sending ping requests. The
Ping class uses instances of the PingReply class to
return information about the operation and receive the reply.
using System.Net.NetworkInformation;
using var ping = new Ping();
PingReply reply = await ping.SendPingAsync("example.com", TimeSpan.FromSeconds(2));
if (reply.Status == IPStatus.Success)
{
Console.WriteLine($"Status: {reply.Status}");
Console.WriteLine($"IP address: {reply.Address}");
Console.WriteLine($"Time: {reply.RoundtripTime} ms");
}
else
{
Console.WriteLine(reply.Status);
}
We send a ping request to the example.com host.
PingReply reply = await ping.SendPingAsync("example.com", TimeSpan.FromSeconds(2));
The SendPingAsync method attempts to send an ICMP echo message.
The second parameter is the timeout. If no reply arrives in time, the status is
TimedOut.
$ dotnet run Status: Success IP address: 2606:4700:10::6814:179a Time: 27 ms
See the C# Ping article for more examples.
C# Socket
In programming, a socket is an endpoint of a communication between two programs running on a network. Sockets are used to create a connection between a client program and a server program.
The System.Net.Sockets.Socket class implements the
Berkeley sockets interface.
using System.Net.Sockets;
using System.Text;
string server = "example.com";
int port = 80;
var request = $"GET / HTTP/1.1\r\nHost: {server}\r\nConnection: close\r\n\r\n";
byte[] requestBytes = Encoding.ASCII.GetBytes(request);
using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
socket.Connect(server, port);
Console.WriteLine($"Connected to {socket.RemoteEndPoint}");
socket.Send(requestBytes);
var buffer = new byte[1024];
var sb = new StringBuilder();
int n;
while ((n = socket.Receive(buffer)) > 0)
{
sb.Append(Encoding.UTF8.GetString(buffer, 0, n));
}
Console.WriteLine(sb);
The example uses the Socket class to send an HTTP request to a
web server and receive the response. Note that socket programming is
low-level. Classes such as HttpClient abstract away these
low-level details.
var request = $"GET / HTTP/1.1\r\nHost: {server}\r\nConnection: close\r\n\r\n";
We define a GET request. A request line consists of the method, the resource
path, and the HTTP version. In HTTP/1.1, the Host header is
mandatory. Each line ends with the \r\n characters and an empty
line ends the headers. The Connection: close header tells the
server to close the connection after sending the response. The details are
described in the RFC 9112
document.
byte[] requestBytes = Encoding.ASCII.GetBytes(request);
We transform the text data of our request into bytes.
using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp); socket.Connect(server, port);
A new TCP socket is created. The SocketType.Stream provides
reliable, two-way, connection-based byte streams. This constructor creates a
dual-mode socket, which works with both IPv4 and IPv6 addresses. The
Connect method resolves the host name and connects to the
server.
socket.Send(requestBytes);
We send the request to the server with the Send method.
while ((n = socket.Receive(buffer)) > 0)
{
sb.Append(Encoding.UTF8.GetString(buffer, 0, n));
}
We read the data from the socket with the Receive method until the
server closes the connection, which is signaled by zero bytes read. We
transform the bytes into text with the GetString method and add
the text into a StringBuilder.
$ dotnet run Connected to [2606:4700:10::6814:179a]:80 HTTP/1.1 200 OK Date: Sat, 17 Oct 2026 16:52:52 GMT Content-Type: text/html; charset=utf-8 Transfer-Encoding: chunked Connection: close Server: cloudflare ...
See the C# Socket article for more examples.
C# UdpClient
The UdpClient provides User Datagram Protocol (UDP) network
services. It contains methods for sending and receiving connectionless UDP
datagrams. We do not need to establish a connection with the remote host before
sending and receiving data, since UDP is a connectionless transport protocol.
In the following example, we create a simple UDP echo server and a client in one program. The server sends every datagram back to its sender.
using System.Net;
using System.Net.Sockets;
using System.Text;
var serverEndPoint = new IPEndPoint(IPAddress.Loopback, 5005);
using var server = new UdpClient(serverEndPoint);
var serverTask = RunEchoServer(server);
using var client = new UdpClient();
foreach (var msg in new[] { "Hello there", "How are you?", "Bye" })
{
byte[] data = Encoding.UTF8.GetBytes(msg);
await client.SendAsync(data, serverEndPoint);
UdpReceiveResult res = await client.ReceiveAsync();
Console.WriteLine($"client received: {Encoding.UTF8.GetString(res.Buffer)}");
}
await serverTask;
static async Task RunEchoServer(UdpClient server)
{
for (int i = 0; i < 3; i++)
{
UdpReceiveResult res = await server.ReceiveAsync();
Console.WriteLine($"server received {res.Buffer.Length} bytes from {res.RemoteEndPoint}");
await server.SendAsync(res.Buffer, res.RemoteEndPoint);
}
}
The client sends three messages and prints the echoed replies.
var serverEndPoint = new IPEndPoint(IPAddress.Loopback, 5005); using var server = new UdpClient(serverEndPoint); var serverTask = RunEchoServer(server);
The server UdpClient is bound to port 5005 on the loopback
interface. We start the server and keep its task.
UdpReceiveResult res = await server.ReceiveAsync(); ... await server.SendAsync(res.Buffer, res.RemoteEndPoint);
The ReceiveAsync method returns the datagram together with the
endpoint of the sender. The server sends the same bytes back to it.
using var client = new UdpClient(); ... await client.SendAsync(data, serverEndPoint); UdpReceiveResult res = await client.ReceiveAsync();
The client is bound to a random free port when it sends the first datagram.
$ dotnet run server received 11 bytes from 127.0.0.1:33011 client received: Hello there server received 12 bytes from 127.0.0.1:33011 client received: How are you? server received 3 bytes from 127.0.0.1:33011 client received: Bye
The messages that we send are echoed back to us.
C# TcpClient
The TcpClient class provides client connections for TCP network
services. It abstracts the low-level details of creating a Socket
for requesting and receiving data over TCP.
Since the connection to the remote device is represented as a stream, data can be read and written with the .NET stream-handling techniques.
using System.Net.Sockets;
using System.Text;
using var client = new TcpClient();
var hostname = "example.com";
await client.ConnectAsync(hostname, 80);
using NetworkStream networkStream = client.GetStream();
networkStream.ReadTimeout = 2000;
var message = """
GET / HTTP/1.1
Host: example.com
Accept: text/html
User-Agent: C# console app
Connection: close
""".ReplaceLineEndings("\r\n");
byte[] bytes = Encoding.ASCII.GetBytes(message);
await networkStream.WriteAsync(bytes);
using var reader = new StreamReader(networkStream, Encoding.UTF8);
Console.WriteLine(await reader.ReadToEndAsync());
The example creates a GET request with the TcpClient.
using var client = new TcpClient(); var hostname = "example.com"; await client.ConnectAsync(hostname, 80);
A TCP client is created. We connect to a remote host with the
ConnectAsync method.
using NetworkStream networkStream = client.GetStream(); networkStream.ReadTimeout = 2000;
With the GetStream method we obtain a network stream. We set the
read timeout to two seconds.
var message = """
GET / HTTP/1.1
Host: example.com
Accept: text/html
User-Agent: C# console app
Connection: close
""".ReplaceLineEndings("\r\n");
We build up the GET request message with a raw string literal. The two empty
lines produce the line terminator of the last header and the empty line that
ends the headers. HTTP requires the \r\n line endings, so we
replace the line endings of the source file with
ReplaceLineEndings.
byte[] bytes = Encoding.ASCII.GetBytes(message); await networkStream.WriteAsync(bytes);
We transform the text into bytes and write them to the network stream.
using var reader = new StreamReader(networkStream, Encoding.UTF8); Console.WriteLine(await reader.ReadToEndAsync());
We read the whole response with a StreamReader.
$ dotnet run HTTP/1.1 200 OK Date: Sat, 17 Oct 2026 16:52:52 GMT Content-Type: text/html; charset=utf-8 Transfer-Encoding: chunked Connection: close ... 241 <!doctype html><html lang=en><head><meta charset=utf-8>...
The body is sent in chunked transfer encoding; the 241 is the size
of the chunk in hexadecimal. Decoding such details is one of the reasons to use
HttpClient. See the C# TcpClient
article for more examples.
C# FTP and SMTP
The FtpWebRequest class, like the whole WebRequest
family, is obsolete in modern .NET and produces the SYSLIB0014
warning. For FTP, use a third-party library such as FluentFTP; see the
C# FluentFTP article.
The SmtpClient class from System.Net.Mail still
works, but Microsoft does not recommend it for new development, since it does
not support many modern protocols. The recommended alternative is the MailKit
library; see the C# send mail article.
C# HttpClient
HttpClient is a class for sending HTTP requests and receiving
HTTP responses from a resource identified by a URI.
using var client = new HttpClient();
using HttpResponseMessage res = await client.GetAsync("https://example.com");
Console.WriteLine($"{(int) res.StatusCode} {res.StatusCode}");
Console.WriteLine(res.Content.Headers.ContentType);
string content = await res.Content.ReadAsStringAsync();
Console.WriteLine($"{content.Length} characters received");
The example creates a GET request to a small website. We get the status code, the content type, and the body of the response.
using var client = new HttpClient();
A new HttpClient is created. An HttpClient is
intended to be created once and reused for the lifetime of the application;
creating a new instance for each request can exhaust the available sockets.
using HttpResponseMessage res = await client.GetAsync("https://example.com");
The GetAsync method sends a GET request to the specified URI as
an asynchronous operation. The await operator suspends the
evaluation of the enclosing async method until the asynchronous operation
completes. When the asynchronous operation completes, the await
operator returns the result of the operation.
string content = await res.Content.ReadAsStringAsync();
We read the body of the response as a string.
$ dotnet run 200 OK text/html; charset=utf-8 577 characters received
We get the 200 OK status code; the website is up. See the C# HttpClient article for more examples.
C# HttpListener
HttpListener is a simple, programmatically controlled HTTP protocol
listener. It is suitable for small tools and tests; for real web applications,
use ASP.NET Core.
using System.Net;
using System.Text;
using var listener = new HttpListener();
listener.Prefixes.Add("http://localhost:8001/");
listener.Start();
Console.WriteLine("Listening on port 8001...");
while (true)
{
HttpListenerContext context = await listener.GetContextAsync();
HttpListenerRequest req = context.Request;
Console.WriteLine($"{req.HttpMethod} {req.Url}");
Console.WriteLine($"User agent: {req.UserAgent}");
using HttpListenerResponse resp = context.Response;
byte[] buffer = Encoding.UTF8.GetBytes("Hello there!");
resp.ContentType = "text/plain; charset=utf-8";
resp.ContentLength64 = buffer.Length;
await resp.OutputStream.WriteAsync(buffer);
}
The example creates a simple HTTP server.
using var listener = new HttpListener();
listener.Prefixes.Add("http://localhost:8001/");
listener.Start();
We create a listener which listens on port 8001.
HttpListenerContext context = await listener.GetContextAsync(); HttpListenerRequest req = context.Request;
With GetContextAsync, we wait for an incoming request. The
context gives us access to the request and the response objects.
Console.WriteLine($"{req.HttpMethod} {req.Url}");
Console.WriteLine($"User agent: {req.UserAgent}");
The server logs the request to the terminal.
using HttpListenerResponse resp = context.Response;
byte[] buffer = Encoding.UTF8.GetBytes("Hello there!");
resp.ContentType = "text/plain; charset=utf-8";
resp.ContentLength64 = buffer.Length;
await resp.OutputStream.WriteAsync(buffer);
We set the content type and the length of the response and write the data to the output stream. Disposing the response sends it to the client and closes the output stream.
$ dotnet run Listening on port 8001... GET http://localhost:8001/greet?name=Jan User agent: curl/7.81.0
We run the server.
$ curl localhost:8001/greet?name=Jan Hello there!
We create a request with the curl tool. See the
C# HttpListener article for more examples.
C# reverse DNS lookup
A reverse DNS lookup finds the host name for an IP address. We pass an
IPAddress to the GetHostEntryAsync method.
using System.Net;
using System.Net.Sockets;
string[] addresses = ["8.8.8.8", "1.1.1.1", "185.15.59.224", "192.0.2.1"];
foreach (var a in addresses)
{
try
{
IPHostEntry entry = await Dns.GetHostEntryAsync(IPAddress.Parse(a));
Console.WriteLine($"{a,-15} {entry.HostName}");
}
catch (SocketException e)
{
Console.WriteLine($"{a,-15} {e.SocketErrorCode}");
}
}
We look up the names of four addresses. The 192.0.2.1 address
belongs to a range reserved for documentation and has no name; the lookup
throws a SocketException.
$ dotnet run 8.8.8.8 dns.google 1.1.1.1 one.one.one.one 185.15.59.224 text-lb.esams.wikimedia.org 192.0.2.1 HostNotFound
C# TCP port check
To find out whether a service listens on a port, we try to connect to it. The following example checks several ports concurrently, each with a timeout.
using System.Net.Sockets;
var host = "example.com";
int[] ports = [22, 80, 443, 8080];
var tasks = ports.Select(port => CheckPortAsync(host, port, TimeSpan.FromSeconds(2)));
bool[] results = await Task.WhenAll(tasks);
for (int i = 0; i < ports.Length; i++)
{
Console.WriteLine($"{host}:{ports[i]} {(results[i] ? "open" : "closed or filtered")}");
}
static async Task<bool> CheckPortAsync(string host, int port, TimeSpan timeout)
{
using var client = new TcpClient();
using var cts = new CancellationTokenSource(timeout);
try
{
await client.ConnectAsync(host, port, cts.Token);
return true;
}
catch (Exception e) when (e is SocketException or OperationCanceledException)
{
return false;
}
}
A port is open if the connection succeeds.
using var cts = new CancellationTokenSource(timeout); ... await client.ConnectAsync(host, port, cts.Token);
A firewall often drops connection attempts silently, and the operating system would wait a long time before giving up. The cancellation token source cancels the attempt after two seconds.
var tasks = ports.Select(port => CheckPortAsync(host, port, TimeSpan.FromSeconds(2))); bool[] results = await Task.WhenAll(tasks);
The checks run concurrently, so the whole program takes about as long as the slowest check.
$ dotnet run example.com:22 closed or filtered example.com:80 open example.com:443 open example.com:8080 open
Check only hosts that you own or are allowed to test.
Source
In this article we have created some basic networking programs in C#.
Author
List all C# tutorials.