Unity の提供する高レベルのネットワーク API は、プレイヤーやネットワーク上の GameObject、その他ネットワークで必要となる機能を簡単に管理できるシステムを備えていますが、これに加えて “Transport Layer” と呼ばれる低レベル API も使用可能となっています。これにより、低レベルで自分のワーキングシステムを構築することが可能となっています。これは、より特殊な、あるいは高度な条件がゲームのネットワークに要求される場合に役に立つでしょう。
Transport Layer は、OS のソケットベースのネットワーキング上で機能する薄いレイヤーです。バイトの配列で表されるメッセージの送受信機能を備え、さまざまなケースに対応するために“quality of service(サービス品質)” を変更できるオプションを多数提供しています。Transport Layer は柔軟性とパフォーマンスに重点を置いたもので、UnityEngine.Networking.NetworkTransport クラス内の API で使用可能です。
Transport Layer は、ネットワーク通信の基本的な機能に対応しています。これには以下が含まれます。
Transport Layer は二つのプロトコルを使用することができます。全般的な通信には UDP、WebGL には WebSockets が使用されます。 Transport Layer を直接使用する場合、典型的なワークフローは以下のようになります。
Network Transport Layer を初期化する場合、デフォルトの初期化(引数なし)にするか、あるいは、ネットワークレイヤーの全面的な挙動を制御するパラメーター(例:最高パケットサイズやスレッドのタイムアウト制限など)を提供するか、どちらかを選ぶことができます。
// 引数無しで Transport Layer を初期化する (デフォルト設定)
NetworkTransport.Init();
// カスタム設定で Transport Layer を初期化する例
GlobalConfig gConfig = new GlobalConfig();
gConfig.MaxPacketSize = 500;
NetworkTransport.Init(gConfig);
上記の二つ目の例では、Transport Layer は、値が 500 にカスタム設定された “MaxPacketSize” を以って初期化されています。カスタムの Init 値は、特殊なネットワーク環境を使用していて且つ必要な設定を明確に理解している場合にのみお使いください。基本的には、インターネットを介してプレイする典型的なマルチプレイヤーゲームの開発の場合、引数なしのデフォルト Init() 設定が適切です。
次のステップは、ピア間の接続の設定です。異なる通信チャンネルをいくつか定義する必要があるかもしれません。各チャンネルのサービス品質レベルは、送りたい特定のメッセージのタイプや、ゲーム中におけるそれらの相対的な重要度に合うように設定します。
ConnectionConfig config = new ConnectionConfig();
int myReiliableChannelId = config.AddChannel(QosType.Reliable);
int myUnreliableChannelId = config.AddChannel(QosType.Unreliable);
上記の例では、サービス品質レベルの値の異なる二つの通信チャンネルを定義しています。“QosType.Reliable” はメッセージを配信し、それが確実に届くようにします。“QosType.Unreliable” はメッセージが届く保証はしませんが、より速く送信されます。
また、ConnectionConfig オブジェクトのプロパティーを調整することで、コンフィグ設定を各接続ごとに行うことも可能です。ただし、あるクライアントから他のクライアントへの接続を行う場合、両方のピアで設定を同じにしないと、エラー CRCMismatch
により接続に失敗してしまいます。
ネットワーク設定の最後のステップは、トポロジー定義です。ネットワーク トポロジーは、接続可能な回線の最大数および、使用される接続設定を定義します。
HostTopology topology = new HostTopology(config, 10);
ここでは、10 まで接続可能なトポロジーを作成しました。それぞれが前の手順で定義されたパラメーターによって設定されます。
準備がすべて完了したので、ホスト (未接続のソケット) を作成します。
int hostId = NetworkTransport.AddHost(topology, 8888);
ここでは、新しいホストをポート 8888 と任意の IP アドレスに追加します。 このホストは最大 10 の接続をサポートし、各接続は config
オブジェクトで定義したパラメーターを持ちます。
When the host is created, we can start our communication. To do this we send different commands to the host and check its status. There are 3 main commands that we can send:
connectionId = NetworkTransport.Connect(hostId, "192.16.7.21", 8888, 0, out error);
NetworkTransport.Disconnect(hostId, connectionId, out error);
NetworkTransport.Send(hostId, connectionId, myReiliableChannelId, buffer, bufferLength, out error);
myReiliableChannelId
と等しい ID を持つ信頼性の高いチャンネルを使用して、connectionId
と等しい ID で接続にメッセージを送信します。メッセージは buffer[]
に格納され、メッセージの長さは bufferLength
で定義されます。ホストの状態を確認するには、以下の 2 つの関数を使用できます。
NetworkTransport.Receive(out recHostId, out connectionId, out channelId, recBuffer, bufferSize, out dataSize, out error);
NetworkTransport.ReceiveFromHost(recHostId, out connectionId, out channelId, recBuffer, bufferSize, out dataSize, out error);
どちらもイベントを返します。最初の関数は、任意のホストからのイベントを返します (そして recHostId
を通してホスト ID を返します)。 2 番目の形式は、id が recHostId
のホストを確認します。Update()
メソッド内でこれらの関数を使用できます。
void Update()
{
int recHostId;
int connectionId;
int channelId;
byte[] recBuffer = new byte[1024];
int bufferSize = 1024;
int dataSize;
byte error;
NetworkEventType recData = NetworkTransport.Receive(out recHostId, out connectionId, out channelId, recBuffer, bufferSize, out dataSize, out error);
switch (recData)
{
case NetworkEventType.Nothing: //1
break;
case NetworkEventType.ConnectEvent: //2
break;
case NetworkEventType.DataEvent: //3
break;
case NetworkEventType.DisconnectEvent: //4
break;
}
}
myConnectionId = NetworkTransport.Connect(hostId, "192.16.7.21", 8888, 0, out error);
NetworkEventType recData = NetworkTransport.Receive(out recHostId, out connectionId, out channelId, recBuffer, bufferSize, out dataSize, out error);
switch (recData)
{
case NetworkEventType.ConnectEvent:
if(myConnectionId == connectionId)
//有効な接続リクエストが承認されました
else
//誰かが自分に接続しています
break;
//...
}
recHostId
はホストを定義し、connectionId は接続を定義し、channelId
はチャンネルを定義し、dataSize
は受信したデータのサイズを定義します。recBuffer
がデータを格納するのに十分な大きさであれば、データはバッファーにコピーされます。 そうでない場合、エラーに MessageToLong
エラーが含まれ、バッファーを再割り当てしてこの関数を再度呼び出す必要があります。myConnectionId = NetworkTransport.Connect(hostId, "192.16.7.21", 8888, 0, out error);
NetworkEventType recData = NetworkTransport.Receive(out recHostId, out connectionId, out channelId, recBuffer, bufferSize, out dataSize, out error);
switch (recData)
{
case NetworkEventType.ConnectEvent:
if(myConnectionId == connectionId)
//有効な接続リクエストが承認されました
else
//誰かが自分に接続しています
break;
//...
}
WebSocket on client is supported. For the client side, all the steps described above (including topology and configuration) should be the same. Web clients can connect to the server only, where the server is a standalone player (Win, Mac or Linux only). On the server you should call
無効な IP または ポート
IP アドレスがリスニングアドレスである場合、IP アドレスに null を渡すことができます。ここではホストはすべてのネットワークインターフェースをリッスンします。サーバーが対応できる Websocket Host はひとつだけで、同時に一般的なホストを扱うこともできます。
NetworkTransport.AddWebsocketHost(topology, 8887, null);
NetworkTransport.AddHost(topology, 8888);
これは ウェブソケットプロトコルを扱う tcp ソケットを 8887 ポートで開き、一般的なプロトコルを扱う udp ソケットは 8888 ポートを開きます。