posts - 615, comments - 10491, trackbacks - 594, articles - 0
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

公告

重新想象 Windows 8 Store Apps (63) - 通信: WebSocket

Posted on 2013-10-10 08:35 webabcd 阅读(...) 评论(...) 编辑 收藏

[源码下载]


重新想象 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
[源码下载]