Open7

統合版マイクラ向けのライブラリづくりメモ

たくのろじぃ | Takumi Okawaたくのろじぃ | Takumi Okawa

必要そうなもの
結構あるなぁ

  1. WebSocket でサーバを立てる
  2. 接続処理
  3. レスポンス処理
  4. エラー処理
  5. 切断処理
  6. JSONシリアライザ
  7. プロパティ
たくのろじぃ | Takumi Okawaたくのろじぃ | Takumi Okawa

多分ライブラリ依存しないほうがいいと思うので、なるべく .NET STL で実装したい。多分基本形になるのがこれ

using System;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        using (var ws = new ClientWebSocket())
        {
            Uri serverUri = new Uri("ws://localhost:8080");
            await ws.ConnectAsync(serverUri, CancellationToken.None);
            Console.WriteLine("connected");

            while (ws.State == WebSocketState.Open)
            {
                byte[] buffer = new byte[1024];
                WebSocketReceiveResult result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);

                if (result.MessageType == WebSocketMessageType.Text)
                {
                    string message = Encoding.UTF8.GetString(buffer, 0, result.Count);
                    Console.WriteLine("Received message: " + message);
                }
            }

            Console.ReadKey(true);
        }
    }
}
たくのろじぃ | Takumi Okawaたくのろじぃ | Takumi Okawa

GPTに相談

using System;
using System.Net;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        HttpListener listener = new HttpListener();
        listener.Prefixes.Add("http://localhost:8080/");
        listener.Start();

        Console.WriteLine("Listening...");

        while (true)
        {
            HttpListenerContext context = await listener.GetContextAsync();

            if (context.Request.IsWebSocketRequest)
            {
                WebSocket webSocket = await context.AcceptWebSocketAsync(null);

                Console.WriteLine("WebSocket connection established.");

                CancellationTokenSource cts = new CancellationTokenSource();

                // Ctrl+Cを押下するまで、コマンドの入力を待機する
                Task sendTask = Task.Run(async () =>
                {
                    while (!cts.Token.IsCancellationRequested)
                    {
                        Console.Write("Enter command: ");
                        string command = Console.ReadLine();

                        if (!string.IsNullOrEmpty(command))
                        {
                            byte[] buffer = Encoding.UTF8.GetBytes(command);
                            await webSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
                            Console.WriteLine($"Sent message: {command}");
                        }
                    }
                });

                // Ctrl+Cを押下すると、CancellationTokenSourceをキャンセルする
                Console.CancelKeyPress += (sender, eArgs) =>
                {
                    eArgs.Cancel = true;
                    cts.Cancel();
                };

                byte[] receiveBuffer = new byte[1024];

                // クライアントからのメッセージを受信し続ける
                while (webSocket.State == WebSocketState.Open)
                {
                    WebSocketReceiveResult receiveResult = await webSocket.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);

                    if (receiveResult.MessageType == WebSocketMessageType.Close)
                    {
                        await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
                    }
                    else
                    {
                        string command = Encoding.UTF8.GetString(receiveBuffer, 0, receiveResult.Count);
                        Console.WriteLine($"Received command: {command}");
                    }
                }

                await sendTask;
                webSocket.Dispose();

                Console.WriteLine("WebSocket connection closed.");
            }
            else
            {
                context.Response.StatusCode = 400;
                context.Response.Close();
            }
        }
    }
}
たくのろじぃ | Takumi Okawaたくのろじぃ | Takumi Okawa

第2案のメモ書き

public class MCWebSocket
{
    protected string Url { get; }
    protected ushort Port { get; }

    private WebSocket _ws;

    protected MCWebSocket(string url, ushort port)
    {
        Url = url;
        Port = port;
    }

    protected async Task WebServerStart()
    {
        var listener = new HttpListener();
        listener.Prefixes.Add($"http://{Url}:{Port}/");
        listener.Start();
#if DEBUG
        Console.WriteLine("server started.");
#endif
        while (true)
        {
            var context = await listener.GetContextAsync();
            if (context.Request.IsWebSocketRequest)
            {
                _ws = (await context.AcceptWebSocketAsync(null)).WebSocket;
                CancellationTokenSource cts = new CancellationTokenSource();

                Task sendTask = Task.Run(async () =>
                {
                    while (!cts.Token.IsCancellationRequested)
                    {
                        string command = Console.ReadLine();
                        if (!string.IsNullOrEmpty(command))
                        {
                            var buffer = Encoding.UTF8.GetBytes(command);
                            await _ws.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
                        }
                    }
                });

                Console.CancelKeyPress += (s, e) =>
                {
                    e.Cancel = true;
                    cts.Cancel();
                };


                var receiveBuffer = new byte[1024];
                while (_ws.State == WebSocketState.Open)
                {
                    var receiveResult = await _ws.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
                    if (receiveResult.MessageType == WebSocketMessageType.Close)
                    {
                        await _ws.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
                    }
                    else
                    {
                        var result = Encoding.UTF8.GetString(receiveBuffer, 0, receiveResult.Count);
                        Console.WriteLine(result);
                    }
                }

                await sendTask;
                _ws.Dispose();
                //await HandleWebSocketAsync(_ws);
            }
            else
            {
                context.Response.StatusCode = 400;
                context.Response.Close();
            }
        }
    }

    protected async Task HandleWebSocketAsync(WebSocket ws)
    {
        var buffer = new byte[1024];

        while (ws.State == WebSocketState.Open)
        {
            var result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
            if (result.MessageType == WebSocketMessageType.Close)
            {
                await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
            }
            else
            {
                var message = Encoding.UTF8.GetString(buffer);
#if DEBUG
                Console.WriteLine(message);
#endif
                var responseBuffer = Encoding.UTF8.GetBytes(message);
                await ws.SendAsync(new ArraySegment<byte>(responseBuffer), WebSocketMessageType.Text, true, CancellationToken.None);
            }
        }
    }

    protected async Task<string> SendCommandAsync(string command)
    {
        var receiveBuffer = new byte[1024];

        var sendBuffer = Encoding.UTF8.GetBytes(command);
        await _ws.SendAsync(new ArraySegment<byte>(sendBuffer), WebSocketMessageType.Text, true, CancellationToken.None);
#if DEBUG
        Console.WriteLine(command);
#endif
        var response = await _ws.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
        var result = Encoding.UTF8.GetString(receiveBuffer, 0, response.Count);
#if DEBUG
        Console.WriteLine(result);
#endif
        return result;
    }
}
たくのろじぃ | Takumi Okawaたくのろじぃ | Takumi Okawa

一旦、WebSocketSharp に頼ることにした。多分1から作ると時間がかかりそうなのと、MinecraftConnectionのように最初はライブラリ依存しつつも早く仕上げる方向性でやってみるか。