|
1 | | -[](https://www.nuget.org/packages/WebSocketChannel) |
2 | | -[](https://www.nuget.org/packages/WebSocketChannel) |
3 | | -[](https://github.com/devlooped/WebSocketChannel/blob/main/license.txt) |
4 | | -[](https://github.com/devlooped/WebSocketChannel/actions) |
| 1 | +<!-- include ../../readme.md#content --> |
| 2 | +<!-- include ../../readme.md#sponsors --> |
5 | 3 |
|
6 | | -# Usage |
7 | | - |
8 | | -```csharp |
9 | | -var client = new ClientWebSocket(); |
10 | | -await client.ConnectAsync(serverUri, CancellationToken.None); |
11 | | - |
12 | | -Channel<ReadOnlyMemory<byte>> channel = client.CreateChannel(); |
13 | | - |
14 | | -await channel.Writer.WriteAsync(Encoding.UTF8.GetBytes("hello").AsMemory()); |
15 | | - |
16 | | -// Read single message when it arrives |
17 | | -ReadOnlyMemory<byte> response = await channel.Reader.ReadAsync(); |
18 | | - |
19 | | -// Read all messages while underlying websocket is open |
20 | | -await foreach (var item in channel.Reader.ReadAllAsync()) |
21 | | -{ |
22 | | - Console.WriteLine(Encoding.UTF8.GetString(item.Span)); |
23 | | -} |
24 | | - |
25 | | -// Completing the writer closes the underlying websocket cleanly |
26 | | -channel.Writer.Complete(); |
27 | | - |
28 | | -// Can also complete reporting an error for the remote party |
29 | | -channel.Writer.Complete(new InvalidOperationException("Bad format")); |
30 | | -``` |
31 | | - |
32 | | - |
33 | | -The `WebSocketChannel` can also be used on the server. The following example is basically |
34 | | -taken from the documentation on [WebSockets in ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/websockets?view=aspnetcore-5.0#configure-the-middleware) |
35 | | -and adapted to use a `WebSocketChannel` to echo messages to the client: |
36 | | - |
37 | | -```csharp |
38 | | -app.Use(async (context, next) => |
39 | | -{ |
40 | | - if (context.Request.Path == "/ws") |
41 | | - { |
42 | | - if (context.WebSockets.IsWebSocketRequest) |
43 | | - { |
44 | | - using var webSocket = await context.WebSockets.AcceptWebSocketAsync(); |
45 | | - var channel = WebSocketChannel.Create(webSocket); |
46 | | - try |
47 | | - { |
48 | | - await foreach (var item in channel.Reader.ReadAllAsync(context.RequestAborted)) |
49 | | - { |
50 | | - await channel.Writer.WriteAsync(item, context.RequestAborted); |
51 | | - } |
52 | | - } |
53 | | - catch (OperationCanceledException) |
54 | | - { |
55 | | - try |
56 | | - { |
57 | | - await webSocket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, null, default); |
58 | | - } |
59 | | - catch { } // Best effort to try closing cleanly. Client may be entirely gone. |
60 | | - } |
61 | | - } |
62 | | - else |
63 | | - { |
64 | | - context.Response.StatusCode = (int) HttpStatusCode.BadRequest; |
65 | | - } |
66 | | - } |
67 | | - else |
68 | | - { |
69 | | - await next(); |
70 | | - } |
71 | | -}); |
72 | | -``` |
73 | | - |
74 | | - |
75 | | -# Installation |
76 | | - |
77 | | -This project can be used either as a regular nuget package: |
78 | | - |
79 | | -``` |
80 | | -<PackageReference Include="WebSocketChannel" Version="*" /> |
81 | | -``` |
82 | | - |
83 | | -Or alternatively, referenced directly as a source-only dependency using [dotnet-file](https://www.nuget.org/packages/dotnet-file): |
84 | | - |
85 | | -``` |
86 | | -> dotnet file add https://github.com/devlooped/WebSocketChannel/blob/main/src/WebSocketChannel/WebSocketChannel.cs |
87 | | -> dotnet file add https://github.com/devlooped/WebSocketChannel/blob/main/src/WebSocketChannel/WebSocketExtensions.cs |
88 | | -``` |
89 | | - |
90 | | -It's also possible to specify a desired target location for the referenced source files, such as: |
91 | | - |
92 | | -``` |
93 | | -> dotnet file add https://github.com/devlooped/WebSocketChannel/blob/main/src/WebSocketChannel/WebSocketChannel.cs src/MyProject/External/. |
94 | | -> dotnet file add https://github.com/devlooped/WebSocketChannel/blob/main/src/WebSocketChannel/WebSocketExtensions.cs src/MyProject/External/. |
95 | | -``` |
96 | | - |
97 | | -When referenced as loose source files, it's easy to also get automated PRs when the upstream files change, |
98 | | -as in the [dotnet-file.yml](https://github.com/devlooped/dotnet-file/blob/main/.github/workflows/dotnet-file.yml) workflow that |
99 | | -keeps the repository up to date with a template. See also [dotnet-config](https://dotnetconfig.org), which is used to |
100 | | -for the `dotnet-file` configuration settings that tracks all this. |
101 | | - |
102 | | - |
103 | | - |
104 | | -## Sponsors |
105 | | - |
106 | | -[](https://github.com/sponsors/devlooped) [](https://github.com/clarius)[](https://github.com/clarius) |
107 | | - |
108 | | -*[get mentioned here too](https://github.com/sponsors/devlooped)!* |
| 4 | +<!-- Exclude from auto-expansion by devlooped/actions-include GH action --> |
| 5 | +<!-- exclude --> |
0 commit comments