Windows Universal Platform WebSocket 示例实战:MessageWebSocket 与 StreamWebSocket 完整指南
示例工程【免费下载链接】Windows-universal-samplesAPI samples for the Universal Windows Platform.项目地址https://gitcode.com/gh_mirrors/wi/Windows-universal-samples点击查看免费下载本指南以 Windows-universal-samples 仓库中的 WebSocket 示例 为蓝本系统讲解 UWP 应用中通过Windows.Networking.Sockets命名空间使用 WebSocket 客户端发送与接收数据的完整方案。你将掌握 MessageWebSocket 与 StreamWebSocket 两种连接对象的选型与编程模型、wss:// 安全连接下的服务端证书自定义校验与客户端证书认证、部分消息partial message的读写以及配套 IIS 回显服务器的搭建与清理流程。示例概览两种 WebSocket 客户端对象Windows 10 为 UWP 应用的 WebSocket 客户端使用提供了完整支持核心 API 位于 Windows.Networking.Sockets 命名空间该命名空间为客户端定义了两种 WebSocket 对象MessageWebSocket适用于消息体不太大的典型场景同时支持 UTF-8 文本消息与二进制消息。每条消息作为一个整体在MessageReceived事件中投递适合聊天、命令推送等以“消息”为粒度的交互。StreamWebSocket更适用于照片、电影等大文件传输场景允许每次读操作只读取消息的某个片段而不必一次性读入整条消息仅支持二进制消息。它暴露InputStream/OutputStream可按流式读写适合持续、大批量数据流。本示例的四个场景分别覆盖了这两种对象的典型用法其入口列表定义在 SampleConfiguration.cs场景名称使用的对象Scenario1UTF-8 text messagesMessageWebSocketScenario2Binary data streamStreamWebSocketScenario3Client authenticationStreamWebSocket ClientCertificateScenario4Partial and Complete MessagesMessageWebSocketPartial 接收模式示例默认通过 loopback回环接口访问本机服务器要求本机具备可用的 WebSocket 服务端环境详见下文“服务器搭建”部分。场景一MessageWebSocket 发送与接收 UTF-8 文本消息1. 用户输入 URI 的安全校验由于服务器地址来自不可信来源用户输入示例在连接前统一通过MainPage.TryGetUri()进行校验其实现位于 SampleConfiguration.cs校验规则包括必须是可解析的绝对 URI不允许包含 fragmentURI 片段WebSocket URI 不支持Scheme 必须严格为ws或wss区分大小写的序数比较因为Uri.SchemeName返回规范化后的名称。校验失败时返回null并给出明确错误提示连接流程随即终止。2. 建立连接与消息类型配置核心连接逻辑在 Scenario1_UTF8.xaml.csmessageWebSocket new MessageWebSocket(); messageWebSocket.Control.MessageType SocketMessageType.Utf8; messageWebSocket.MessageReceived MessageReceived; messageWebSocket.Closed OnClosed; await messageWebSocket.ConnectAsync(server);Control.MessageType SocketMessageType.Utf8将消息类型固定为 UTF-8 文本可选值为Utf8与Binary不设置时默认二进制。MessageReceived事件在每条完整消息到达时触发Closed事件既可能由远端服务器触发也可能由本端Close()/Dispose()触发。ConnectAsync失败时如服务器不可达示例会Dispose()释放 socket 并置空引用再通过MainPage.BuildWebSocketError(ex)输出可读的错误信息。3. 发送DataWriter 缓冲与 StoreAsync发送逻辑见 Scenario1_UTF8.xaml.csmessageWriter new DataWriter(messageWebSocket.OutputStream); messageWriter.WriteString(message); await messageWriter.StoreAsync();DataWriter默认编码即为 UTF-8WriteString只是把数据放入缓冲StoreAsync()才真正把缓冲内容作为一条完整消息发出。4. 接收MessageReceived 事件与 DataReaderusing (DataReader reader args.GetDataReader()) { reader.UnicodeEncoding UnicodeEncoding.Utf8; string read reader.ReadString(reader.UnconsumedBufferLength); }事件参数MessageWebSocketMessageReceivedEventArgs.MessageType可判断消息类型GetDataReader()获取数据读取器。示例将读取操作通过Dispatcher.RunAsync派发回 UI 线程后再更新输出文本框避免跨线程访问 UI 控件。5. 关闭连接的正确姿势CloseSocket()Scenario1_UTF8.xaml.cs展示了两个关键细节先messageWriter.DetachStream()再Dispose()若想复用 socket 与新的 DataWriter必须先从输出流上分离否则 DataWriter 析构时会自动关闭该流后续 I/O 将抛出ObjectDisposedException。调用messageWebSocket.Close(1000, Closed due to user request.)1000 是标准 WebSocket 关闭码Normal Closure并携带关闭原因文本。场景二StreamWebSocket 二进制数据流的后台读写StreamWebSocket 不采用“消息事件”模型而是暴露流式接口。连接逻辑与场景一几乎一致仅使用StreamWebSocket并只挂接Closed事件差别集中在连接建立后的读写任务上见 Scenario2_Binary.xaml.cs。1. 持续发送OutputStream.WriteAsyncbyte[] data new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 }; while (true) { if (streamWebSocket ! activeSocket) { /* 停止发送 */ return; } await activeSocket.OutputStream.WriteAsync(data.AsBuffer()); bytesSent data.Length; await Task.Delay(TimeSpan.FromSeconds(1)); // 便于观察 }示例每秒钟向服务器写入 10 字节的二进制块并通过data.AsBuffer()将字节数组转为IBuffer供WriteAsync使用也可改用activeSocket.OutputStream.AsStreamForWrite()走 .NET 流 API。2. 持续接收InputStream.AsStreamForReadStream readStream streamWebSocket.InputStream.AsStreamForRead(); byte[] readBuffer new byte[1000]; while (true) { int read await readStream.ReadAsync(readBuffer, 0, readBuffer.Length); bytesReceived read; }接收侧把InputStream转换为 .NETStream循环读取回显数据并累计字节数。这正是 StreamWebSocket 与 MessageWebSocket 的本质差异每次读操作只取流中的一个片段适合大文件分块传输。3. 错误诊断WebSocketError.GetStatus两个后台任务都在catch中通过WebSocketError.GetStatus(ex.GetBaseException().HResult)将异常映射为WebErrorStatus枚举例如OperationCanceled表示取消如用户主动停止其余错误则输出状态与消息——这与 SampleConfiguration.cs 中BuildWebSocketError的思路一致对CannotConnect、NotFound、RequestTimeout等状态给出“请先运行服务器安装脚本”的提示对0x800C000EINET_E_SECURITY_PROBLEM则提示“被自定义证书校验拒绝”。场景三wss:// 安全连接与客户端证书认证该场景覆盖两个安全主题对服务器证书的默认校验 可选的客户端证书认证均基于 StreamWebSocket 实现见 Scenario3_ClientAuthentication.xaml.cs。1. 忽略可忽略的服务器证书错误仅测试用途连接 localhost 时服务器使用自签名证书主题名为 fabrikam.com示例通过Control.IgnorableServerCertificateErrors忽略两类校验错误streamWebSocket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.Untrusted); streamWebSocket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.InvalidName);源码注释对此有明确警告只有测试应用才应忽略 SSL 错误真实应用中忽略证书错误会引入中间人攻击风险连接虽加密但服务器身份未经验证。且并非所有证书校验错误都可被忽略。2. 客户端证书的获取与安装客户端证书获取分两步Scenario3_ClientAuthentication.xaml.cs先查找用CertificateQuery按IssuerName www.contoso.com与FriendlyName WebSocketSampleClientCert在CertificateStores.FindAllAsync中查询找到即复用该证书由clientCertGenerator.ps1生成见下文。未找到则安装从应用包内ms-appx:///Assets/clientCert.pfx读取 PFX密码1234经 Base64 编码后调用CertificateEnrollmentManager.ImportPfxDataAsync安装到应用证书存储await CertificateEnrollmentManager.ImportPfxDataAsync( clientCertData, ClientCertPassword, ExportOption.Exportable, KeyProtectionLevel.NoConsent, InstallOptions.DeleteExpired, ClientCertFriendlyName);需要留意两点若希望安装到CurrentUser\MY存储应用卸载后证书仍保留需改用UserCertificateEnrollmentManager.ImportPfxDataAsync并要求应用声明sharedUserCertificates能力——Package.appxmanifest 中该能力以注释形式给出。源码明确提示将 PFX 打包进应用包违反 Windows Store 认证要求此处仅为演示客户端证书用法要发布到商店的应用应通过其他途径获取客户端证书。3. 绑定客户端证书并连接streamWebSocket.Control.ClientCertificate await GetClientCertificateAsync(); await streamWebSocket.ConnectAsync(server);即使GetClientCertificateAsync返回null设置ClientCertificate属性也是安全的。服务器端对应页面EchoWebSocketWithClientAuthentication.ashx在 IIS 上被配置为必须协商并校验客户端证书详见下文服务器脚本分析。4. 自定义服务器证书校验ServerCustomValidationRequested场景一、二的SecureWebSocketCheckBox勾选路径展示了自定义校验服务器证书的完整写法Scenario1_UTF8.xaml.csprivate async void OnServerCustomValidationRequested(MessageWebSocket sender, WebSocketServerCustomValidationRequestedEventArgs args) { bool isValid; using (Deferral deferral args.GetDeferral()) { isValid await MainPage.AreCertificateAndCertChainValidAsync( args.ServerCertificate, args.ServerIntermediateCertificates); if (!isValid) { args.Reject(); } } // 回到 UI 线程更新界面输出 }两个要点事件处理器内若调用异步 API必须先取得Deferral延迟并在操作完成后释放示例用using语句保证离开代码块即完成释放。处理器可通过args.Reject()主动拒绝该连接。示例的AreCertificateAndCertChainValidAsyncSampleConfiguration.cs会遍历证书链并逐一校验真实校验逻辑仅为演示检查证书签发者是否为www.fabrikam.com其中包含 100ms 的模拟延迟用于提示“该校验运行在 SSL/TLS 握手期间避免执行耗时操作否则远端服务器可能中断连接”。场景四部分消息Partial Message读写MessageWebSocket 默认只在收到完整消息时触发MessageReceived若想尽早处理大消息的已到达片段需要显式开启部分消息接收模式见 Scenario4_PartialReadWrite.xaml.cs。1. 开启 Partial 接收模式messageWebSocket.Control.ReceiveMode MessageWebSocketReceiveMode.PartialMessage;MessageWebSocketReceiveMode取值FullMessage默认仅在完整消息到达时通知与PartialMessage数据一到即可处理不必等待EndOfMessage。2. 发送最终帧与非最终帧DataWriter messageWriter new DataWriter(); messageWriter.WriteString(message); IBuffer buffer messageWriter.DetachBuffer(); if (EndOfMessageCheckBox.IsChecked true) await messageWebSocket.SendFinalFrameAsync(buffer); // 完整消息最后一段 else await messageWebSocket.SendNonfinalFrameAsync(buffer); // 部分消息后续还有帧SendNonfinalFrameAsync发送一个非结束帧消息尚未结束SendFinalFrameAsync发送结束帧该帧即完整消息或最后一片。示例用复选开关演示二者切换。3. 接收端区分 Partial 与 Completestring partialOrCompleted args.IsMessageComplete ? Complete : Partial;MessageWebSocketMessageReceivedEventArgs.IsMessageComplete指示本次回调是否为完整消息。一个重要的 UTF-8 注意事项部分消息可能在多字节字符中间被截断示例刻意只使用 ASCII 字符规避该问题真实应用中若使用多字节字符需要对被截断的字符做专门的拼接处理。网络能力配置Package.appxmanifest示例默认运行在 loopback 接口上但运行时访问网络仍必须声明能力否则无法收发数据。相关配置在 cs/Package.appxmanifestC/CWinRT 版本位于Samples/WebSocket/cpp/Package.appxmanifest与Samples/WebSocket/cppwinrt/Package.appxmanifestCapabilities Capability NameinternetClientServer / Capability NameprivateNetworkClientServer / !--uap:Capability NamesharedUserCertificates -- /CapabilitiesPrivate Networks (Client Server)privateNetworkClientServer允许在家庭或办公网络本地 intranet上的入站与出站访问。本示例依赖该能力运行于回环/内网环境。Internet (Client)internetClient若修改示例去连接互联网上的服务器更典型的应用形态客户端组件必须声明此能力若同时需要监听入站连接则用示例中的internetClientServer。sharedUserCertificates仅当需要把客户端证书安装到CurrentUser\MY存储非应用专属时启用示例中以注释形式保留。构建与运行构建步骤若通过 ZIP 下载务必解压整个归档而非仅解压目标示例文件夹——示例依赖SharedContent目录中的共享依赖。重要在提升权限的 PowerShell 中进入Samples/WebSocket/shared文件夹执行.\clientCertGenerator.ps1生成应用与服务器均需要的RootCert.cer与clientCert.pfx。用 Visual Studio 打开对应语言子目录下的 .sln 解决方案文件cs/、cpp/、cppwinrt/三个版本可选。按CtrlShiftB或Build Build Solution编译。部署与运行仅部署Build Deploy Solution。部署并调试运行按F5不调试运行CtrlF5。服务器端准备关键前置条件应用尝试建立 WebSocket 连接前必须先启动一个支持 WebSockets、且暴露WebSocketSample路径的 Web 服务器。示例附带两套 PowerShell 脚本位于Samples/WebSocket/server/安装并启动 IIS 服务器——以管理员身份执行其一.\SetupServer.ps1 # 或同时放宽脚本执行策略 PowerShell.exe -ExecutionPolicy Unrestricted -File SetupServer.ps1SetupServer.ps1的主要动作setupserver.ps1校验管理员权限、前置脚本执行状态存在配置残留时要求先运行RemoveServer.ps1缺少证书时提示先运行clientCertGenerator.ps1启用 IIS 相关 Windows 可选功能包括IIS-WebServer、IIS-WebSockets、IIS-ASPNET45等在%systemdrive%\inetpub\wwwroot\WebSocketSample创建应用目录复制website下的页面文件并把echowebsocket.ashx复制一份改名为EchoWebSocketWithClientAuthentication.ashx在 Default Web Site 下创建WebSocketSampleWeb 应用并为客户端认证页面设置sslFlags Ssl,SslNegotiateCert,SslRequireCert必须 SSL 且要求客户端证书生成主题名为www.fabrikam.com的自签名服务器证书并绑定到 443 端口将RootCert.cer导入本机受信任根存储该根证书曾用于签发客户端证书添加 80/443 端口的入站防火墙规则将安装信息功能列表、证书指纹、绑定标记等导出到设置文件供卸载脚本回滚。清理服务器环境——同样以管理员身份执行.\RemoveServer.ps1 # 或 PowerShell.exe -ExecutionPolicy Unrestricted -File RemoveServer.ps1RemoveServer.ps1removeserver.ps1会移除防火墙规则、删除安装期间创建的服务器证书与根证书、还原 443 SSL 绑定、移除 Web 应用与目录并询问是否禁用此前启用的 Windows 功能。连接非 localhost 服务器示例可使用任意 WebSocket 服务器不限于自带脚本使用其他机器上的 IIS把Server文件夹拷到目标机器并执行上述脚本客户端需在应用清单补充能力如服务器在互联网上则加internetClientServer并将 XAML 中ServerAddressField元素的默认值localhost改为目标主机名/IP或在运行界面的Server Address输入框中直接填写。使用非 IIS 服务器将Server/website/EchoWebSocket.ashx复制到服务器的WebSocketSample目录再复制一份并重命名为EchoWebSocketWithClientAuthentication.ashx然后配置服务器接受 WebSocket 连接并强制该页面启用 SSL 与客户端证书要求。ARM 设备与 Windows Phone 限制IIS 在这些平台上不可用应将 Web 服务器部署到独立的 32/64 位机器再按上述非 localhost 方式配置客户端。服务器端回显处理器解析配套的 echowebsocket.ashx 是标准的 ASP.NET HTTP HandlerIHttpHandler通过HttpContext.AcceptWebSocketRequest升级为 WebSocket 连接其行为连接建立后立即发送一条文本通告含服务器时间循环ReceiveAsync接收数据利用EndOfMessage标志拼接多帧消息接收缓冲上限MaxBufferSize 64 * 1024字节收到Close消息时回显关闭码与原因字符串后关闭连接文本消息回显为You said: ...二进制消息回显一条说明文本含字节数——这正是场景一、二“服务器回显”行为在服务端的对应实现。已知问题场景三所演示的客户端证书 API 目前在 XBOX 上不工作见 README.md Known issues 一节。在 XBOX 上运行该示例时客户端证书认证路径不可用。相关资源导航本示例的 JavaScript 历史版本已归档archived/WebSocket可在仓库archived/目录下查看网络能力配置参考cs/Package.appxmanifestC 版见cpp/Package.appxmanifestC/WinRT 版见cppwinrt/Package.appxmanifest客户端证书生成脚本shared/clientCertGenerator.ps1负责创建www.contoso.com根证书导出server/RootCert.cer与WebSocketSampleClientCert客户端证书导出shared/clientCert.pfx密码1234该 PFX 随后被场景三安装到应用证书存储服务器安装/清理脚本server/setupserver.ps1 与 server/removeserver.ps1回显服务器实现server/website/echowebsocket.ashx 与 server/website/partialMessages.ashx后者供场景四部分消息演示使用。系统要求Windows 10示例清单声明的目标设备家族为Windows.UniversalMinVersion10.0.17134.0见 Package.appxmanifest支持 WebSocket 的服务器示例提供 IIS 自动化脚本也支持任意自建服务器构建需 Visual Studio含 UWP 工作负载与 .NET / C 相应工具链。赞分享示例工程【免费下载链接】Windows-universal-samplesAPI samples for the Universal Windows Platform.项目地址https://gitcode.com/gh_mirrors/wi/Windows-universal-samples点击查看免费下载相关推荐OnnxStream源码深度解读核心操作符实现与执行流程OnnxStream源码深度解读核心操作符实现与执行流程 OnnxStream是一款轻量级ONNX推理库采用C编写能够在资源受限设备上高效运行各类ON示例工程Windows Universal 应用示例项目指南Windows Universal 应用示例项目指南 一、项目介绍 该项目由Microsoft维护旨在提供一系列适用于Universal Windows Pl示例工程UWP 中 ListView 与 GridView 的实战指南基于 Windows-universal-samples XamlListView 示例的源码解析UWP 中 ListView 与 GridView 的实战指南基于 Windows universal samples XamlListView 示例的源码解示例工程上一篇Luminus-template 数据库迁移指南用 luminus-migrations 优雅管理 SQL 版本下一篇3步实战深度掌握用户事件模拟库 user-event 的必备高效技巧创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考