JavaでのStream Control Transport Protocol(SCTP)

Chris Hegarty、2009年6月

JavaでのStream Control Transport Protocol(SCTP)のサポートが、JDK 7の機能として承認されました。 APIとリファレンス実装の定義は、OpenJDK SCTPプロジェクトを通して実施されました。 この作業はJDK 7 マイルストーン3に統合されており、将来のすべての拡張で利用できます。

SCTPの概要

Stream Control Transport Protocol(SCTP)は、信頼性に優れたメッセージ指向のトランスポート・プロトコルであり、UDP(User Datagram Protocol)およびTCP(Transmission Control Protocol)と同じ階層に位置します。 SCTPはセッション指向であり、データを転送する前にエンドポイント間のアソシエーションを確立する必要があります。

SCTPはマルチホーミングを直接サポートしています。つまり、エンドポイントを複数のアドレスで表すことができ、各アドレスをデータの送受信に使用できます。このため、ネットワークの冗長性が確保されます。 2つのエンドポイント間のコネクションは、これらのエンドポイント間のアソシエーションと呼ばれます。 エンドポイントは、アソシエーション設定中にアドレスのリストを交換できます。 1つのアドレスがプライマリ・アドレスとして指定されます。これが、ピア・エンドポイントがデータの送信に使用するデフォルト・アドレスとなります。 あるエンドポイントの特定のセッションでは、アドレス・リスト全体で単一のポート番号が使用されます。

SCTPはメッセージベースです。 I/O処理はメッセージに対して行われ、メッセージ間の境界が維持されます。 各アソシエーションは複数の独立した論理ストリームをサポートできます。 各ストリームは、単一のアソシエーション内部におけるメッセージ・シーケンスを表し、ストリームは互いに独立しています。つまり、ストリーム識別子とシーケンス番号がデータ・パケットに含まれており、ストリームごとにメッセージのシーケンスを設定できます。

SCTPのおもな機能

  • メッセージ・フレーミング
  • 信頼性の高いトランスポート・サービス
  • セッション指向
  • 順序あり/順序なしのメッセージ配信
  • マルチホーミング
    • 2つのエンドポイント間のアソシエーション
    • 各エンドポイントは複数のIPアドレスで表すことが可能
    • フェイルオーバーと冗長性を確保
  • マルチストリーミング
    • データを複数のストリームにパーティション化
    • 独立したシーケンス配信
  • ヘッドオブライン・ブロッキングを回避

JDK 7でのSCTPのサポート

Java APIは、NIOチャネル・フレームワークをベースとしています。このため、SCTPを必要とするアプリケーションは、ブロッキングなしの多重化I/Oを活用できます。新しいクラス/インタフェースを格納するための新しいパッケージcom.sun.nio.sctpが定義されています。 パッケージ名はcom.sun.nio.sctpです。java.nio.channels.sctpなどの形式にはなっていません。

この違いは、APIと実装は完全にサポートされ公開されますが、Java SEプラットフォームには含まれないことを表しています。 業界でSCTPに関する経験がもっと積まれた後に、標準APIが定義できることになります。

このパッケージ内のメイン・クラスは、新しい3種類のチャネル・タイプを表します。 これらの新しいチャネルは、2つの論理グループに分類できます。

  1. 1つ目の論理グループは、TCPと同様のセマンティクスとなる、SctpChannelSctpServerChannelです。 SctpChannelは、単一のアソシエーション、つまり、単一のエンドポイントに入出力するデータの送受信のみを制御できます。 SctpServerChannelは、そのソケット・アドレス上で開始された新しいアソシエーションをリスニングし、受け入れます。
  2. 2つ目の論理グループはSctpMultiChannelのみで構成されます。 このチャネル・タイプのインスタンスは複数のアソシエーションを制御できます。そのため、異なる複数のエンドポイントに入出力するデータを送受信できます。

SCTPスタックはイベント駆動型であり、アプリケーションは特定のSCTPイベントの通知を受け取ることができます。 これらのイベントは、SctpMultiChannelと使用するともっとも有効です。このクラスは複数のアソシエーションを制御できるので、通知の状況を追跡する必要があります。 たとえば、AssociationChangeNotificationは、新しいアソシエーションの開始または終了を通知します。 アソシエーションが動的なアドレス設定をサポートしている場合、PeerAddressChangeNotificationは、ピア・エンドポイントに追加された、またはピア・エンドポイントから削除されたIPアドレスを通知します。 MessageInfoを使用すると、送信中または受信中のメッセージの補助的なデータを使用できます。

マルチストリーミングの例

次の例では、SCTPのマルチストリーミング機能について示します。 この例のサーバーは、あるタイプの日時プロトコルを実装します。 一方のストリームでは英語(米国)形式、もう一方のストリームではフランス語形式でフォーマットした現在の日時を送信します。

