29、WebSocket
本篇讲述通过 MessageWebSocket 通信,服务器端是 aspx 的网站。MessageWebSocket 和 Socket 大同小异。
附上位于 Windows.Networking.Sockets 命名空间下的 MessageWebSocket 类的定义 :
// 摘要: // 支持允许使用 WebSocket 的读取和写入整个消息的网络通信。 [Activatable(100794368)] [DualApiPartition(version = 100794368)] [MarshalingBehavior(MarshalingType.Agile)] [Threading(ThreadingModel.Both)] [Version(100794368)] public sealed class MessageWebSocket : IWebSocket, IDisposable { // 摘要: // 创建新的 MessageWebSocket 对象。 public MessageWebSocket(); // 摘要: // 获取 MessageWebSocket 对象上的套接字控件数据。 // // 返回结果: // 某一 MessageWebSocket 对象上的套接字控件数据。 public MessageWebSocketControl Control { get; } // // 摘要: // 获取 MessageWebSocket 对象上的套接字信息。 // // 返回结果: // 该 MessageWebSocket 对象的套接字信息。 public MessageWebSocketInformation Information { get; } // // 摘要: // 获取 MessageWebSocket 对象上写入远程网络目标的输出流。 // // 返回结果: // 要作为单一消息写入远程目标的有序字节流。 public IOutputStream OutputStream { get; } // 摘要: // 当 MessageWebSocket 对象作为关闭握手的一部分收到结束帧时发生。 public event TypedEventHandler<IWebSocket, WebSocketClosedEventArgs> Closed; // // 摘要: // 指示在 MessageWebSocket 对象上收到消息的事件。 public event TypedEventHandler<MessageWebSocket, MessageWebSocketMessageReceivedEventArgs> MessageReceived; // 摘要: // 关闭 MessageWebSocket 对象并指示关闭的原因。 // // 参数: // code: // 指示关闭原因的状态代码。 // // reason: // 包含有关关闭的其他信息的可选 UTF-8 编码数据。 [Overload("CloseWithStatus")] public void Close(ushort code, string reason); public IAsyncAction ConnectAsync(Uri uri); public void Dispose(); // // 摘要: // 向由 MessageWebSocket 对象握手的 WebSocket 协议中使用的 HTTP 请求消息添加 HTTP 请求标头。 // // 参数: // headerName: // 请求标头的名称。 // // headerValue: // 请求标头的值。 public void SetRequestHeader(string headerName, string headerValue); }
位于 namespace Windows.Networking.Sockets 命名空间下的 StreamWebSocket 类的定义:
// 摘要: // 支持允许使用 WebSocket 的读取和写入流的网络通信。 [Activatable(100794368)] [DualApiPartition(version = 100794368)] [MarshalingBehavior(MarshalingType.Agile)] [Threading(ThreadingModel.Both)] [Version(100794368)] public sealed class StreamWebSocket : IWebSocket, IDisposable { // 摘要: // 创建新的 StreamWebSocket 对象。 public StreamWebSocket(); // 摘要: // 获取 StreamWebSocket 对象上的套接字控件数据。 // // 返回结果: // 某一 StreamWebSocket 对象上的套接字控件数据。 public StreamWebSocketControl Control { get; } // // 摘要: // 获取 StreamWebSocket 对象上的套接字信息。 // // 返回结果: // 该 StreamWebSocket 对象的套接字信息。 public StreamWebSocketInformation Information { get; } // // 摘要: // 获取要从 StreamWebSocket 对象上的远程目标读取的输入流。 // // 返回结果: // 要从远程目标读取的有序字节流。 public IInputStream InputStream { get; } // // 摘要: // 获取 StreamWebSocket 对象上写入远程网络目标的输出流。 // // 返回结果: // 要写入远程目标的有序字节流。 public IOutputStream OutputStream { get; } // 摘要: // 当 StreamWebSocket 对象作为关闭握手的一部分收到结束帧时发生。 public event TypedEventHandler<IWebSocket, WebSocketClosedEventArgs> Closed; // 摘要: // 关闭 StreamWebSocket 对象并指示关闭的原因。 // // 参数: // code: // 指示关闭原因的状态代码。 // // reason: // 包含有关关闭的其他信息的可选 UTF-8 编码数据。 [Overload("CloseWithStatus")] public void Close(ushort code, string reason); public IAsyncAction ConnectAsync(Uri uri); public void Dispose(); // // 摘要: // 向由 StreamWebSocket 对象握手的 WebSocket 协议中使用的 HTTP 请求消息添加 HTTP 请求标头。 // // 参数: // headerName: // 请求标头的名称。 // // headerValue: // 请求标头的值。 public void SetRequestHeader(string headerName, string headerValue); }
位于 System.Net.WebSockets 命名空间下的 WebSocket 类 :
// 摘要: // The WebSocket class allows applications to send and receive data after the // WebSocket upgrade has completed. public abstract class WebSocket : IDisposable { // 摘要: // 创建 System.Net.WebSockets.WebSocket 类的实例。 protected WebSocket(); // 摘要: // Indicates the reason why the remote endpoint initiated the close handshake. // // 返回结果: // 返回 System.Net.WebSockets.WebSocketCloseStatus。 public abstract WebSocketCloseStatus? CloseStatus { get; } // // 摘要: // Allows the remote endpoint to describe the reason why the connection was // closed. // // 返回结果: // 返回 System.String。 public abstract string CloseStatusDescription { get; } public static TimeSpan DefaultKeepAliveInterval { get; } // // 摘要: // Returns the current state of the WebSocket connection. // // 返回结果: // 返回 System.Net.WebSockets.WebSocketState。 public abstract WebSocketState State { get; } // // 摘要: // The subprotocol that was negotiated during the opening handshake. // // 返回结果: // 返回 System.String。 public abstract string SubProtocol { get; } // 摘要: // Aborts the WebSocket connection and cancels any pending IO operations. public abstract void Abort(); // // 摘要: // Closes the WebSocket connection using the close handshake defined in the // WebSocket protocol specification section 7. // // 参数: // closeStatus: // Indicates the reason for closing the WebSocket connection. // // statusDescription: // Specifies a human readable explanation as to why the connection is closed. // // cancellationToken: // The token that can be used to propagate notification that operations should // be canceled. // // 返回结果: // 返回 System.Threading.Tasks.Task。 public abstract Task CloseAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken); // // 摘要: // Initiates or completes the close handshake defined in the WebSocket protocol // specification section 7. // // 参数: // closeStatus: // Indicates the reason for closing the WebSocket connection. // // statusDescription: // Allows applications to specify a human readable explanation as to why the // connection is closed. // // cancellationToken: // The token that can be used to propagate notification that operations should // be canceled. // // 返回结果: // 返回 System.Threading.Tasks.Task。 public abstract Task CloseOutputAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken); public static ArraySegment<byte> CreateClientBuffer(int receiveBufferSize, int sendBufferSize); [EditorBrowsable(EditorBrowsableState.Never)] public static WebSocket CreateClientWebSocket(Stream innerStream, string subProtocol , int receiveBufferSize, int sendBufferSize, TimeSpan keepAliveInterval , bool useZeroMaskingKey, ArraySegment<byte> internalBuffer); public static ArraySegment<byte> CreateServerBuffer(int receiveBufferSize); // // 摘要: // Used to clean up unmanaged resources for ASP.NET and self-hosted implementations. public abstract void Dispose(); // // 摘要: // Returns true if the state of the WebSocket instance is Closed or Aborted. // // 参数: // state: // The current state of the WebSocket. // // 返回结果: // 返回 System.Boolean。 protected static bool IsStateTerminal(WebSocketState state); // // 摘要: // Receives data from the WebSocket connection asynchronously. // // 参数: // buffer: // References the application buffer that is the storage location for the received // data. // // cancellationToken: // Propagate the notification that operations should be canceled. // // 返回结果: // 返回 System.Threading.Tasks.Task<TResult>。 public abstract Task<WebSocketReceiveResult> ReceiveAsync(ArraySegment<byte> buffer, CancellationToken cancellationToken); // // 摘要: // 此 API 支持 .NET Framework 基础结构,但不应在代码中直接使用。Allows callers to register prefixes // for WebSocket requests (ws and wss). [EditorBrowsable(EditorBrowsableState.Never)] public static void RegisterPrefixes(); // // 摘要: // Sends data over the WebSocket connection asynchronously. // // 参数: // buffer: // The buffer to be sent over the connection. // // messageType: // Indicates whether the application is sending a binary or text message. // // endOfMessage: // Indicates whether the data in “buffer†is the last part of a message. // // cancellationToken: // The token that propagates the notification that operations should be canceled. // // 返回结果: // 返回 System.Threading.Tasks.Task。 public abstract Task SendAsync(ArraySegment<byte> buffer, WebSocketMessageType messageType, bool endOfMessage , CancellationToken cancellationToken); // // 摘要: // Verifies that the connection is in an expected state. // // 参数: // state: // The current state of the WebSocket to be tested against the list of valid // states. // // validStates: // List of valid connection states. protected static void ThrowOnInvalidState(WebSocketState state, params WebSocketState[] validStates); }
在下面的 1、和 2、 中的响应客户端的请求的 EchoWebSocket.ashx 文件 :
using System; using System.Web; using System.Net.WebSockets; using System.Web.WebSockets; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; public class EchoWebSocket : IHttpHandler { private const int MaxBufferSize = 64 * 1024; public void ProcessRequest (HttpContext context) { try { //AcceptWebSocketRequest(Func<AspNetWebSocketContext, System.Threading.Tasks.Task> userFunc); 方法 //用一个指定的方法,接收一个 System.Web.WebSockets.AspNetWebSocket 请求。这个 //请求不是一个 System.Web.WebSockets.AspNetWebSocket 请求。 context.AcceptWebSocketRequest(async wsContext => { try { byte[] receiveBuffer = new byte[MaxBufferSize]; // 初始化 System.ArraySegment<T> 结构的新实例,该结构用于分隔指定数组中的所有元素。 ArraySegment<byte> buffer = new ArraySegment<byte>(receiveBuffer); //返回 : 当前的 System.Web.WebSockets.AspNetWebSocket 实例。 WebSocket socket = wsContext.WebSocket; string userString; if (socket.State == WebSocketState.Open) { // 链接建立起来后通知一下 var announceString = "EchoWebSocket Connected at: " + DateTime.Now.ToString(); //ArrarSegment<T> : 分隔一维数组的一部分。 ArraySegment<byte> outputBuffer2 = new ArraySegment<byte>(Encoding.UTF8.GetBytes(announceString)); //通过 WebSocket 连接异步发送数据。 await socket.SendAsync(outputBuffer2, WebSocketMessageType.Text, true, CancellationToken.None); } // 当 WebSocket处于打开状态时, 轮询客户端发送来的数据 while (socket.State == WebSocketState.Open) { //从 WebSocket 链接中异步接收数据 WebSocketReceiveResult receiveResult = await socket.ReceiveAsync(buffer, CancellationToken.None); if (receiveResult.MessageType == WebSocketMessageType.Close) { // public abstract Task CloseAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken
// cancellationToken) : 关闭 WebSocket 连接,使用关闭握手中定义的 WebSocket协议 await socket.CloseAsync( receiveResult.CloseStatus.GetValueOrDefault(), receiveResult.CloseStatusDescription, CancellationToken.None); return; } //显示 WebSocket 收到的字节数。 int offset = receiveResult.Count; while (receiveResult.EndOfMessage == false) { //在异步 WebSocket 连接中接收的数据。 receiveResult = await socket.ReceiveAsync(new ArraySegment<byte>(receiveBuffer,
offset, MaxBufferSize - offset), CancellationToken.None); offset += receiveResult.Count; } //判断当前收到的消息是一个 utf - 8 消息 还是一个 二进制的消息。 if (receiveResult.MessageType == WebSocketMessageType.Text) { string cmdString = Encoding.UTF8.GetString(receiveBuffer, 0, offset); //将指定字节数组中的一个字节序列解码为一个字符串。 userString = cmdString; userString = "You said: \"" + userString + "\""; ArraySegment<byte> outputBuffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(userString)); //通过 WebSocket 对象 异步发送数据 await socket.SendAsync(outputBuffer, WebSocketMessageType.Text, true, CancellationToken.None); } else if (receiveResult.MessageType == WebSocketMessageType.Binary) { userString = String.Format("binary message received, size={0} bytes", receiveResult.Count); ArraySegment<byte> outputBuffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(userString)); await socket.SendAsync(outputBuffer, WebSocketMessageType.Text, true, CancellationToken.None); } } } catch (Exception ex) { //System.Diagnostics.Trace.WriteLine(ex); } }); } catch (Exception ex) { //System.Diagnostics.Trace.WriteLine(ex); context.Response.StatusCode = 500; context.Response.StatusDescription = ex.Message; context.Response.End(); } } public bool IsReusable { get { return false; } } }
页面中会用到的工具方法 , 判断并创建合法的 Uri:
public bool TryGetUri(string uriString, out Uri uri) { uri = null; Uri webSocketUri; if (!Uri.TryCreate(uriString.Trim(), UriKind.Absolute, out webSocketUri)) { // Error: Invalid URI return false; } // Fragments are not allowed in WebSocket URIs. if (!String.IsNullOrEmpty(webSocketUri.Fragment)) { // Error: URI fragments not supported in WebSocket URIs return false; } // Uri.SchemeName returns the canonicalized scheme name so we can use case-sensitive, ordinal string // comparison.
//获取此 URI 的方案名称。 if ((webSocketUri.Scheme != "ws") && (webSocketUri.Scheme != "wss")) { // Error: WebSockets only support ws:// and wss:// schemes return false; } uri = webSocketUri; return true; }
1、UTF-8 text messages :
操作截图 :
在第一个 TextBox 中写上固定的本地服务地址和端口号, 指向服务器端的响应一般处理程序 (EchoWebSocket.ashx),在服务器端使用 WebSocket
类的对象进行响应客户端的请求。
点击 'Start' 按钮, 和服务器端建立连接 (服务器端已经运行),并且发送第二个 TextBox 中的文本, 服务器端响应 :
页面的 xaml :
//服务器的地址 <TextBox Name="ServerAddressField" IsEnabled="False" Text="ws://localhost:10000/EchoWebSocket.ashx" /> //向服务器端发送的文本字符串 <TextBox Name="InputField" Text="Hello Windows 8~" />
//响应服务器返回信息 <TextBox Name="OutputField" IsReadOnly="True" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollMode="Auto" />
相应的 C# :
首先声明两个变量 :
//用来与服务端通信 private MessageWebSocket messageWebSocket; //执行向 MessageWebSocket 对象的输出流写入操作 private DataWriter messageWriter;
在 'Start' 按钮的单击事件中:
private async void Start_Click(object sender, RoutedEventArgs e) { bool connecting = true; try { // 如果 messageWebSocket 为空时,创建 if (messageWebSocket == null) { // 需要在工程文件清单中选择功能选项卡,勾选 Internet(客户端) 或者 // Internet(客户端和服务器)选项。 //尝试创建合法的 Uri 对象 Uri server; if (!TryGetUri(ServerAddressField.Text, out server)) { return; } // 支持允许使用 WebSocket 的读取和写入整个消息的网络通信。 messageWebSocket = new MessageWebSocket();
//将在 MessageWebSocket 对象上配置的 WebSocket 消息类型。 messageWebSocket.Control.MessageType = SocketMessageType.Utf8;
//指示在 MessageWebSocket 对象上收到消息的事件。 messageWebSocket.MessageReceived += MessageReceived; //在 UI 线程调度关闭事件。这让我们可以避免同步访问 messageWebSocket 。 //当 MessageWebSocket 对象作为关闭握手的一部分收到结束帧时发生。 messageWebSocket.Closed += async (senderSocket, args) => { //异步运行事件调度程序并返回调度事件的结果。 await Window.Current.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => Closed(senderSocket, args)); }; await messageWebSocket.ConnectAsync(server); //创建并初始化指向 messageWebSocket 对象输出流的数据编写器的新实例。 messageWriter = new DataWriter(messageWebSocket.OutputStream); // Connected } else { // Already connected } connecting = false; string message = InputField.Text; OutputField.Text += "Sending Message:\r\n" + message + "\r\n"; //缓冲任何我们想要发送的数据。 将字符串值写入输出流。 messageWriter.WriteString(message); // 异步存储数据操作。作为一个完整的消息将数据发送出去。 await messageWriter.StoreAsync(); // Send Complete } catch (Exception ex) { // 在连接操作是发生的错误 if (connecting && messageWebSocket != null) { messageWebSocket.Dispose(); messageWebSocket = null; } // 基于 WebSocket 操作遇到的错误,获取 WebErrorStatus 值。 WebErrorStatus status = WebSocketError.GetStatus(ex.GetBaseException().HResult); switch (status) { case WebErrorStatus.CannotConnect: case WebErrorStatus.NotFound: case WebErrorStatus.RequestTimeout: // Cannot connect to the server. Please make sure to run the server setup script before running the sample break; case WebErrorStatus.Unknown: throw; default: // Error break; } OutputField.Text += ex.Message + "\r\n"; } }
当收到服务器端的消息时触发 :
private void MessageReceived(MessageWebSocket sender, MessageWebSocketMessageReceivedEventArgs args) { try { MarshalText(OutputField, "Message Received; Type: " + args.MessageType + "\r\n"); // 获取 DataReader 对象,以读取MessageWebSocket 上的远程网络目标接收到的传入数据。 using (DataReader reader = args.GetDataReader()) { //获取或设置用于输入流的 Unicode 字符编码。 reader.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8; string read = reader.ReadString(reader.UnconsumedBufferLength); MarshalText(OutputField, read + "\r\n"); } } catch (Exception ex) // For debugging { //基于 WebSocket 操作遇到的错误,获取 WebErrorStatus 值。 WebErrorStatus status = WebSocketError.GetStatus(ex.GetBaseException().HResult); if (status == WebErrorStatus.Unknown) { throw; } MarshalText(OutputField, "Error: " + status + "\r\n"); MarshalText(OutputField, ex.Message + "\r\n"); } }
// 可能由服务器端触发,也可能本地的 Close/Dispose() 方法触发
private void Closed(IWebSocket sender, WebSocketClosedEventArgs args) { MarshalText(OutputField, "Closed; Code: " + args.Code + ", Reason: " + args.Reason + "\r\n"); if (messageWebSocket != null) { messageWebSocket.Dispose(); messageWebSocket = null; } } private void MarshalText(TextBox output, string value) { MarshalText(output, value, true); } // 后台操作对 UI 线程中的元素进行更改时,需要返回到 UI 线程执行 private void MarshalText(TextBox output, string value, bool append) { //异步运行事件调度程序并返回调度事件的结果。 var ignore = output.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { if (append) { output.Text += value; } else { output.Text = value; } }); }
单击 'Close' 按钮时 :
private void Close_Click(object sender, RoutedEventArgs e) { try { if (messageWebSocket != null) { //public void Close(ushort code, string reason) : 关闭 MessageWebSocket //对象并指示关闭的原因。 (code: 指示关闭原因的状态代码。) messageWebSocket.Close(1000, "Closed due to user request."); messageWebSocket = null; } else { // No active WebSocket, send something first } } catch (Exception ex) { WebErrorStatus status = WebSocketError.GetStatus(ex.GetBaseException().HResult); if (status == WebErrorStatus.Unknown) { throw; } OutputField.Text += ex.Message + "\r\n"; } }
2、Binary data stream :
本例介绍如何使用 StreamWebSocket 发送二进制数据。
操作截图 :
点击 'Start' 按钮 :
页面的 xaml :
//服务器端地址 <TextBox Name="ServerAddressField" IsEnabled="False" Text="ws://localhost:10000/EchoWebSocket.ashx" /> <Button Content="Start" Click="Start_Click"/> <Button Content="Stop" Click="Stop_Click"/>
<TextBlock Text="Data Sent:"/> <TextBox Name="DataSentField" IsReadOnly="True" /> <TextBlock Text="Data Received:" /> <TextBox Name="DataReceivedField" />
相应的 C# 方法 :
private StreamWebSocket streamWebSocket; private byte[] readBuffer; private async void Start_Click(object sender, RoutedEventArgs e) { if (streamWebSocket != null) { // Already connected return; } Uri server; if (!TryGetUri(ServerAddressField.Text, out server)) { return; } try { //Connecting to server // 支持允许使用 WebSocket 的读取和写入流的网络通信。 streamWebSocket = new StreamWebSocket(); streamWebSocket.Closed += async (senderSocket, args) => { await Window.Current.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => Closed(senderSocket, args)); }; //连接服务器 await streamWebSocket.ConnectAsync(server); readBuffer = new byte[1000]; //启动一个后台任务持续阅读传入的数据 Task receiving = Task.Factory.StartNew(Scenario2ReceiveData, streamWebSocket.InputStream.AsStreamForRead(), TaskCreationOptions.LongRunning); // 启动一个后台任务,不断写输出数据 Task sending = Task.Factory.StartNew(Scenario2SendData, streamWebSocket.OutputStream, TaskCreationOptions.LongRunning); //Connected } catch (Exception ex) { if (streamWebSocket != null) { streamWebSocket.Dispose(); streamWebSocket = null; } WebErrorStatus status = WebSocketError.GetStatus(ex.GetBaseException().HResult); switch (status) { case WebErrorStatus.CannotConnect: case WebErrorStatus.NotFound: case WebErrorStatus.RequestTimeout: // Cannot connect to the server. Please make sure to run the server setup script before running the sample break; case WebErrorStatus.Unknown: throw; default: Error: status break; } OutputField.Text += ex.Message + "\r\n"; } }
//不断写输出数据。写入数据,我们将展示如何使用 data.AsBuffer() 来获得一个 IBuffer 来使用 //webSocket.OutputStream.WriteAsync() 。 或者你可以调用 //webSocket.OutputStream.AsStreamForWrite() 来使用 .NET streams private async void Scenario2SendData(object state) { int dataSent = 0; byte[] data = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 }; MarshalText(OutputField, "Background sending data in " + data.Length + " byte chunks each second.\r\n"); try { IOutputStream writeStream = (IOutputStream)state; // 发送直到套接字 关闭/停止 while (true) { // using System.Runtime.InteropServices.WindowsRuntime; // 在有序流中以异步方式写入数据。 await writeStream.WriteAsync(data.AsBuffer()); dataSent += data.Length; MarshalText(DataSentField, dataSent.ToString(), false); // 延迟 1 秒 ,这样用户可以看发生了什么。 await Task.Delay(TimeSpan.FromSeconds(1)); } } catch (ObjectDisposedException) { MarshalText(OutputField, "Background write stopped.\r\n"); } catch (Exception ex) { WebErrorStatus status = WebSocketError.GetStatus(ex.GetBaseException().HResult); switch (status) { case WebErrorStatus.OperationCanceled: MarshalText(OutputField, "Background write canceled.\r\n"); break; case WebErrorStatus.Unknown: throw; default: MarshalText(OutputField, "Error: " + status + "\r\n"); MarshalText(OutputField, ex.Message + "\r\n"); break; } } }
//持续阅读传入的数据。阅读数据, 我们将展示如何使用 webSocket.InputStream.AsStream() //得到一个 .NET stream。 或者你可以调用 readBuffer.AsBuffer() //webSocket.InputStream.ReadAsync 来使用IBuffer。 private async void Scenario2ReceiveData(object state) { int bytesReceived = 0; try { Stream readStream = (Stream)state; MarshalText(OutputField, "Background read starting.\r\n"); while (true) // Until closed and ReadAsync fails. {
//异步读取一个字节序列,并且在流中通过读取的字节数提前 position int read = await readStream.ReadAsync(readBuffer, 0, readBuffer.Length); bytesReceived += read; MarshalText(DataReceivedField, bytesReceived.ToString(), false); // 处理获得的数据. } } catch (ObjectDisposedException) { MarshalText(OutputField, "Background read stopped.\r\n"); } catch (Exception ex) { WebErrorStatus status = WebSocketError.GetStatus(ex.GetBaseException().HResult); switch (status) { case WebErrorStatus.OperationCanceled: MarshalText(OutputField, "Background write canceled.\r\n"); break; case WebErrorStatus.Unknown: throw; default: MarshalText(OutputField, "Error: " + status + "\r\n"); MarshalText(OutputField, ex.Message + "\r\n"); break; } } }
// 可能由服务器端触发,也可能本地的 Close/Dispose() 方法触发
private void Closed(IWebSocket sender, WebSocketClosedEventArgs args) { MarshalText(OutputField, "Closed; Code: " + args.Code + ", Reason: " + args.Reason + "\r\n"); if (streamWebSocket != null) { streamWebSocket.Dispose(); streamWebSocket = null; } } private void MarshalText(TextBox output, string value) { MarshalText(output, value, true); } private void MarshalText(TextBox output, string value, bool append) { var ignore = output.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { if (append) { output.Text += value; } else { output.Text = value; } }); }
点击 'Close' 按钮时触发 :
private void Stop_Click(object sender, RoutedEventArgs e) { try { if (streamWebSocket != null) { //Stopping streamWebSocket.Close(1000, "Closed due to user request."); streamWebSocket = null; } else { //There is no active socket to stop } } catch (Exception ex) { WebErrorStatus status = WebSocketError.GetStatus(ex.GetBaseException().HResult); if (status == WebErrorStatus.Unknown) { throw; } //Error OutputField.Text += ex.Message + "\r\n"; } }