{"id":18602964,"url":"https://github.com/devlooped/websocketchannel","last_synced_at":"2025-04-10T19:31:20.842Z","repository":{"id":40290388,"uuid":"413500075","full_name":"devlooped/WebSocketChannel","owner":"devlooped","description":"High-performance System.Threading.Channels API adapter for System.Net.WebSockets","archived":false,"fork":false,"pushed_at":"2025-03-18T00:28:44.000Z","size":173,"stargazers_count":11,"open_issues_count":7,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-25T03:34:03.654Z","etag":null,"topics":["csharp","dotnet","websocket","websockets"],"latest_commit_sha":null,"homepage":"https://clarius.org/WebSocketChannel","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/devlooped.png","metadata":{"files":{"readme":"readme.md","changelog":"changelog.md","contributing":null,"funding":null,"license":"license.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"devlooped"}},"created_at":"2021-10-04T16:25:00.000Z","updated_at":"2025-02-25T02:49:11.000Z","dependencies_parsed_at":"2025-03-18T01:24:35.541Z","dependency_job_id":"dfc6772b-5a67-43ad-937d-3846c28c4abb","html_url":"https://github.com/devlooped/WebSocketChannel","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devlooped%2FWebSocketChannel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devlooped%2FWebSocketChannel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devlooped%2FWebSocketChannel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devlooped%2FWebSocketChannel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/devlooped","download_url":"https://codeload.github.com/devlooped/WebSocketChannel/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248281398,"owners_count":21077423,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","dotnet","websocket","websockets"],"created_at":"2024-11-07T02:13:07.527Z","updated_at":"2025-04-10T19:31:15.824Z","avatar_url":"https://github.com/devlooped.png","language":"C#","funding_links":["https://github.com/sponsors/devlooped","https://github.com/sponsors"],"categories":[],"sub_categories":[],"readme":"![Icon](https://raw.githubusercontent.com/devlooped/WebSocketChannel/main/assets/img/icon.png) WebSocketChannel\n============\n\nHigh-performance [System.Threading.Channels](https://devblogs.microsoft.com/dotnet/an-introduction-to-system-threading-channels/) API adapter for System.Net.WebSockets\n\n[![Version](https://img.shields.io/nuget/v/WebSocketChannel.svg?color=royalblue)](https://www.nuget.org/packages/WebSocketChannel)\n[![Downloads](https://img.shields.io/nuget/dt/WebSocketChannel.svg?color=green)](https://www.nuget.org/packages/WebSocketChannel)\n[![License](https://img.shields.io/github/license/devlooped/WebSocketChannel.svg?color=blue)](https://github.com/devlooped/WebSocketChannel/blob/main/license.txt)\n[![Build](https://github.com/devlooped/WebSocketChannel/workflows/build/badge.svg?branch=main)](https://github.com/devlooped/WebSocketChannel/actions)\n\n\u003c!-- #content --\u003e\n# Usage\n\n```csharp\nvar client = new ClientWebSocket();\nawait client.ConnectAsync(serverUri, CancellationToken.None);\n\nChannel\u003cReadOnlyMemory\u003cbyte\u003e\u003e channel = client.CreateChannel();\n\nawait channel.Writer.WriteAsync(Encoding.UTF8.GetBytes(\"hello\").AsMemory());\n\n// Read single message when it arrives\nReadOnlyMemory\u003cbyte\u003e response = await channel.Reader.ReadAsync();\n\n// Read all messages while underlying websocket is open\nawait foreach (var item in channel.Reader.ReadAllAsync())\n{\n    Console.WriteLine(Encoding.UTF8.GetString(item.Span));\n}\n\n// Completing the writer closes the underlying websocket cleanly\nchannel.Writer.Complete();\n\n// Can also complete reporting an error for the remote party\nchannel.Writer.Complete(new InvalidOperationException(\"Bad format\"));\n```\n\n\nThe `WebSocketChannel` can also be used on the server. The following example is basically \ntaken 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) \nand adapted to use a `WebSocketChannel` to echo messages to the client:\n\n```csharp\napp.Use(async (context, next) =\u003e\n{\n    if (context.Request.Path == \"/ws\")\n    {\n        if (context.WebSockets.IsWebSocketRequest)\n        {\n            using var webSocket = await context.WebSockets.AcceptWebSocketAsync();\n            var channel = WebSocketChannel.Create(webSocket);\n            try\n            {\n                await foreach (var item in channel.Reader.ReadAllAsync(context.RequestAborted))\n                {\n                    await channel.Writer.WriteAsync(item, context.RequestAborted);\n                }\n            }\n            catch (OperationCanceledException)\n            {\n                try\n                {\n                    await webSocket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, null, default);\n                }\n                catch { } // Best effort to try closing cleanly. Client may be entirely gone.\n            }\n        }\n        else\n        {\n            context.Response.StatusCode = (int) HttpStatusCode.BadRequest;\n        }\n    }\n    else\n    {\n        await next();\n    }\n});\n```\n\n\u003c!-- #content --\u003e\n# Installation\n\nThis project can be used either as a regular nuget package:\n\n```\n\u003cPackageReference Include=\"WebSocketChannel\" Version=\"*\" /\u003e\n```\n\nOr alternatively, referenced directly as a source-only dependency using [dotnet-file](https://www.nuget.org/packages/dotnet-file):\n\n```\n\u003e dotnet file add https://github.com/devlooped/WebSocketChannel/blob/main/src/WebSocketChannel/WebSocketChannel.cs\n\u003e dotnet file add https://github.com/devlooped/WebSocketChannel/blob/main/src/WebSocketChannel/WebSocketExtensions.cs\n```\n\nIt's also possible to specify a desired target location for the referenced source files, such as:\n\n```\n\u003e dotnet file add https://github.com/devlooped/WebSocketChannel/blob/main/src/WebSocketChannel/WebSocketChannel.cs src/MyProject/External/.\n\u003e dotnet file add https://github.com/devlooped/WebSocketChannel/blob/main/src/WebSocketChannel/WebSocketExtensions.cs src/MyProject/External/.\n```\n\nWhen referenced as loose source files, it's easy to also get automated PRs when the upstream files change, \nas in the [dotnet-file.yml](https://github.com/devlooped/dotnet-file/blob/main/.github/workflows/dotnet-file.yml) workflow that \nkeeps the repository up to date with a template. See also [dotnet-config](https://dotnetconfig.org), which is used to \nfor the `dotnet-file` configuration settings that tracks all this.\n\n\n\n# Dogfooding\n\n[![CI Version](https://img.shields.io/endpoint?url=https://shields.kzu.io/vpre/WebSocketChannel/main\u0026label=nuget.ci\u0026color=brightgreen)](https://pkg.kzu.io/index.json)\n[![Build](https://github.com/devlooped/WebSocketChannel/workflows/build/badge.svg?branch=main)](https://github.com/devlooped/WebSocketChannel/actions)\n\nWe also produce CI packages from branches and pull requests so you can dogfood builds as quickly as they are produced. \n\nThe CI feed is `https://pkg.kzu.io/index.json`. \n\nThe versioning scheme for packages is:\n\n- PR builds: *42.42.42-pr*`[NUMBER]`\n- Branch builds: *42.42.42-*`[BRANCH]`.`[COMMITS]`\n\n\u003c!-- #sponsors --\u003e\n\u003c!-- include https://github.com/devlooped/sponsors/raw/main/footer.md --\u003e\n# Sponsors \n\n\u003c!-- sponsors.md --\u003e\n[![Clarius Org](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/clarius.png \"Clarius Org\")](https://github.com/clarius)\n[![C. Augusto Proiete](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/augustoproiete.png \"C. Augusto Proiete\")](https://github.com/augustoproiete)\n[![Kirill Osenkov](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/KirillOsenkov.png \"Kirill Osenkov\")](https://github.com/KirillOsenkov)\n[![MFB Technologies, Inc.](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/MFB-Technologies-Inc.png \"MFB Technologies, Inc.\")](https://github.com/MFB-Technologies-Inc)\n[![Stephen Shaw](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/decriptor.png \"Stephen Shaw\")](https://github.com/decriptor)\n[![Torutek](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/torutek-gh.png \"Torutek\")](https://github.com/torutek-gh)\n[![DRIVE.NET, Inc.](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/drivenet.png \"DRIVE.NET, Inc.\")](https://github.com/drivenet)\n[![David Kean](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/davkean.png \"David Kean\")](https://github.com/davkean)\n[![](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/chiluap.png \"\")](https://github.com/chiluap)\n[![Daniel Gnägi](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/dgnaegi.png \"Daniel Gnägi\")](https://github.com/dgnaegi)\n[![Ashley Medway](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/AshleyMedway.png \"Ashley Medway\")](https://github.com/AshleyMedway)\n[![Keith Pickford](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/Keflon.png \"Keith Pickford\")](https://github.com/Keflon)\n[![bitbonk](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/bitbonk.png \"bitbonk\")](https://github.com/bitbonk)\n[![Thomas Bolon](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/tbolon.png \"Thomas Bolon\")](https://github.com/tbolon)\n[![Yurii Rashkovskii](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/yrashk.png \"Yurii Rashkovskii\")](https://github.com/yrashk)\n[![Kori Francis](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/kfrancis.png \"Kori Francis\")](https://github.com/kfrancis)\n[![Zdenek Havlin](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/wdolek.png \"Zdenek Havlin\")](https://github.com/wdolek)\n[![Sean Killeen](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/SeanKilleen.png \"Sean Killeen\")](https://github.com/SeanKilleen)\n[![Toni Wenzel](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/twenzel.png \"Toni Wenzel\")](https://github.com/twenzel)\n[![Giorgi Dalakishvili](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/Giorgi.png \"Giorgi Dalakishvili\")](https://github.com/Giorgi)\n[![Kelly White](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/mckhendry.png \"Kelly White\")](https://github.com/mckhendry)\n[![Allan Ritchie](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/aritchie.png \"Allan Ritchie\")](https://github.com/aritchie)\n[![Mike James](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/MikeCodesDotNET.png \"Mike James\")](https://github.com/MikeCodesDotNET)\n[![Uno Platform](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/unoplatform.png \"Uno Platform\")](https://github.com/unoplatform)\n[![Dan Siegel](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/dansiegel.png \"Dan Siegel\")](https://github.com/dansiegel)\n[![Reuben Swartz](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/rbnswartz.png \"Reuben Swartz\")](https://github.com/rbnswartz)\n[![Jeremy Simmons](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/jeremysimmons.png \"Jeremy Simmons\")](https://github.com/jeremysimmons)\n[![Jacob Foshee](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/jfoshee.png \"Jacob Foshee\")](https://github.com/jfoshee)\n[![](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/Mrxx99.png \"\")](https://github.com/Mrxx99)\n[![Eric Johnson](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/eajhnsn1.png \"Eric Johnson\")](https://github.com/eajhnsn1)\n[![Norman Mackay](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/mackayn.png \"Norman Mackay\")](https://github.com/mackayn)\n[![Certify The Web](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/certifytheweb.png \"Certify The Web\")](https://github.com/certifytheweb)\n[![Taylor Mansfield](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/lavahot.png \"Taylor Mansfield\")](https://github.com/lavahot)\n[![Mårten Rånge](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/mrange.png \"Mårten Rånge\")](https://github.com/mrange)\n[![David Petric](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/davidpetric.png \"David Petric\")](https://github.com/davidpetric)\n[![Rich Lee](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/richlee.png \"Rich Lee\")](https://github.com/richlee)\n[![Danilo Dantas](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/dannevesdantas.png \"Danilo Dantas\")](https://github.com/dannevesdantas)\n[![](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/nietras.png \"\")](https://github.com/nietras)\n[![Gary Woodfine](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/garywoodfine.png \"Gary Woodfine\")](https://github.com/garywoodfine)\n[![](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/kristinnstefansson.png \"\")](https://github.com/kristinnstefansson)\n[![](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/DarrenAtConexus.png \"\")](https://github.com/DarrenAtConexus)\n[![Steve Bilogan](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/kazo0.png \"Steve Bilogan\")](https://github.com/kazo0)\n[![Ix Technologies B.V.](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/IxTechnologies.png \"Ix Technologies B.V.\")](https://github.com/IxTechnologies)\n[![New Relic](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/newrelic.png \"New Relic\")](https://github.com/newrelic)\n[![Chris Johnston‮](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/Chris-Johnston.png \"Chris Johnston‮\")](https://github.com/Chris-Johnston)\n[![David JENNI](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/davidjenni.png \"David JENNI\")](https://github.com/davidjenni)\n[![](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/ehonda.png \"\")](https://github.com/ehonda)\n[![Jonathan ](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/Jonathan-Hickey.png \"Jonathan \")](https://github.com/Jonathan-Hickey)\n[![Oleg Kyrylchuk](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/okyrylchuk.png \"Oleg Kyrylchuk\")](https://github.com/okyrylchuk)\n\n\n\u003c!-- sponsors.md --\u003e\n\n[![Sponsor this project](https://raw.githubusercontent.com/devlooped/sponsors/main/sponsor.png \"Sponsor this project\")](https://github.com/sponsors/devlooped)\n\u0026nbsp;\n\n[Learn more about GitHub Sponsors](https://github.com/sponsors)\n\n\u003c!-- https://github.com/devlooped/sponsors/raw/main/footer.md --\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevlooped%2Fwebsocketchannel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdevlooped%2Fwebsocketchannel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevlooped%2Fwebsocketchannel/lists"}