C# tutorials > Input/Output (I/O) and Networking > .NET Networking > Working with WebSockets (`ClientWebSocket`, `WebSocket`)
Working with WebSockets (`ClientWebSocket`, `WebSocket`)
This tutorial explores how to use WebSockets in C# with `ClientWebSocket` for clients and `WebSocket` (primarily within ASP.NET Core) for servers. WebSockets provide full-duplex communication channels over a single TCP connection, enabling real-time data exchange between client and server.
Introduction to WebSockets
What are WebSockets?
WebSockets are a communication protocol that provides full-duplex communication channels over a single TCP connection. Unlike HTTP, which is request-response based, WebSockets allow for persistent connections where both client and server can send data at any time.
Why use WebSockets?
WebSockets are ideal for real-time applications such as chat applications, online games, financial tickers, and live dashboards where low latency and continuous data flow are crucial.
Client-Side WebSocket with `ClientWebSocket`
This code snippet demonstrates how to create a WebSocket client using `ClientWebSocket`.
Explanation:
using System;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
public class WebSocketClient
{
public static async Task ConnectAndReceive(Uri wsUri)
{
using (ClientWebSocket ws = new ClientWebSocket())
{
try
{
await ws.ConnectAsync(wsUri, CancellationToken.None);
Console.WriteLine("Connected to: " + wsUri);
await Receive(ws);
}
catch (Exception ex)
{
Console.WriteLine("Exception: {0}", ex);
}
finally
{
Console.WriteLine("Closing...");
if (ws.State == WebSocketState.Open)
{
await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
}
}
}
}
private static async Task Receive(ClientWebSocket ws)
{
byte[] buffer = new byte[1024 * 4];
while (ws.State == WebSocketState.Open)
{
WebSocketReceiveResult result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Close)
{
await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closed by server", CancellationToken.None);
break;
}
else
{
Console.WriteLine("Received: " + Encoding.UTF8.GetString(buffer, 0, result.Count));
}
}
}
public static async Task Send(ClientWebSocket ws, string message)
{
byte[] buffer = Encoding.UTF8.GetBytes(message);
await ws.SendAsync(new ArraySegment<byte>(buffer, 0, buffer.Length), WebSocketMessageType.Text, true, CancellationToken.None);
Console.WriteLine("Sent: " + message);
}
public static async Task Main(string[] args)
{
Uri wsUri = new Uri("ws://localhost:5000/ws"); // Replace with your WebSocket server URL
await ConnectAndReceive(wsUri);
}
}
Server-Side WebSocket with `WebSocket` (ASP.NET Core)
This code snippet demonstrates how to handle WebSocket connections in an ASP.NET Core application.
Explanation:
// Startup.cs (Configure method)
app.UseWebSockets();
app.Use(async (context, next) =>
{
if (context.Request.Path == "/ws")
{
if (context.WebSockets.IsWebSocketRequest)
{
WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
await Echo(webSocket);
}
else
{
context.Response.StatusCode = 400;
}
}
else
{
await next();
}
});
// Echo method
private static async Task Echo(WebSocket webSocket)
{
var buffer = new byte[1024 * 4];
WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
while (!result.CloseStatus.HasValue)
{
await webSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);
result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
}
await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
}
Concepts Behind the Snippet
WebSocket Protocol: WebSockets use the `ws://` or `wss://` URI scheme. `wss://` indicates a secure WebSocket connection over TLS/SSL.
Full-Duplex Communication: Unlike HTTP, WebSockets allow data to flow in both directions simultaneously.
Persistent Connection: The connection remains open until explicitly closed by either the client or the server.
Real-Life Use Case
A real-time chat application is a classic example of WebSocket usage. When a user sends a message, the server immediately pushes the message to all connected clients in the chat room without requiring each client to repeatedly poll the server for updates. This drastically reduces latency and provides a seamless user experience.
Best Practices
Interview Tip
When discussing WebSockets in an interview, highlight your understanding of the benefits of full-duplex communication, the importance of persistent connections, and the use cases where WebSockets are superior to traditional HTTP polling. Be prepared to explain the difference between `ws://` and `wss://` and the security implications.
When to Use Them
Use WebSockets when:
Memory Footprint
WebSockets can be more memory-efficient than long polling or server-sent events because they maintain a single persistent connection. However, you need to manage WebSocket connections carefully to avoid memory leaks or resource exhaustion, especially with a large number of concurrent connections. Pooling buffers used for receiving and sending data can help reduce memory allocation overhead.
Alternatives
Alternatives to WebSockets include:
Pros
Cons
FAQ
-
What's the difference between `ws://` and `wss://`?
`ws://` is the URI scheme for unencrypted WebSockets, while `wss://` is the URI scheme for secure WebSockets using TLS/SSL. Always use `wss://` when transmitting sensitive data. -
How do I handle errors in WebSocket connections?
Wrap your WebSocket code in `try-catch` blocks to catch exceptions during connection, sending, and receiving. Log the errors for debugging and handle them gracefully to prevent application crashes. Implement retry logic for transient errors. -
How do I close a WebSocket connection?
Use the `CloseAsync` method of the `ClientWebSocket` or `WebSocket` object. Specify a `WebSocketCloseStatus` and a description for the closure. Ensure that both the client and server close the connection gracefully.