C#標準ライブラリでWebSocketクライアントを作る
C#でWebSocketクライアントを作りたい場合、websocket-sharpというライブラリがよく使われているが、標準ライブラリだけで賄いたい。
標準でSystem.Net.WebSockets名前空間にClientWebSocketクラスが用意されており、MSDNに使い方が載っている。
Windows 8 のネットワーク接続 - Windows 8 と WebSocket プロトコル
一応通信はできるのだが、あまりにも原始的なのでラッパークラスを作る。
using System; using System.IO; using System.Net.WebSockets; using System.Threading; using System.Threading.Tasks; public class WebSocketClient { private const int SendBufferSize = 1024; private const int ReceiveBufferSize = 1024; private ClientWebSocket Socket; public event EventHandler OnOpenHandler; public event EventHandler OnCloseHandler; public event EventHandler<string> OnTextHandler; public event EventHandler<byte[]> OnBinaryHandler; public Uri Uri { get; set; } public async Task SendTextAsync(string text) { using var memoryStream = new MemoryStream(); using var streamWriter = new StreamWriter(memoryStream); await streamWriter.WriteAsync(text); await streamWriter.FlushAsync(); memoryStream.Seek(0, SeekOrigin.Begin); while (memoryStream.Position < memoryStream.Length) { var buffer = new byte[SendBufferSize]; var remaining = memoryStream.Length - memoryStream.Position; var bufferSize = SendBufferSize < remaining ? SendBufferSize : (int)remaining; await memoryStream.ReadAsync(buffer, 0, buffer.Length); await Socket.SendAsync(new ArraySegment<byte>(buffer, 0, bufferSize), WebSocketMessageType.Text, !(memoryStream.Position < memoryStream.Length), CancellationToken.None); } } public async Task SendBinaryAsync(byte[] binary) { using var memoryStream = new MemoryStream(); memoryStream.Write(binary, 0, binary.Length); memoryStream.Seek(0, SeekOrigin.Begin); while (memoryStream.Position < memoryStream.Length) { var buffer = new byte[SendBufferSize]; var remaining = memoryStream.Length - memoryStream.Position; var bufferSize = SendBufferSize < remaining ? SendBufferSize : (int)remaining; await memoryStream.ReadAsync(buffer, 0, buffer.Length); await Socket.SendAsync(new ArraySegment<byte>(buffer, 0, bufferSize), WebSocketMessageType.Binary, !(memoryStream.Position < memoryStream.Length), CancellationToken.None); } } public async Task ConnectAsync() { if (Socket?.State == WebSocketState.Open) return; Socket = new ClientWebSocket(); await Socket.ConnectAsync(Uri, CancellationToken.None); OnOpenHandler(this, EventArgs.Empty); } public async Task CloseAsync() { if (Socket == null || Socket.State == WebSocketState.Closed) return; await Socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); } public WebSocketClient(Uri uri) { Uri = uri; OnOpenHandler += async (sender, e) => { using var memoryStream = new MemoryStream(); using var streamReader = new StreamReader(memoryStream); WebSocketMessageType messageType = WebSocketMessageType.Text; while (Socket.State == WebSocketState.Open) { var buffer = new byte[ReceiveBufferSize]; var result = await Socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); if (messageType != result.MessageType) { messageType = result.MessageType; await memoryStream.FlushAsync(); memoryStream.Seek(0, SeekOrigin.Begin); memoryStream.SetLength(0); } if (result.MessageType == WebSocketMessageType.Text) { await memoryStream.WriteAsync(buffer, 0, result.Count); if (result.EndOfMessage) { await memoryStream.FlushAsync(); memoryStream.Seek(0, SeekOrigin.Begin); OnTextHandler(this, streamReader.ReadToEnd()); memoryStream.Seek(0, SeekOrigin.Begin); memoryStream.SetLength(0); } } else if (result.MessageType == WebSocketMessageType.Binary) { await memoryStream.WriteAsync(buffer, 0, result.Count); if (result.EndOfMessage) { await memoryStream.FlushAsync(); memoryStream.Seek(0, SeekOrigin.Begin); OnBinaryHandler(this, memoryStream.ToArray()); memoryStream.Seek(0, SeekOrigin.Begin); memoryStream.SetLength(0); } } else if (result.MessageType == WebSocketMessageType.Close) { OnCloseHandler(this, EventArgs.Empty); } } }; } public WebSocketClient(string uri) : this(new Uri(uri)) { } }
var ws = new WebSocketClient("ws://localhost:3000"); ws.OnOpenHandler += async (event, e) => { ws.OnTextHandler += (event, e) => { Console.WriteLine(e); }; ws.SendText("aaaaaaaaaa"); await ws.CloseAsync(); }; ws.ConnectAsync();
LINQで短くする
using System; using System.Collections.Generic; using System.Linq; using System.Net.WebSockets; using System.Text; using System.Threading; using System.Threading.Tasks; public class WebSocketClient { private const int SendBufferSize = 1024; private const int ReceiveBufferSize = 1024; private ClientWebSocket Socket; public event EventHandler OnOpenHandler; public event EventHandler OnCloseHandler; public event EventHandler<string> OnTextHandler; public event EventHandler<byte[]> OnBinaryHandler; public Uri Uri { get; set; } public async Task SendTextAsync(string text) { var values = Encoding.UTF8.GetBytes(text).Select((value, index) => (value, index)).GroupBy(a => a.index / SendBufferSize).Select(a => a.Select(b => b.value).ToArray()).ToArray(); foreach (var value in values) { await Socket.SendAsync(new ArraySegment<byte>(value), WebSocketMessageType.Text, value == values.Last(), CancellationToken.None); } } public async Task SendBinaryAsync(byte[] binary) { var values = binary.Select((value, index) => (value, index)).GroupBy(a => a.index / SendBufferSize).Select(a => a.Select(b => b.value).ToArray()).ToArray(); foreach (var value in values) { await Socket.SendAsync(new ArraySegment<byte>(value), WebSocketMessageType.Binary, value == values.Last(), CancellationToken.None); } } public async Task ConnectAsync() { if (Socket?.State == WebSocketState.Open) return; Socket = new ClientWebSocket(); await Socket.ConnectAsync(Uri, CancellationToken.None); OnOpenHandler(this, EventArgs.Empty); } public async Task CloseAsync() { if (Socket == null || Socket.State == WebSocketState.Closed) return; await Socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); } public WebSocketClient(Uri uri) { Uri = uri; OnOpenHandler += async (sender, e) => { WebSocketMessageType messageType = WebSocketMessageType.Text; var bytes = new List<byte>(); while (Socket.State == WebSocketState.Open) { var buffer = new byte[ReceiveBufferSize]; var result = await Socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); if (messageType != result.MessageType) { messageType = result.MessageType; bytes.Clear(); } if (result.MessageType == WebSocketMessageType.Text) { bytes.AddRange(buffer.Take(result.Count)); if (result.EndOfMessage) { OnTextHandler(this, Encoding.UTF8.GetString(bytes.ToArray())); bytes.Clear(); } } else if (result.MessageType == WebSocketMessageType.Binary) { bytes.AddRange(buffer.Take(result.Count)); if (result.EndOfMessage) { OnBinaryHandler(this, bytes.ToArray()); bytes.Clear(); } } else if (result.MessageType == WebSocketMessageType.Close) { OnCloseHandler(this, EventArgs.Empty); } } }; } public WebSocketClient(string uri) : this(new Uri(uri)) { } }
コメント
コメントを投稿