Skip to content

Commit 83123b5

Browse files
committed
dotnet: add websocket example program
1 parent 2501e5f commit 83123b5

File tree

3 files changed

+197
-1
lines changed

3 files changed

+197
-1
lines changed

runtime/dotnet/HelloWorld/Program.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ public static async Task Main(string[] args)
5252

5353
// Exercise websockets
5454
var client = new ClientWebSocket();
55-
var uri = new Uri("ws://corefx-net-http11.azurewebsites.net/WebSocket/EchoWebSocket.ashx");
55+
// var uri = new Uri("ws://corefx-net-http11.azurewebsites.net/WebSocket/EchoWebSocket.ashx");
56+
var uri = new Uri("ws://localhost:8001/");
5657

5758
await client.ConnectAsync(uri, CancellationToken.None);
5859

runtime/dotnet/wserver/Program.cs

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
namespace wserver;
2+
3+
using System;
4+
using System.Collections.Concurrent;
5+
using System.Net;
6+
using System.Net.WebSockets;
7+
using System.Text;
8+
using System.Threading;
9+
using System.Threading.Tasks;
10+
11+
#nullable enable
12+
13+
public class WebSocketServer
14+
{
15+
private readonly HttpListener _listener;
16+
private readonly ConcurrentDictionary<Guid, WebSocket> _sockets;
17+
private readonly CancellationTokenSource _cancellationTokenSource;
18+
19+
public WebSocketServer(string prefix)
20+
{
21+
_listener = new HttpListener();
22+
_listener.Prefixes.Add(prefix);
23+
_sockets = new ConcurrentDictionary<Guid, WebSocket>();
24+
_cancellationTokenSource = new CancellationTokenSource();
25+
}
26+
27+
public async Task StartAsync()
28+
{
29+
_listener.Start();
30+
Console.WriteLine("WebSocket server started...");
31+
32+
while (!_cancellationTokenSource.Token.IsCancellationRequested)
33+
{
34+
try
35+
{
36+
HttpListenerContext context = await _listener.GetContextAsync();
37+
38+
if (context.Request.IsWebSocketRequest)
39+
{
40+
ProcessWebSocketRequest(context);
41+
}
42+
else
43+
{
44+
context.Response.StatusCode = 400;
45+
context.Response.Close();
46+
}
47+
}
48+
catch (Exception ex)
49+
{
50+
Console.WriteLine($"Error: {ex.Message}");
51+
}
52+
}
53+
}
54+
55+
private async void ProcessWebSocketRequest(HttpListenerContext context)
56+
{
57+
HttpListenerWebSocketContext? webSocketContext = null;
58+
Guid socketId = Guid.Empty;
59+
60+
try
61+
{
62+
webSocketContext = await context.AcceptWebSocketAsync(subProtocol: null);
63+
64+
// Handle potential null return
65+
if (webSocketContext?.WebSocket == null)
66+
{
67+
context.Response.StatusCode = 500;
68+
context.Response.Close();
69+
return;
70+
}
71+
72+
WebSocket socket = webSocketContext.WebSocket;
73+
socketId = Guid.NewGuid();
74+
_sockets.TryAdd(socketId, socket);
75+
76+
Console.WriteLine($"Client {socketId} connected");
77+
78+
await HandleMessages(socket, socketId);
79+
}
80+
catch (Exception ex)
81+
{
82+
Console.WriteLine($"WebSocket error: {ex.Message}");
83+
if (webSocketContext?.WebSocket != null && webSocketContext.WebSocket.State == WebSocketState.Open)
84+
{
85+
await webSocketContext.WebSocket.CloseAsync(WebSocketCloseStatus.InternalServerError, "Error", CancellationToken.None);
86+
}
87+
}
88+
finally
89+
{
90+
if (webSocketContext?.WebSocket != null)
91+
{
92+
WebSocket socket = webSocketContext.WebSocket;
93+
if (socket.State == WebSocketState.Open || socket.State == WebSocketState.CloseReceived)
94+
{
95+
await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
96+
}
97+
}
98+
99+
WebSocket? removedSocket;
100+
_sockets.TryRemove(socketId, out removedSocket);
101+
Console.WriteLine($"Client {socketId} disconnected");
102+
}
103+
}
104+
105+
private async Task HandleMessages(WebSocket socket, Guid socketId)
106+
{
107+
byte[] buffer = new byte[1024];
108+
109+
while (socket.State == WebSocketState.Open)
110+
{
111+
WebSocketReceiveResult result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
112+
113+
if (result.MessageType == WebSocketMessageType.Text)
114+
{
115+
string message = Encoding.UTF8.GetString(buffer, 0, result.Count);
116+
Console.WriteLine($"Received from {socketId}: {message}");
117+
118+
await socket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count),
119+
WebSocketMessageType.Text, result.EndOfMessage, CancellationToken.None);
120+
121+
BroadcastMessage(message, socketId);
122+
}
123+
else if (result.MessageType == WebSocketMessageType.Close)
124+
{
125+
await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
126+
}
127+
}
128+
}
129+
130+
private void BroadcastMessage(string message, Guid senderId)
131+
{
132+
byte[] messageBytes = Encoding.UTF8.GetBytes(message);
133+
134+
foreach (var socket in _sockets.Values)
135+
{
136+
if (socket.State == WebSocketState.Open)
137+
{
138+
try
139+
{
140+
socket.SendAsync(new ArraySegment<byte>(messageBytes),
141+
WebSocketMessageType.Text, true, CancellationToken.None);
142+
}
143+
catch (Exception ex)
144+
{
145+
Console.WriteLine($"Broadcast error: {ex.Message}");
146+
}
147+
}
148+
}
149+
}
150+
151+
public async Task StopAsync()
152+
{
153+
_cancellationTokenSource.Cancel();
154+
155+
foreach (var socket in _sockets.Values)
156+
{
157+
if (socket.State == WebSocketState.Open)
158+
{
159+
await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Server stopping", CancellationToken.None);
160+
}
161+
}
162+
163+
_listener.Stop();
164+
_listener.Close();
165+
166+
Console.WriteLine("WebSocket server stopped");
167+
}
168+
}
169+
170+
public class Program
171+
{
172+
public static async Task Main(string[] args)
173+
{
174+
var server = new WebSocketServer("http://localhost:8001/");
175+
176+
try
177+
{
178+
await server.StartAsync();
179+
}
180+
catch (Exception ex)
181+
{
182+
Console.WriteLine($"Server error: {ex.Message}");
183+
}
184+
}
185+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net10.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
</Project>

0 commit comments

Comments
 (0)