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
オブジェクトで定義したパラメーターを持ちます。
ホストが作成されたら通信を開始できます。通信を行うには、各種コマンドをホストに送り、その状態を確認します。 送信できる主要なコマンドは3つあります。
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);
connectionId
と同等の ID を持つ接続へ、myReiliableChannelId
と同等の ID を持った信頼できるチャンネルを使用して、メッセージを送ります。メッセージは buffer[]
に保管され、メッセージの長さは bufferLength
によって定義されます。ホストの状態を確認するには、以下の関数を使用できます。
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);
これらは両方ともイベントを返しますが、一つ目の関数はどんなホストからでもイベントを返す(またホスト ID を recHostId
を経由で返す)のに対し、二つ目は 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
がデータを持つに十分なサイズであれば、データがバッファーにコピーされます。そうでない場合は error
に 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 が対応になりました。 クライアント側においても、上記のステップ(トポロジーおよびコンフィグを含む)はすべて同様であるはずです。ウェブクライアントは、スタンドアロンプレイヤーであるサーバーにのみ接続できます(Win、Mac、Linux のみ)。 サーバーでは以下を呼び出してください。
無効な IP または ポート
IP アドレスがリスニングアドレスである場合、IP アドレスに null を渡すことができます。ここではホストはすべてのネットワークインターフェースをリッスンします。サーバーが対応できる Websocket Host はひとつだけで、同時に一般的なホストを扱うこともできます。
NetworkTransport.AddWebsocketHost(topology, 8887, null);
NetworkTransport.AddHost(topology, 8888);
これは ウェブソケットプロトコルを扱う tcp ソケットを 8887 ポートで開き、一般的なプロトコルを扱う udp ソケットは 8888 ポートを開きます。