Socket.IOサーバーと、UnityのWebSocketライブラリとでやりとり

## はじめに

UnityでSocket.IOとの通信で、UnityのC#のバージョンを変えなきゃいけなかったりして、結構詰まってしまったので、そこらへん変えたくないよって時のメモです

ちなみに前回の記事の続きのような感じで書いてます

https://qiita.com/ono-honeycomb/items/359a254fd4c859692472

前提として、インターネットが使える状況ならばクラウドサービス使ったほうが楽だったりします

### 追記

https://github.com/Rocher0724/socket.io-unity

こちらを使うためにSocket.IOのバージョンを `1.7.4` まで落とすと動作します（Known Bug項目に書いてあります）

### 環境と使うもの

* Unity 2021.3.6f1
    * .NET Standard2.1
    * websocket-sharp 
* Node.js v16.15.1
    * Socket.IO v4.5.1
    * express v4.18.1

## Socket.IOについて

https://socket.io/

昔からあるソケット通信をするためのライブラリ

Node.JSでよく使っていて、今はいろいろな言語に対応しているっぽい

概念的には自分の言葉で言うと…「その時に使えるものを使ってリアルタイム通信する」ってことだと思います

Node.JSでソケット通信するために、他にはWebSocketとかよく聞くのであるが、それも包括している感じ

昔だったら例えば、WebSocket使えるブラウザだったら使うけど、対応してないブラウザだったらAjaxを勝手に選んでくれて通信してくれる

もちろん利用する側は使う技術は意識せず、ソースコードを変えずに実装できる

素晴らしい！

### Socket.IOとUnity

今はいろいろ言語対応してますが…やはり主に、`サーバー側はNode.js` `クライアント側はブラウザ` っていう感じ

ですが今回はUnityとで、

https://socket.io/docs/v4/

公式ドキュメントでは、「.NET」に対応していて、コードは以下に

https://github.com/doghappy/socket.io-client-csharp

ですが、.NET6バージョンが必要でした

自分は替えられたと思ったのですが、なんかうまくいがず…

他にも

https://github.com/nhn/socket.io-client-unity3d

https://github.com/Rocher0724/socket.io-unity

ここらへんもすんなりいかなかったでした

### 今回Unity側で使うもの

https://github.com/sta/websocket-sharp

Socket.IOの特徴として、WebSocketも包括しているということで使ってみました

ちなみに、サーバの通信先がUnityだけならば、Node.js側はWebSocketサーバとして作るほうがシンプルではあります（[ws](https://github.com/websockets/ws)とか[WebSocket-Node](https://github.com/theturtle32/WebSocket-Node)とかを使ったりで）

今回は、前回スマートフォンブラウザとのやりとりを作っていたので、Socket.IOでやってみようと思ってやってみました

## ソースコード

### サーバ側 Node.js

まだコード全てを理解したわけではないですが…

```js:server.js
const fs = require('fs')
const express = require('express');
const app = express();
const https = require('https');
const options = {
  key:  fs.readFileSync('./key.pem'),
  cert: fs.readFileSync('./cert.pem')
};
const server = https.createServer(options, app);
const io = require('socket.io')(server);
const WsServer = require('ws').Server;
const PORT = process.env.PORT || 3000;
const wss = new WsServer({ noServer: true });

server.removeAllListeners("upgrade");
server.on("upgrade", (req, socket, head) => {
  console.log('Upgrade!');
  wss.handleUpgrade(req, socket, head, (ws) => {
    console.log('ws connection!');
    ws.on('message', (mess) => {
      console.log(mess.toString());
    });
    ws.on('close', () => {
      console.log('ws close');
    });
  });
});

// --- ブラウザ向け ---
app.use(express.static('public'));
io.on('connection', function(socket){
  socket.on('message', function(mess){
    console.log(mess);
    // 例えば他の接続している端末に配信
    io.emit('message', mess);
  });
  socket.on('disconnect', () => {
    console.log('user disconnected');
  });
});
// ---

// サーバの立ち上げ
server.listen(PORT, () => {
  console.log('listening on https://localhost:' + PORT);
});
```

### クライアント側 Unity

```C#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UniRx;
using System.Net.Sockets;
using WebSocketSharp;

public class SocketTest : MonoBehaviour
{
    private WebSocket _webSocket;

    void Start()
    {
        Debug.Log("start");

        // websocket
        _webSocket = new WebSocket("ws://localhost:3000/");
        _webSocket.OnOpen += (sender, e) => Debug.Log("WebSocket Open");
        _webSocket.OnMessage += (sender, e) => Debug.Log("WebSocket Message Type: " + e.GetType() + ", Data: " + e.Data);
        _webSocket.OnError += (sender, e) => Debug.Log("WebSocket Error Message: " + e.Message);
        _webSocket.OnClose += (sender, e) => Debug.Log("WebSocket Close");
        _webSocket.Connect();
    }

    void Update()
    {
        if (Input.GetKeyUp("s"))
        {
            Debug.Log("send!!");
            _webSocket.Send("TestMessage");
        }
    }

    private void OnDestroy()
    {
        _webSocket.Close();
        _webSocket = null;
    }
}

```