WebSocket 提供全雙工長連線通訊,適用於即時聊天、股票報價、協同編輯等場景。本文提供完整的伺服端 (ASP.NET) 與用戶端 (Windows 8 Store App) 實作,涵蓋文字與二進制兩種傳輸模式。
需在 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);
}
}
});
}
}
}
<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";
});
}
}
適用於大量二進制資料傳輸,使用背景執行緒持續收發:
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" />
| 特性 | MessageWebSocket | StreamWebSocket |
|---|---|---|
| 資料類型 | 訊息導向 (UTF-8 / Binary) | 串流導向 (僅 Binary) |
| 適用場景 | 聊天、指令、JSON 交換 | 檔案傳輸、影音串流、大量二進制 |
| API 風格 | DataWriter/DataReader | Stream/Task 非同步 |
Windows8.rar - 包含伺服端與用戶端完整專案