C# で Socket.IO (SocketIoClientDotNet) を使う

# はじめに

*こんにちは、のんびりエンジニアのたっつーです。*
*[ブログ](https://www.fast-system.jp/)を運営しているのでよろしければ見てください。*

C#(Windowアプリ)、Node.js(サーバ)、Vue.js(ブラウザ)で双方向通信を行いたいと思い、Websocket を隠蔽して使えるようになる Socket.IO を用いた通信のサンプルを作成しました。

本投稿では、C#(Windowアプリ) と Node.js(サーバ) との通信部分を Socket.IO で実装した例を紹介させていただきます。

Node.js(サーバ)、Vue.js(ブラウザ)については以下ご参照ください。
[Nuxt.js で Socket.IO を使う](https://qiita.com/kingyo222/items/a705fbd0d7461320713b)

# SocketIoClientDotNet ライブラリ

以下にインストールから各使用方法を記載します。

## インストール

メニューから ツール > NuGet パッケージマネージャー > ソリューションの NuGet パッケージの管理 > 参照 で SocketIoClientDotNet を検索してインストールします。

## 接続処理

```C#
var socket = IO.Socket("http://localhost:3000");

socket.On(Socket.EVENT_CONNECT, () =>
{
    // 接続完了
});
```

## サーバへのプッシュ

Json形式でデータを送信する場合

```C#
// 送信オブジェクトを作成
var message = new NewMessage()
{
    Date = DateTime.Now,
    Text = this.txtMessage.Text
};
// JObject型に変換
var jobject = JObject.FromObject(message);

// JObject型で送信すると受信側でJsonで取得できる。
socket.Emit("send-message", jobject);
```

文字列でデータを送信する場合

```C#
// Jsonではなく文字列として送る
socket.Emit("send-message", "送信データ");
```

## サーバからの非同期受信

```C#
// new-message を購読し、サーバからの受信を受け取る。
socket.On("new-message", (obj) =>
{
    // obj は
    // Json形式の場合、JObjectで格納されます。
    // テキスト形式の場合、Stringで格納されます。
});
```

## サーバからの同期受信

Emitでサーバにリクエストを行い、ラムダでサーバからのレスポンスが取得できます

```C#
// last-messages を送信して、サーバからの受信を受け取る。
socket.Emit("last-messages", (ary) =>
{
    foreach (JObject jo in (ary as JArray))
        this.RecvNewMessage(jo);
});
```

# サンプルプロジェクト

以下のサンプルを作成しました、C#（Windowsアプリ）、Vue.js（ブラウザ）側で双方向のデータ送受信が行えている事が確認できます。

Windowsアプリ側の実装：[CSharp-Socket.IO-Example](https://github.com/fastsystem/CSharp-Socket.IO-Example)

ブラウザ側の実装：[Nuxt.js で Socket.IO を使う](https://qiita.com/kingyo222/items/a705fbd0d7461320713b)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/227880/379a2bb6-4d33-7048-376b-7483c0f61247.png)

フォーム側のソースコード

```C#
using Newtonsoft.Json.Linq;
using Quobject.SocketIoClientDotNet.Client;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Example
{
    public partial class Form1 : Form
    {
        Socket socket;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            this.socket = IO.Socket("http://localhost:3000");

            this.socket.On(Socket.EVENT_CONNECT, () =>
            {
                this.socket.Emit("last-messages", (ary) =>
                {
                    foreach (JObject jo in (ary as JArray))
                        this.RecvNewMessage(jo);
                });
            });

            this.socket.On("new-message", (jo) =>
            {
                this.RecvNewMessage(jo as JObject);
            });
        }


        private void RecvNewMessage(JObject jobject)
        {
            var message = jobject.ToObject<NewMessage>();

            this.Invoke((MethodInvoker)(() => {
                this.lstMessages.Items.Add(message.Date.ToString() + ":" + message.Text);
            }));
        }

        private void btnSend_Click(object sender, EventArgs e)
        {
            var message = new NewMessage()
            {
                Date = DateTime.Now,
                Text = this.txtMessage.Text
            };

            var jobject = JObject.FromObject(message);

            this.RecvNewMessage(jobject);
            this.socket.Emit("send-message", jobject);
        }
    }
}
```

# まとめ
Socket.IO は非常に便利な仕組みなので皆さんもぜひご活用ください。
時間のかかる部分はしっかりと非同期で実装するクセをつけたいですね（反省）

*よければ ブログ「[初心者向けUnity情報サイト](https://www.fast-system.jp/)」の方にも色々記載しているのでぜひご参照いただければと思います。*