コードを読みやすくするため、エラー処理は省略しています。

多言語日時サーバー(DaytimeServer)

次に、DaytimeServerのソース・コードを示します。

public class DaytimeServer {
   
static int SERVER_PORT = 3456;
   
static int US_STREAM = 0;
   
static int FR_STREAM = 1;

   
static SimpleDateFormat USformatter = new SimpleDateFormat(
                               
"h:mm:ss a EEE d MMM yy, zzzz", Locale.US);
   
static SimpleDateFormat FRformatter = new SimpleDateFormat(
                               
"h:mm:ss a EEE d MMM yy, zzzz", Locale.FRENCH);

   
public static void main(String[] args) throws IOException {
       
SctpServerChannel ssc = SctpServerChannel.open();
       
InetSocketAddress serverAddr = new InetSocketAddress(SERVER_PORT);
        ssc
.bind(serverAddr);

       
ByteBuffer buf = ByteBuffer.allocateDirect(60);
       
CharBuffer cbuf = CharBuffer.allocate(60);
       
Charset charset = Charset.forName("ISO-8859-1");
       
CharsetEncoder encoder = charset.newEncoder();

       
while (true) {
           
SctpChannel sc = ssc.accept();

           
/* get the current date */
           
Date today = new Date();
            cbuf
.put(USformatter.format(today)).flip();
            encoder
.encode(cbuf, buf, true);
            buf
.flip();

           
/* send the message on the US stream */
           
MessageInfo messageInfo = MessageInfo.createOutgoing(null,
                                                                 US_STREAM
);
            sc
.send(buf, messageInfo);

           
/* update the buffer with French format */
            cbuf
.clear();
            cbuf
.put(FRformatter.format(today)).flip();
            buf
.clear();
            encoder
.encode(cbuf, buf, true);
            buf
.flip();

           
/* send the message on the French stream */
            messageInfo
.streamNumber(FR_STREAM);
            sc
.send(buf, messageInfo);

            cbuf
.clear();
            buf
.clear();

            sc
.close();
       
}
   
}
}

多言語日時クライアント(DaytimeClient)

次に、DaytimeClientのソース・コードを示します。

public class DaytimeClient {
   
static int SERVER_PORT = 3456;
   
static int US_STREAM = 0;
   
static int FR_STREAM = 1;

   
public static void main(String[] args) throws IOException {
       
InetSocketAddress serverAddr = new InetSocketAddress("localhost",
                                                             SERVER_PORT
);
       
ByteBuffer buf = ByteBuffer.allocateDirect(60);
       
Charset charset = Charset.forName("ISO-8859-1");
       
CharsetDecoder decoder = charset.newDecoder();

       
SctpChannel sc = SctpChannel.open(serverAddr, 0, 0);

       
/* handler to keep track of association setup and termination */
       
AssociationHandler assocHandler = new AssociationHandler();

         
/* expect two messages and two notifications */
       
MessageInfo messageInfo = null;
       
do {
            messageInfo
= sc.receive(buf, System.out, assocHandler);
            buf
.flip();

           
if (buf.remaining() > 0 &&
                messageInfo
.streamNumber() == US_STREAM) {

               
System.out.println("(US) " + decoder.decode(buf).toString());
           
} else if (buf.remaining() > 0 &&
                       messageInfo
.streamNumber() == FR_STREAM) {

               
System.out.println("(FR) " +  decoder.decode(buf).toString());
           
}
            buf
.clear();
       
} while (messageInfo != null);

        sc
.close();
   
}

   
static class AssociationHandler
       
extends AbstractNotificationHandler
   
{
       
public HandlerResult handleNotification(AssociationChangeNotification not,
                                               
PrintStream stream) {
           
if (not.event().equals(COMM_UP)) {
               
int outbound = not.association().maxOutboundStreams();
               
int inbound = not.association().maxInboundStreams();
                stream
.printf("New association setup with %d outbound streams" +
                             
", and %d inbound streams.\n", outbound, inbound);
           
}

           
return HandlerResult.CONTINUE;
       
}

       
public HandlerResult handleNotification(ShutdownNotification not,
                                               
PrintStream stream) {
            stream
.printf("The association has been shutdown.\n");
           
return HandlerResult.RETURN;
       
}
   
}
}

出力サンプル

出力例は、次のとおりです。

>: java DaytimeClient
New association setup with 32 outbound streams, and 32 inbound streams.
(US) 4:00:51 PM Fri 15 May 09, British Summer Time
(FR) 4:00:51 PM ven. 15 mai 09, Heure d'ete britannique
The association has been shutdown.

この記事に対するコメントをご投稿ください。また、SCTP開発メーリング・リストまでお気軽にご連絡ください。

著者について

Chris Hegartyは、アイルランドにあるSun Microsystemsのソフトウェア・エンジニアです。 余暇は、スーパーバイクのライディングを楽しんでいます。