重新想象 Windows 8 Store Apps (63) - 通信: WebSocket
重新想象 Windows 8 Store Apps (63) - 通信: WebSocket
作者:webabcd
介绍
重新想象 Windows 8 Store Apps 之 通信
- Socket - 与 WebSocket 服务端做 Text 通信
- Socket - 与 WebSocket 服务端做 Stream(Binary) 通信
示例
WebSocket 的服务端
WebServer/WebSocketServer.ashx.cs
/* * WebSocket 协议的服务端 * * 需要在 iis 启用 WebSocket 协议:控制面板 -> 程序和功能 -> 启用或关闭 Windows 功能 -> 开启 iis 的 WebSocket 协议 */ using System; using System.IO; using System.Net.WebSockets; using System.Text; using System.Threading; using System.Web; namespace WebServer { public class WebSocketServer : IHttpHandler { // 接收数据的缓冲区的最大大小 private int _maxBufferSize = 64 * 1024; public void ProcessRequest(HttpContext context) { try { // HttpContext.AcceptWebSocketRequest() - 接受一个 WebSocket 请求 context.AcceptWebSocketRequest(async wsContext => // AspNetWebSocketContext { try { byte[] receiveBuffer = new byte[_maxBufferSize]; ArraySegment<byte> buffer = new ArraySegment<byte>(receiveBuffer); // AspNetWebSocketContext.WebSocket - 获取当前上下文的 WebSocket 对象 WebSocket socket = wsContext.WebSocket; // HTTP 握手完成 if (socket.State == WebSocketState.Open) { var outputString = "WebSocket Connected: " + DateTime.Now.ToString("mm:ss"); ArraySegment<byte> outputBuffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(outputString)); // WebSocket.SendAsync() - 发送数据 // WebSocketMessageType.Text - 发送的是文本数据 // WebSocketMessageType.Binary - 发送的是二进制数据 await socket.SendAsync(outputBuffer, WebSocketMessageType.Text, true, CancellationToken.None); } // HTTP 握手完成 while (socket.State == WebSocketState.Open) { // WebSocket.ReceiveAsync() - 接收数据,返回 WebSocketReceiveResult 对象 WebSocketReceiveResult receiveResult = await socket.ReceiveAsync(buffer, CancellationToken.None); // WebSocketReceiveResult.MessageType - 接收到的数据的类型(WebSocketMessageType 枚举) // WebSocketMessageType.Text - 收到的是文本数据 // WebSocketMessageType.Binary - 收到的是二进制数据 // WebSocketMessageType.Close - 收到的是来自客户端的 WebSocket 关闭的消息 if (receiveResult.MessageType == WebSocketMessageType.Close) { // WebSocket.CloseAsync() - 关闭 WebSocket await socket.CloseAsync( receiveResult.CloseStatus.GetValueOrDefault(), receiveResult.CloseStatusDescription, CancellationToken.None); return; } int offset = receiveResult.Count; // WebSocketReceiveResult.EndOfMessage - 消息是否被完全接收 while (receiveResult.EndOfMessage == false) { // WebSocket.ReceiveAsync() - 接收数据,返回 WebSocketReceiveResult 对象 receiveResult = await socket.ReceiveAsync(new ArraySegment<byte>(receiveBuffer, offset, _maxBufferSize - offset), CancellationToken.None); offset += receiveResult.Count; } // 收到文本数据 if (receiveResult.MessageType == WebSocketMessageType.Text) { string receivedText = Encoding.UTF8.GetString(receiveBuffer, 0, offset); string sendText = "server to client: \"" + receivedText + "\""; // 发送文本数据到客户端 ArraySegment<byte> outputBuffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(sendText)); await socket.SendAsync(outputBuffer, WebSocketMessageType.Text, true, CancellationToken.None); } // 收到二进制数据 else if (receiveResult.MessageType == WebSocketMessageType.Binary) { string sendText = "server to client: binary message received, size: " + receiveResult.Count + " bytes"; // 发送文本数据到客户端 ArraySegment<byte> outputBuffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(sendText)); await socket.SendAsync(outputBuffer, WebSocketMessageType.Text, true, CancellationToken.None); } } } catch (Exception ex) { } }); } catch (Exception ex) { } } public bool IsReusable { get { return false; } } } }
1、演示如何通过 MessageWebSocket 与 WebSocket 服务端做 Text 通信
Communication/Socket/MessageWebSocketDemo.xaml
<Page x:Class="XamlDemo.Communication.Socket.MessageWebSocketDemo" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlDemo.Communication.Socket" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="Transparent"> <StackPanel Margin="120 0 0 0" Orientation="Horizontal"> <StackPanel> <Button Name="btnTextDemo" Content="与 WebSocket 服务端做 Text 通信" Click="btnTextDemo_Click" /> <Button Name="btnClose" Content="Close" Click="btnClose_Click" Margin="0 10 0 0" /> </StackPanel> <TextBlock Name="lblMsg" FontSize="14.667" TextWrapping="Wrap" Margin="20 0 0 0" /> </StackPanel> </Grid> </Page>
Communication/Socket/MessageWebSocketDemo.xaml.cs
/* * 演示如何通过 MessageWebSocket 与 WebSocket 服务端做 Text 通信 * * 注:需要在 Package.appxmanifest 中增加配置 <Capability Name="privateNetworkClientServer" /> 和 <Capability Name="internetClient" /> */ using System; using Windows.Networking.Sockets; using Windows.Storage.Streams; using Windows.UI.Core; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.Web; namespace XamlDemo.Communication.Socket { public sealed partial class MessageWebSocketDemo : Page { // WebSocket 协议的服务端地址(服务端代码参见:WebServer/WebSocketServer.cs) private Uri _serverUri = new Uri("ws://localhost:86/WebSocketServer.ashx"); // MessageWebSocket - 用于与 WebSocket 服务端做 Message 通信(可以是 utf8 或 binary) private MessageWebSocket _socket; // 用于发送数据 DataWriter _dataWriter; public MessageWebSocketDemo() { this.InitializeComponent(); } private async void btnTextDemo_Click(object sender, RoutedEventArgs e) { try { if (_socket == null) { lblMsg.Text += "connecting to: " + _serverUri.ToString(); lblMsg.Text += Environment.NewLine; _socket = new MessageWebSocket(); // 发送的消息的类型 Utf8 或 Binary _socket.Control.MessageType = SocketMessageType.Utf8; // 接收到消息时所触发的事件 _socket.MessageReceived += _socket_MessageReceived; // WebSocket 关闭时所触发的事件 _socket.Closed += async (senderSocket, args) => { await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { // WebSocketClosedEventArgs.Code - 关闭原因的状态吗 // WebSocketClosedEventArgs.Reason - 关闭原因的详细信息 lblMsg.Text += "socket closed - code: " + args.Code + ", reason: " + args.Reason; lblMsg.Text += Environment.NewLine; if (_socket != null) { _socket.Dispose(); _socket = null; } }); }; // 连接指定的 WebSocket 服务 await _socket.ConnectAsync(_serverUri); // 根据 MessageWebSocket 的 OutputStream,实例化一个 DataWriter,以便发数据到服务端 _dataWriter = new DataWriter(_socket.OutputStream); lblMsg.Text += "connected"; lblMsg.Text += Environment.NewLine; } string message = "hello " + DateTime.Now.ToString("hh:mm:ss"); lblMsg.Text += "send: " + message; lblMsg.Text += Environment.NewLine; // 发送数据到服务端 _dataWriter.WriteString(message); await _dataWriter.StoreAsync(); lblMsg.Text += "sent"; lblMsg.Text += Environment.NewLine; } catch (Exception ex) { if (_socket != null) { _socket.Dispose(); _socket = null; } WebErrorStatus errStatus = WebSocketError.GetStatus(ex.GetBaseException().HResult); lblMsg.Text += "errStatus: " + errStatus.ToString(); lblMsg.Text += Environment.NewLine; lblMsg.Text += ex.ToString(); lblMsg.Text += Environment.NewLine; } } void _socket_MessageReceived(MessageWebSocket sender, MessageWebSocketMessageReceivedEventArgs args) { try { // MessageWebSocketMessageReceivedEventArgs.MessageType - 收到的数据的类型 Utf8 或 Binary // MessageWebSocketMessageReceivedEventArgs.GetDataReader() - 获取收到的数据的 DataReader 对象,用于读取接收到的数据 using (DataReader reader = args.GetDataReader()) { reader.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8; string read = reader.ReadString(reader.UnconsumedBufferLength); var ignore = this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { lblMsg.Text += "received: " + read; lblMsg.Text += Environment.NewLine; }); } } catch (Exception ex) { WebErrorStatus errStatus = WebSocketError.GetStatus(ex.GetBaseException().HResult); lblMsg.Text += "errStatus: " + errStatus.ToString(); lblMsg.Text += Environment.NewLine; lblMsg.Text += ex.ToString(); lblMsg.Text += Environment.NewLine; } } private void btnClose_Click(object sender, RoutedEventArgs e) { try { if (_socket != null) { lblMsg.Text += "socket closing"; lblMsg.Text += Environment.NewLine; // 关闭 WebSocket,可以指定 code 和 reason(此处指定的 code 和 reason 可以在 MessageWebSocket.Closed 事件中获取) _socket.Close(8888, "用户在客户端关闭了 WebSocket"); _socket = null; } } catch (Exception ex) { WebErrorStatus errStatus = WebSocketError.GetStatus(ex.GetBaseException().HResult); lblMsg.Text += "errStatus: " + errStatus.ToString(); lblMsg.Text += Environment.NewLine; lblMsg.Text += ex.ToString(); lblMsg.Text += Environment.NewLine; } } } }
2、演示如何通过 StreamWebSocket 与 WebSocket 服务端做 Stream(Binary) 通信
Communication/Socket/StreamWebSocketDemo.xaml
<Page x:Class="XamlDemo.Communication.Socket.StreamWebSocketDemo" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlDemo.Communication.Socket" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="Transparent"> <StackPanel Margin="120 0 0 0" Orientation="Horizontal"> <StackPanel> <Button Name="btnBinaryDemo" Content="与 WebSocket 服务端做 Binary 通信" Click="btnBinaryDemo_Click" /> <Button Name="btnClose" Content="Close" Click="btnClose_Click" Margin="0 10 0 0" /> </StackPanel> <TextBlock Name="lblMsg" FontSize="14.667" TextWrapping="Wrap" Margin="20 0 0 0" /> </StackPanel> </Grid> </Page>
Communication/Socket/StreamWebSocketDemo.xaml.cs
/* * 演示如何通过 StreamWebSocket 与 WebSocket 服务端做 Stream(Binary) 通信 * * 注:需要在 Package.appxmanifest 中增加配置 <Capability Name="privateNetworkClientServer" /> 和 <Capability Name="internetClient" /> */ using System; using Windows.Networking.Sockets; using Windows.Storage.Streams; using Windows.UI.Core; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.Web; using System.Runtime.InteropServices.WindowsRuntime; using System.Threading.Tasks; using System.IO; namespace XamlDemo.Communication.Socket { public sealed partial class StreamWebSocketDemo : Page { // WebSocket 协议的服务端地址(服务端代码参见:WebServer/WebSocketServer.cs) private Uri _serverUri = new Uri("ws://localhost:86/WebSocketServer.ashx"); // StreamWebSocket - 用于与 WebSocket 服务端做 Stream 通信(只能是 binary) private StreamWebSocket _socket; public StreamWebSocketDemo() { this.InitializeComponent(); } private async void btnBinaryDemo_Click(object sender, RoutedEventArgs e) { try { if (_socket == null) { lblMsg.Text += "connecting to: " + _serverUri.ToString(); lblMsg.Text += Environment.NewLine; _socket = new StreamWebSocket(); // WebSocket 关闭时所触发的事件 _socket.Closed += async (senderSocket, args) => { await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { // WebSocketClosedEventArgs.Code - 关闭原因的状态吗 // WebSocketClosedEventArgs.Reason - 关闭原因的详细信息 lblMsg.Text += "socket closed - code: " + args.Code + ", reason: " + args.Reason; lblMsg.Text += Environment.NewLine; if (_socket != null) { _socket.Dispose(); _socket = null; } }); }; // 连接指定的 WebSocket 服务 await _socket.ConnectAsync(_serverUri); // 新开线程,用于接收数据 Task receiving = Task.Factory.StartNew(ReceiveData, _socket.InputStream.AsStreamForRead(), TaskCreationOptions.LongRunning); // 新开线程,用于发送数据 Task sending = Task.Factory.StartNew(SendData, _socket.OutputStream, TaskCreationOptions.LongRunning); lblMsg.Text += "connected"; lblMsg.Text += Environment.NewLine; } } catch (Exception ex) { if (_socket != null) { _socket.Dispose(); _socket = null; } WebErrorStatus errStatus = WebSocketError.GetStatus(ex.GetBaseException().HResult); lblMsg.Text += "errStatus: " + errStatus.ToString(); lblMsg.Text += Environment.NewLine; lblMsg.Text += ex.ToString(); lblMsg.Text += Environment.NewLine; } } // 发送数据 private async void SendData(object state) { // 自定义需要发送的二进制数据 byte[] data = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 }; try { IOutputStream writeStream = (IOutputStream)state; while (true) { var ignore = this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { lblMsg.Text += "send: " + data.Length.ToString() + " 字节的数据"; lblMsg.Text += Environment.NewLine; }); // 发送 stream 数据 await writeStream.WriteAsync(data.AsBuffer()); var ignore2 = this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { lblMsg.Text += "sent"; lblMsg.Text += Environment.NewLine; }); await Task.Delay(TimeSpan.FromSeconds(3)); } } catch (ObjectDisposedException) { var ignore = this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { lblMsg.Text += "用于发送数据的后台线程已经停止"; lblMsg.Text += Environment.NewLine; }); } catch (Exception ex) { WebErrorStatus errStatus = WebSocketError.GetStatus(ex.GetBaseException().HResult); var ignore = this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { lblMsg.Text += "errStatus: " + errStatus.ToString(); lblMsg.Text += Environment.NewLine; lblMsg.Text += ex.ToString(); lblMsg.Text += Environment.NewLine; }); } } // 接收数据 private async void ReceiveData(object state) { // 用于接收数据的缓冲区 byte[] readBuffer = new byte[1024]; int bytesReceived = 0; try { Stream readStream = (Stream)state; while (true) { // 接收数据 int read = await readStream.ReadAsync(readBuffer, 0, readBuffer.Length); bytesReceived += read; await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { lblMsg.Text += "received: " + System.Text.Encoding.UTF8.GetString(readBuffer, 0, read); lblMsg.Text += Environment.NewLine; lblMsg.Text += "累计已收到 " + bytesReceived.ToString() + " 字节的数据"; lblMsg.Text += Environment.NewLine; }); } } catch (ObjectDisposedException) { var ignore = this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { lblMsg.Text += "用于接收数据的后台线程已经停止"; lblMsg.Text += Environment.NewLine; }); } catch (Exception ex) { WebErrorStatus errStatus = WebSocketError.GetStatus(ex.GetBaseException().HResult); var ignore = this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { lblMsg.Text += "errStatus: " + errStatus.ToString(); lblMsg.Text += Environment.NewLine; lblMsg.Text += ex.ToString(); lblMsg.Text += Environment.NewLine; }); } } private void btnClose_Click(object sender, RoutedEventArgs e) { try { if (_socket != null) { lblMsg.Text += "socket closing"; lblMsg.Text += Environment.NewLine; // 关闭 WebSocket,可以指定 code 和 reason(此处指定的 code 和 reason 可以在 MessageWebSocket.Closed 事件中获取) _socket.Close(8888, "用户在客户端关闭了 WebSocket"); _socket = null; } } catch (Exception ex) { WebErrorStatus errStatus = WebSocketError.GetStatus(ex.GetBaseException().HResult); lblMsg.Text += "errStatus: " + errStatus.ToString(); lblMsg.Text += Environment.NewLine; lblMsg.Text += ex.ToString(); lblMsg.Text += Environment.NewLine; } } } }
OK
[源码下载]