介紹
重新想象 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