今週ずっと悩んでる。
動機
なんとなくリアルタイムサーバーっていうか、 websocket でクライアントと繋がってるサーバーがどうなってるのかよくわかってなかったので、雑に書いてみたかった。 Unity をリアルタイムサーバーにしたければ、 NetworkManager とか使えばすぐできるのはわかってて、 適当に体験する分には楽かなと思って .Net 4.5 で入ったらしいそれで雑に書いてみたかった。
うまくいかないこと
HttpListener でリクエストを受け取って、ハンドシェイクするところが、どうにも websocket として判定されない。
var httpListener = new HttpListener(); httpListener.Prefixes.Add(uriPrefix); httpListener.Start(); while (true) { var listenerContext = await httpListener.GetContextAsync(); if (listenerContext.Request.IsWebSocketRequest) // これが true にならない {
全コードは最後に。
windows でも mac でもだめ。 Unity 2017.3 と 2018.1b10 は試した。 参考にした .Net のアプリケーションを実行しても同じだったので、 元のコードが悪そうだけど、どうすればいいのか検討もつかない。
Windows 7 で動かないという情報しか見つからない。久しぶりに"詰んだ"感覚を味わっている。
実装
一応載せておくけど、ほぼ下記のコピペ
WebSocket-Samples/Server.cs at master · paulbatum/WebSocket-Samples · GitHub
サーバー
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.WebSockets; using System.Text; using System.Threading.Tasks; using UnityEngine; public class Server : MonoBehaviour { [SerializeField] private string uriPrefix; void Awake() { StartServer(uriPrefix); Log("start."); } void OnDestroy() { Parallel.ForEach(_client,p=> { if (p.State == WebSocketState.Open) p.CloseAsync(WebSocketCloseStatus.NormalClosure, "", System.Threading.CancellationToken.None); }); } static readonly List<WebSocket> _client = new List<WebSocket>(); static async void StartServer(string uriPrefix) { var httpListener = new HttpListener(); httpListener.Prefixes.Add(uriPrefix); httpListener.Start(); while (true) { var listenerContext = await httpListener.GetContextAsync(); if (listenerContext.Request.IsWebSocketRequest) { ProcessRequest(listenerContext); } else { listenerContext.Response.StatusCode = 400; listenerContext.Response.Close(); Log("Response 400"); } } } static async void ProcessRequest(HttpListenerContext listenerContext) { Log($"New Session:{listenerContext.Request.RemoteEndPoint.Address}"); var ws = (await listenerContext.AcceptWebSocketAsync(null)).WebSocket; _client.Add(ws); while (ws.State == WebSocketState.Open) { try { var buff = new ArraySegment<byte>(new byte[1024]); var ret = await ws.ReceiveAsync(buff, System.Threading.CancellationToken.None); if (ret.MessageType == WebSocketMessageType.Text) { Log($"String Received:{listenerContext.Request.RemoteEndPoint.Address}"); Log($"Message={Encoding.UTF8.GetString(buff.Take(ret.Count).ToArray())}"); Parallel.ForEach(_client, p => p.SendAsync(new ArraySegment<byte>(buff.Take(ret.Count).ToArray()), WebSocketMessageType.Text, true, System.Threading.CancellationToken.None)); } else if(ret.MessageType == WebSocketMessageType.Close) { Log($"Session Close:{listenerContext.Request.RemoteEndPoint.Address}"); break; } } catch { Log($"Session Abort:{listenerContext.Request.RemoteEndPoint.Address}"); break; } } _client.Remove(ws); ws.Dispose(); } private static void Log(object o) { Debug.Log($"Server: {o}"); } }