Stream Control Transport Protocol (SCTP) in Java


Stream Control Transport Protocol (SCTP) in Java

By Chris Hegarty, June 2009


SCTP 已經在JDK7中被批准實現了。相關的API和參考實現在sctp openjdk project. 該項目被集成到了JDK7的里程碑3裏面,後續版本都支持SCTP。

SCTP簡介

SCTP是一個可靠的,面向消息的傳輸協議。在TCP/IP協議中和UDP/TCP處於同一層。SCTP是基於sesstion的,要先建立端點之間的連接,然後傳輸數據。

SCTP支持多址的,也就是說一個端點可以由多個地址表示,每個地址可以用來傳輸數據,從而提供了網絡的冗餘。端點之間可以在建立連接的時候,交換多個地址。其中一個地址是主地址,也是向對端傳輸數據的默認地址。一個端口代表一個特定的session。

SCTP是基於消息的。每一個association支持多個獨立的邏輯流。每個流代表了一系列順序消息。每個流都是相互獨立的,也就是流的唯一標識和序列號在數據包中存在,從而使得每個流中的消息都是順序傳遞的。

SCTP的關鍵特性

  • Message framing 消息幀
  • Reliable transport service可靠傳輸
  • Session-oriented 基於session的
  • Ordered and unordered message delivery 順序和無順序的消息傳遞
  • Multi-Homing 多址
    • Association between exactly two endpoints 每兩個端點間有association
    • Each endpoint may be represented by multiple IP addresses 每個端點可以由多個IP地址
    • Provides failover and redundancy 提供了冗餘
  • Multi-Streaming 多流
    • Data partitioned into multiple streams 數據被分成多個流傳輸
    • Independent sequenced delivery 獨立順序傳遞
  • Eliminates head-of-line blocking 消除了??

Support for SCTP in JDK 7

SCTP API是基於NIO設計的,因此可以利用非阻塞的複用I/O. 引入了一個新的包com.sun.nio.sctp。包名字不是java.nio.channels.sctp,這意味着這些API是完全公開的。當這些API成熟後,再引入到標準API中。

這個包中的主要類有三個新channel類型。可以分爲兩個邏輯組。

1 第一個組和TCP類似,包括了sctpChannel 和 SctpServerChannel。一個SctpChannel只能控制一個association,也就是說只能和一個端點發送接受數據。 SctpSeverChannel監聽和接受在socket地址上的接入。

2 第二組包括了SctpMultiChannel。這個類可以控制多個association,因此可以和多個端點傳送數據。

SCTP是事件驅動的。程序能接收到特定的SCTP事件。這些事件尤其實在SctpMultiChannel有用。SctpMultiChannel可以控制多個association,所以需要跟蹤每個association的狀態。例如,當收到AssociationChangNotification的時候,表示有個新的連接的association或者斷開。如果association支持動態地址配置,PeerAddressChangeNotification表示IP地址在對端剛剛增加或者刪除。

Multi-Streaming Example

這個例子展示了多個流。服務器端視線了一個時間的協議,它把當前日期時間在一個流中以英語形式傳遞,一個流中以法語形式傳遞。爲了簡便,本程序沒有考慮異常處理。

Multilingual DayTime Server

Here is the source code for 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();
        }
    }
}
       

Multilingual DayTime Client

Here is the source code for 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<printstream>
    {
        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;
        }
    }
}

Sample Output

Following is an example of the output you might get:

>: 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.

As well as posting comments on this article, please feel free to email the sctp development mailing list.

About the Author

Chris Hegarty is a software engineer at Sun Microsystems in Ireland. In his spare time, he rides a superbike.

Rate This Article
 

Comments

Terms of Use


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章