Unity(Unet)-ソーシャルネットワークと安全なWebSocketとの統合

はじめに



この記事では、ネットワークアプリケーション(私の場合はゲーム)をソーシャルネットワークと統合した経験を共有したいと思います。 可能であれば、サードパーティのソリューションに頼らないようにするため、ネットワーク部分はUnity3Dが提供するもの、つまり低レベル部分( LLAPI )を使用するUnetで開発されました。 クライアントのNetworkClientは、リモートサーバーのNetworkServerに接続します。 ゲームサーバーもUnity3Dで記述されています 。 このようなバンドルで動作しますが、 Unetの詳細な知識が必要ですが、否定できない利点があります。



問題



そして、今度はソーシャルにアプリケーションを投稿します。 ネットワーク。 APIソーシャルを接続することにより。 ネットワーク、ソーシャルでアプリケーションを設定します。 ネットワーク、 WebGlアセンブリをコンパイルし、サーバーにゲームを投稿しました。 アプリケーションの最初の起動ですぐにエラーが明らかになりました。WebGlアセンブリは通常のSocketを開くことができず、 WebSocketを介してのみ作業する必要があります。 ここではすべてが明確です-これは私の省略です。なぜなら、 Unity Web Playerの下で極端なアプリケーションを作成したからです。これは通常のSocketで動作します。 WebSocketで動作するようにネットワークを設定する問題を解決した後(これについては後で説明します)、次の問題に遭遇しました。 なぜなら 社会的 ネットワーク(少なくとも私が統合したネットワーク)は、セキュアなプロトコル( Https )を介して機能し、そのコンテンツがセキュアなプロトコルを介して機能することも要求します。 「それは問題ではない」と思ったので、今からやろうと思った...数日間インターネットを「掘り」、 Unity開発者と話をして、私は憂鬱に陥りました 。 私の知る限り、この場所のほとんどの開発者は「光子」に行きます。 しかし、私たちはそうではありません!



それで、社会は何をしますか。 ネットワーク。 通常のWebアセンブリの場合、通常のWebSocketを使用して作業を楽しみます。



一般的なスクリプト



