介紹
重新想象 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
源碼下載:http://files.cnblogs.com/webabcd/Windows8.rar