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))
{
}
}
コメント
コメントを投稿