クライアント(c#):
#if _CLIENT_ using UnityEngine; using UnityEngine.Networking; namespace EXAMPLE.CLIENT { public class NetworkClientExample : NetworkClient { NetworkWriter mWriter = new NetworkWriter(); public NetworkClientExample() : base() { Configure(NetworkableConfig.GetConnectionConfig(), 1); RegisterHandler(MsgType.Connect, OnConnect); RegisterHandler(MsgType.Disconnect, OnDisconnect); RegisterHandler(MsgType.Error, OnError); RegisterHandler((short)EnumRpc.RpcHelloWorld, RpcLobbbyHelloWorld); } public void OnConnect(NetworkMessage _msg) { Debug.Log("OnConnectLobby"); mWriter.StartMessage((short)EnumRpc.CmdHelloWorld); mWriter.Write("Hello from client"); mWriter.FinishMessage(); SendWriter(mWriter, Channels.DefaultReliable); } void OnDisconnect(NetworkMessage _msg) { Debug.Log("OnDisconnectLobby"); UnregisterHandler(MsgType.Connect); UnregisterHandler(MsgType.Disconnect); UnregisterHandler(MsgType.Error); UnregisterHandler((short)EnumRpc.RpcHelloWorld); } void OnError(NetworkMessage _msg) { Debug.Log("OnErrorLobby"); } void RpcLobbbyHelloWorld(NetworkMessage _msg) { Debug.Log(_msg.reader.ReadString()); } } // class } // namespace #endif
      
      







NetworkClientExampleを使用する(c#):
 NetworkClientExample client = new NetworkClientExample(); client.Connect(NetworkableConfig.ServerHost, NetworkableConfig.ServerPort);
      
      









クライアントとのサーバー接続(c#):
 #if _SERVER_ using UnityEngine; using UnityEngine.Networking; namespace EXAMPLE.SERVER { public class ConnectionServerExample: NetworkConnection { NetworkWriter mWriter = new NetworkWriter(); public void SendHelloWorld() { mWriter.StartMessage((short)EnumRpc.RpcHelloWorld); mWriter.Write("Hello from server"); mWriter.FinishMessage(); SendWriter(mWriter, Channels.DefaultReliable); } } // class } // namespace #endif
      
      







サーバー(c#):
 #if _SERVER_ using UnityEngine; using UnityEngine.Networking; namespace EXAMPLE.SERVER { public class NetworkServerExample : MonoBehaviour { public const int sMaxConnections = 5000; void Start() { NetworkServer.SetNetworkConnectionClass<ConnectionServerExample>(); ConnectionConfig config = NetworkableConfig.GetConnectionConfig(); NetworkServer.Configure(config, sMaxConnections); NetworkServer.RegisterHandler(MsgType.Connect, OnConnect); NetworkServer.RegisterHandler(MsgType.Disconnect, OnDisconnect); NetworkServer.RegisterHandler(MsgType.Error, OnError); NetworkServer.RegisterHandler((short)EnumRpc.CmdHelloWorld, CmdHelloWorld); NetworkServer.Listen(NetworkableConfig.ServerPort ); } void OnConnect(NetworkMessage _msg) { Debug.Log("OnConnect"); } void OnDisconnect(NetworkMessage _msg) { Debug.Log("OnDisconnect"); } public void OnError(NetworkMessage _msg) { Debug.Log("OnError"); } void CmdHelloWorld(NetworkMessage _msg) { Debug.Log(_msg.reader.ReadString()); ((ConnectionServerExample)_msg.conn).SendHelloWorld(); } } // class } // namespace #endif
      
      







ネットワーク定数(c#):
 using UnityEngine.Networking; namespace EXAMPLE { public enum EnumRpc : short { RpcHelloWorld = 1, CmdHelloWorld, } public static class NetworkableConfig { /// <summary> ///  Lobby- /// </summary> public const int ServerPort = 5000; /// <summary> ///  Lobby-web- /// </summary> public const int ServerPortWebSocket = 5500; /// <summary> ///  Lobby- /// </summary> public const string ServerHost = "127.0.0.1"; /// <summary> /// Timeout  /// </summary> public const int DisconnectTimeout = 10000; /// <summary> ///     /// </summary> public static ConnectionConfig GetConnectionConfig() { ConnectionConfig config = new ConnectionConfig(); config.AddChannel(QosType.ReliableSequenced); config.AddChannel(QosType.Unreliable); config.DisconnectTimeout = DisconnectTimeout; return config; } } // class } // namespace
      
      







WebSocketに行く



私の場合、サーバーゲームロジックは「モバイル」クライアントと「ブラウザ」クライアントの両方に共通する必要があるため、 WebSocketに切り替えることにしました。 しかし、後者の「スローダウン」について読んだ後、2台の並列サーバーを作成することにしました。 これらのサーバーの相互作用について考えていたときに、あるフォーラムでNetworkServerSimpleクラスについて言及し、喜びました。 彼をWebSocketに設定します。 そして、残りのことは、クライアントをコアサーバーに転送することだけです。



WebSocket用サーバー(c#):
 #if _SERVER_ using UnityEngine.Networking; namespace EXAMPLE.SERVER { class NetworkServerWebSocket : NetworkServerSimple { public void Start(ConnectionConfig _config, int _maxConnections) { base.Initialize(); base.Configure(_config, _maxConnections); //    webSocket' useWebSockets = true; Listen(NetworkableConfig.ServerPortWebSocket); } public override void OnConnected(NetworkConnection _conn) { base.OnConnected(_conn); //         NetworkServer.AddExternalConnection(_conn); } public override void OnDisconnected(NetworkConnection _conn) { //        NetworkServer.RemoveExternalConnection(_conn.connectionId); base.OnDisconnected(_conn); } } // class } // namespace #endif
      
      







WebSocketサーバーを起動するには、メインサーバーのスクリプトを編集する必要があります。



NetworkServerExampleの修正(c#):
 public class NetworkServerExample : MonoBehaviour { //... NetworkServerWebSocket mServerWebSocket; //... void Start() { //... mServerWebSocket = new NetworkServerWebSocket(); mServerWebSocket.Start(config, sMaxConnections); //... } //... void Update() { //... // WebSocket      if(mServerWebSocket != null) { mServerWebSocket.Update(); } //... } //... }
      
      







そして、それはすべてすぐに働きました...しかし、長くは続きませんでした。 両方のサーバーへの接続が同時に行われるまで。 Unetは 、すべての稼働中のサーバーの接続識別子の一意性という単純な問題を解決できないことが判明しました。 それは恥ずかしくて迷惑ですが、...松葉杖のないところです。 メインサーバーから可能な限り多くの接続をシフトすることで識別子を置き換えるWebSocket接続用のクラスを単に紹介します。 なぜなら サーバーはリモートであり、多くの場合専用です。接続に二重のリンク配列を割り当てるために、わずかなメモリの無駄で目を覆うことができます。 だから:



WebSocket接続サーバークラス(c#):
 #if _SERVER_ using UnityEngine.Networking; namespace EXAMPLE.SERVER { public class ConnectionLobbyServerWebSocket : NetworkConnection { public override void Initialize(string networkAddress, int networkHostId, int networkConnectionId, HostTopology hostTopology) { base.Initialize(networkAddress, networkHostId, networkConnectionId + NetworkServerExample.sMaxConnections, hostTopology); } public override bool TransportSend(byte[] bytes, int numBytes, int channelId, out byte error) { return NetworkTransport.Send(hostId, connectionId - NetworkServerExample.sMaxConnections, channelId, bytes, numBytes, out error); } } // class } // namespace #endif
      
      







今、すべてが大丈夫です!



保護されたWebSocket



なぜなら 私はサーバー管理の完全な素人です-私にとって、このアイテムは最も難しいことがわかりました。 ホスティングには、 Windows Serverを使用します 。 そして、 IISの WSSからWSへの転送のトピックに取り組むほど、悪化しました。 確かに「Atts-Odmin」の場合、このタスクは簡単ですが、私は彼ではありません。 ただし、研究中

IISでは、 Apacheではこれが非常に簡単であることに気付きました。 やった! Apacheという言葉を知っています。 確かに、前述の管理者はWindows Serverと Apacheの組み合わせから病気になるでしょう。

ApacheWSSからWSに転送するには、その上でSSLをサポートする必要があり(これについては実際の専門家から多くの情報があります)、モジュール「mod_proxy.so」および「mod_proxy_wstunnel.so」を有効にして転送を設定します。



httpd.conf
 #  c   LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so #      ProxyPass /my_proxy/ ws://localhost:5500/ ProxyPassReverse /my_proxy/ ws://localhost:5500/
      
      





ここで、「my_proxy」は、クライアントへの接続に使用される識別子のいずれかです。 5500- WebSocketサーバーをリッスンしているポート



WebSocketサーバーは既に目的のポートでリッスンしており、クライアント接続を構成するために残ります。



NetworkClientExampleの修正(c#)
 //... public NetworkClientExample() : base() { //... //      . _VK_ -    .  #if _VK_ // -  NetworkServer NetworkServer.useWebSockets = true; #endif //... } //...
      
      







NetworkClientExampleの使用(c#)
 NetworkClientExample client = new NetworkClientExample(); #if _VK_ client.Connect("_/my_proxy/", 443); #else client.Connect(NetworkableConfig.ServerHost, NetworkableConfig.ServerPort); #endif
      
      





your_domainは、プロトコルのないドメインの名前です。例:habrahabr.ru

my_proxyは、「httpd.conf」で指定したプロキシ識別子です

プロキシIDの後の「/」を忘れないでください!



残りはほとんどありません。 次に、通常のWebソケット( WS )を介してUnet要求を安全なWSS Webソケットに変換する必要があります。 これは、小さなJavaScriptスクリプトを使用して、Webページの本文に直接挿入するか、プラグインとしてプロジェクトに接続することで実行できます。 2番目のオプションの方が快適でした。 したがって、プロジェクトの「Assets / Plugins」フォルダにスクリプトを作成します。たとえば、「WssHack.jspre」(拡張子「.jspre」、そうでない場合「little girl」は気まぐれです)と次の内容です。



WssHack.jspre(JavaScript)
 Object.defineProperty(Module, "asmLibraryArg", { set: function (value) { value._JS_UNETWebSockets_SocketCreate = function (hostId, urlPtr) { var url = Pointer_stringify(urlPtr).replace(/^ws:\/\//, "wss://"); urlPtr = Runtime.stackAlloc((url.length << 2) + 1); stringToUTF8(url, urlPtr, (url.length << 2) + 1); return _JS_UNETWebSockets_SocketCreate(hostId, urlPtr); }; Module._asmLibraryArg = value; }, get: function () { return Module._asmLibraryArg; }, });
      
      







すべてがあなたの役に立つことを願っています! ご質問、お気軽に。



All Articles