💎

それ、F#でやってみました - WebSocket (サーバ:Bun 1.1 + クライアント:.NET ClientWebSocket)

2024/05/10に公開

.NETのClientWebSocketクラスを使ってF#で文字列を送受信するWebSocketクライアントを作成してみました。WebSocketのサーバは受信した文字列をそのまま送信(エコー)するものをBun1.1で作成しました。

サーバ:ASP.NET Core + クライアント:Bun 1.1はこちら

WebSocketクライアント(Program.fs)
// .NET での WebSocket のサポート
// https://learn.microsoft.com/ja-jp/dotnet/fundamentals/networking/websockets

// WebSocketクライアント サーバに文字列を送信、受信した文字列を表示
// クライアントから先に切断すること
// .NET 9 Preview 3 対応

// dotnet new console -lang f# -o ディレクトリ名
// cd ディレクトリ名
// (Program.fsを編集)
// dotnet run

open System
open System.Net.WebSockets
open System.Text
open System.Threading

// メイン(エントリーポイント)
[<EntryPoint>]
let main args =
    // メッセージ入力時のプロンプト
    let prompt = "ws> "
    // 受信用バッファ
    let bytes = Array.zeroCreate<byte> 1024

    // メッセージ入力を継続するか
    let mutable cond = true;
    // 受信メッセージ用
    let mutable message = ""

    // WebSocketの接続先
    let uri = Uri "ws://localhost:5080/ws"
    // クライアントWebSocket
    let ws = new ClientWebSocket();

    // WebSocket接続
    ws.ConnectAsync(uri, CancellationToken.None).Wait()

    // サーバからメッセージを受信
    let mutable result =
        ws.ReceiveAsync(new ArraySegment<byte>(bytes),
                        CancellationToken.None).Result
    message <- Encoding.UTF8.GetString(bytes, 0, result.Count)
    printfn "%s" message

    // メッセージ入力が続く間はWebSocketの送受信を行う
    while (cond && not result.CloseStatus.HasValue) do
        printf "%s" prompt
        // ターミナルにメッセージを入力されたメッセージを取得
        message <- stdin.ReadLine()
        if (message = "") then
            // Enterのみなら終了
            cond <- false
        else
            // 入力されたメッセージをサーバに送信
            ws.SendAsync(
                new ArraySegment<byte>(Encoding.UTF8.GetBytes(message)),
                WebSocketMessageType.Text, true, CancellationToken.None).Wait()
            // サーバからメッセージを受信(エコー)
            result <- ws.ReceiveAsync(new ArraySegment<byte>(bytes),
                CancellationToken.None).Result
            message <- Encoding.UTF8.GetString(bytes, 0, result.Count)
            printfn "%s" message 
    // WebSocketクローズ
    ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "WebSocket Closed!",
              CancellationToken.None).Wait()
    0   // mainの終了
WebSocketサーバ(Bun1.1)
// Bun 1.1 WebSocket is stable
// https://bun.sh/blog/bun-v1.1#websocket-is-stable

// WebSocketサーバ クライアントから受信したメッセージをそのまま送り返す
// クライアントから先に切断すること
// Bun 1.1対応

// bun run ソースファイル名.js

Bun.serve({
  port: 5080,
  fetch(req, server) {	// サーバの処理
    // WebSocketはURLの末尾/wsで受け付ける
    if (server.upgrade(req) && new URL(req.url).pathname === "/ws") {
      return; // レスポンスは出力しない
    }
    return new Response("Upgrade failed", { status: 500 });
  },
  websocket: {	// WebSocketの処理
    open(ws) {	// WebSocketクライアントが接続されたとき
      console.log("WebSocket Opened.");
      ws.send("WebSocketサーバに接続しました");
    },
    message(ws, message) {	// メッセージを受信したとき
      console.log("message:", message);
      ws.send(message);
    },
    close(ws) {	// WebSocketクライアントが終了したとき
      console.log("WebSocket Cloased.");
    }
  },
});
結果
// クライアント
WebSocketサーバに接続しました        (サーバから受信した文字列)
ws> こんにちはWebSocket!            (入力した文字列)
こんにちはWebSocket!                (サーバから受信した文字列))
ws> クライアントはASP.NET Core
クライアントはASP.NET Core
ws> サーバはBun 1.1
サーバはBun 1.1
ws> (Enterのみで終了)

// サーバ
WebSocket Opened.
message: こんにちはWebSocket!       (クライアントから受信したメッセージ)
message: クライアントはASP.NET Core
message: サーバはBun 1.1
WebSocket Cloased.

Discussion