首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

Windows 8 應用開發實戰:WebSocket 即時通訊完整實作 (Client/Server)

     編輯:關於.NET

前言

WebSocket 提供全雙工長連線通訊,適用於即時聊天、股票報價、協同編輯等場景。本文提供完整的伺服端 (ASP.NET) 與用戶端 (Windows 8 Store App) 實作,涵蓋文字與二進制兩種傳輸模式。

伺服端實作 (ASP.NET 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)
        {
            context.AcceptWebSocketRequest(async wsContext =>
            {
                WebSocket socket = wsContext.WebSocket;
                
                if (socket.State == WebSocketState.Open)
                {
                    // 發送歡迎訊息
                    var outputString = "WebSocket Connected: " + DateTime.Now.ToString("mm:ss");
                    await socket.SendAsync(
                        new ArraySegment(Encoding.UTF8.GetBytes(outputString)),
                        WebSocketMessageType.Text, true, CancellationToken.None);
                }

                byte[] receiveBuffer = new byte[_maxBufferSize];
                
                while (socket.State == WebSocketState.Open)
                {
                    var receiveResult = await socket.ReceiveAsync(
                        new ArraySegment(receiveBuffer), CancellationToken.None);
                    
                    if (receiveResult.MessageType == WebSocketMessageType.Close)
                    {
                        await socket.CloseAsync(
                            receiveResult.CloseStatus.Value,
                            receiveResult.CloseStatusDescription,
                            CancellationToken.None);
                        return;
                    }

                    int offset = receiveResult.Count;
                    // 處理分段訊息
                    while (!receiveResult.EndOfMessage)
                    {
                        receiveResult = await socket.ReceiveAsync(
                            new ArraySegment(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 + "\"";
                        await socket.SendAsync(
                            new ArraySegment(Encoding.UTF8.GetBytes(sendText)),
                            WebSocketMessageType.Text, true, CancellationToken.None);
                    }
                    else if (receiveResult.MessageType == WebSocketMessageType.Binary)
                    {
                        string sendText = "server to client: binary message received, size: " + receiveResult.Count + " bytes";
                        await socket.SendAsync(
                            new ArraySegment(Encoding.UTF8.GetBytes(sendText)),
                            WebSocketMessageType.Text, true, CancellationToken.None);
                    }
                }
            });
        }
    }
}

用戶端:MessageWebSocket (文字模式)

XAML 介面

<StackPanel 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>

核心連線與收發邏輯

private Uri _serverUri = new Uri("ws://localhost:86/WebSocketServer.ashx");
private MessageWebSocket _socket;
private DataWriter _dataWriter;

private async void btnTextDemo_Click(object sender, RoutedEventArgs e)
{
    try
    {
        if (_socket == null)
        {
            _socket = new MessageWebSocket();
            _socket.Control.MessageType = SocketMessageType.Utf8;
            
            _socket.MessageReceived += _socket_MessageReceived;
            _socket.Closed += async (s, args) =>
            {
                await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                {
                    lblMsg.Text += "socket closed - code: " + args.Code + ", reason: " + args.Reason + "\n";
                    if (_socket != null) { _socket.Dispose(); _socket = null; }
                });
            };
            
            await _socket.ConnectAsync(_serverUri);
            _dataWriter = new DataWriter(_socket.OutputStream);
            lblMsg.Text += "connected\n";
        }
        
        string message = "hello " + DateTime.Now.ToString("hh:mm:ss");
        _dataWriter.WriteString(message);
        await _dataWriter.StoreAsync();
        lblMsg.Text += "send: " + message + "\nsent\n";
    }
    catch (Exception ex)
    {
        // 錯誤處理
    }
}

void _socket_MessageReceived(MessageWebSocket sender, MessageWebSocketMessageReceivedEventArgs args)
{
    using (DataReader reader = args.GetDataReader())
    {
        reader.UnicodeEncoding = UnicodeEncoding.Utf8;
        string read = reader.ReadString(reader.UnconsumedBufferLength);
        await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
        {
            lblMsg.Text += "received: " + read + "\n";
        });
    }
}

用戶端:StreamWebSocket (二進制串流模式)

適用於大量二進制資料傳輸,使用背景執行緒持續收發:

private StreamWebSocket _socket;

private async void btnBinaryDemo_Click(object sender, RoutedEventArgs e)
{
    if (_socket == null)
    {
        _socket = new StreamWebSocket();
        _socket.Closed += async (s, args) => { /* 關閉處理 */ };
        await _socket.ConnectAsync(_serverUri);
        
        // 獨立執行緒處理收發
        Task.Factory.StartNew(ReceiveData, _socket.InputStream.AsStreamForRead(), TaskCreationOptions.LongRunning);
        Task.Factory.StartNew(SendData, _socket.OutputStream, TaskCreationOptions.LongRunning);
    }
}

private async void SendData(object state)
{
    IOutputStream writeStream = (IOutputStream)state;
    byte[] data = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 };
    
    while (true)
    {
        await writeStream.WriteAsync(data.AsBuffer());
        await Task.Delay(TimeSpan.FromSeconds(3));
    }
}

private async void ReceiveData(object state)
{
    Stream readStream = (Stream)state;
    byte[] readBuffer = new byte[1024];
    
    while (true)
    {
        int read = await readStream.ReadAsync(readBuffer, 0, readBuffer.Length);
        // 處理接收資料
    }
}

必要權限設定

在 Package.appxmanifest 加入:

<Capability Name="internetClient" />
<Capability Name="privateNetworkClientServer" />

關鍵差異比較

特性MessageWebSocketStreamWebSocket
資料類型訊息導向 (UTF-8 / Binary)串流導向 (僅 Binary)
適用場景聊天、指令、JSON 交換檔案傳輸、影音串流、大量二進制
API 風格DataWriter/DataReaderStream/Task 非同步

原始碼下載

Windows8.rar - 包含伺服端與用戶端完整專案

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved