4927640: Implementation of the sctp protocol

An implementation-specific API for the Stream Control Transmission Protocol

Reviewed-by: alanb, michaelm, jccollet
This commit is contained in:
Chris Hegarty 2009-04-16 17:42:00 +01:00
parent 6e1d4b9a47
commit 26ae13fe80
63 changed files with 12920 additions and 2 deletions

View File

@ -41,7 +41,7 @@ endif
# Omit mirror since it's built with the apt tool.
SUBDIRS = $(SCRIPT_SUBDIR) image security crypto/provider jndi jmx \
java inputmethods org xml rowset net/httpserver net/ssl demo \
tools jarsigner tracing servicetag
tools jarsigner tracing servicetag nio
all build clean clobber::
$(SUBDIRS-loop)

View File

@ -0,0 +1,38 @@
#
# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Sun designates this
# particular file as subject to the "Classpath" exception as provided
# by Sun in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
# CA 95054 USA or visit www.sun.com if you need additional information or
# have any questions.
#
#
# Makefile for com.sun.nio packages.
#
BUILDDIR = ../../..
include $(BUILDDIR)/common/Defs.gmk
SUBDIRS = sctp
all build clean clobber::
$(SUBDIRS-loop)
clean clobber::
$(RM) -r $(CLASSDESTDIR)/com/sun/nio

View File

@ -0,0 +1,39 @@
#
# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Sun designates this
# particular file as subject to the "Classpath" exception as provided
# by Sun in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
# CA 95054 USA or visit www.sun.com if you need additional information or
# have any questions.
#
#
# These are the names of Java classes for which we will make .h files.
#
ifneq ($(PLATFORM), windows)
FILES_export = \
sun/nio/ch/SctpAssocChange.java \
sun/nio/ch/SctpChannelImpl.java \
sun/nio/ch/SctpNet.java \
sun/nio/ch/SctpPeerAddrChange.java \
sun/nio/ch/SctpResultContainer.java \
sun/nio/ch/SctpServerChannelImpl.java \
sun/nio/ch/SctpStdSocketOption.java
endif

View File

@ -0,0 +1,31 @@
#
# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Sun designates this
# particular file as subject to the "Classpath" exception as provided
# by Sun in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
# CA 95054 USA or visit www.sun.com if you need additional information or
# have any questions.
#
ifneq ($(PLATFORM),windows)
FILES_c = \
SctpNet.c \
SctpChannelImpl.c \
SctpServerChannelImpl.c
endif

View File

@ -0,0 +1,67 @@
#
# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Sun designates this
# particular file as subject to the "Classpath" exception as provided
# by Sun in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
# CA 95054 USA or visit www.sun.com if you need additional information or
# have any questions.
#
FILES_java = \
com/sun/nio/sctp/AbstractNotificationHandler.java \
com/sun/nio/sctp/Association.java \
com/sun/nio/sctp/AssociationChangeNotification.java \
com/sun/nio/sctp/HandlerResult.java \
com/sun/nio/sctp/IllegalReceiveException.java \
com/sun/nio/sctp/IllegalUnbindException.java \
com/sun/nio/sctp/InvalidStreamException.java \
com/sun/nio/sctp/MessageInfo.java \
com/sun/nio/sctp/Notification.java \
com/sun/nio/sctp/NotificationHandler.java \
com/sun/nio/sctp/PeerAddressChangeNotification.java \
com/sun/nio/sctp/SctpChannel.java \
com/sun/nio/sctp/SctpMultiChannel.java \
com/sun/nio/sctp/SctpServerChannel.java \
com/sun/nio/sctp/SctpSocketOption.java \
com/sun/nio/sctp/SctpStandardSocketOption.java \
com/sun/nio/sctp/SendFailedNotification.java \
com/sun/nio/sctp/ShutdownNotification.java \
\
sun/nio/ch/SctpMessageInfoImpl.java \
sun/nio/ch/SctpStdSocketOption.java
ifneq ($(PLATFORM), windows)
FILES_java += \
sun/nio/ch/SctpAssocChange.java \
sun/nio/ch/SctpAssociationImpl.java \
sun/nio/ch/SctpChannelImpl.java \
sun/nio/ch/SctpMultiChannelImpl.java \
sun/nio/ch/SctpNet.java \
sun/nio/ch/SctpNotification.java \
sun/nio/ch/SctpPeerAddrChange.java \
sun/nio/ch/SctpResultContainer.java \
sun/nio/ch/SctpSendFailed.java \
sun/nio/ch/SctpServerChannelImpl.java \
sun/nio/ch/SctpShutdown.java \
sun/nio/ch/SctpSocketDispatcher.java
else
FILES_java += \
sun/nio/ch/SctpChannelImpl.java \
sun/nio/ch/SctpMultiChannelImpl.java \
sun/nio/ch/SctpServerChannelImpl.java
endif

View File

@ -0,0 +1,80 @@
#
# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Sun designates this
# particular file as subject to the "Classpath" exception as provided
# by Sun in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
# CA 95054 USA or visit www.sun.com if you need additional information or
# have any questions.
#
#
# Makefile for com.sun.nio.sctp
#
BUILDDIR = ../../../..
PACKAGE = com.sun.nio.sctp
LIBRARY = sctp
PRODUCT = sun
#OTHER_JAVACFLAGS += -Xmaxwarns 1000 -Xlint
include $(BUILDDIR)/common/Defs.gmk
#
# Files to compile
#
include FILES_c.gmk
include FILES_java.gmk
include Exportedfiles.gmk
ifneq ($(PLATFORM), windows)
include $(BUILDDIR)/common/Mapfile-vers.gmk
include $(BUILDDIR)/common/Library.gmk
#
# Find platform-specific C source files
#
vpath %.c $(PLATFORM_SRC)/native/sun/nio/ch
#
# Include nio.h, net_util.h, sun_nio_ch_IOStatus.h, etc
#
OTHER_INCLUDES += \
-I$(SHARE_SRC)/native/sun/nio/ch \
-I$(SHARE_SRC)/native/java/net \
-I$(PLATFORM_SRC)/native/java/net \
-I$(CLASSHDRDIR)/../../../../java/java.nio/nio/CClassHeaders
ifeq ($(PLATFORM), linux)
COMPILER_WARNINGS_FATAL=true
#OTHER_LDLIBS += -L$(LIBDIR)/$(LIBARCH) -ljava -lnet -lpthread -ldl
OTHER_LDLIBS += -L$(LIBDIR)/$(LIBARCH) -lnio -lnet -lpthread -ldl
endif
ifeq ($(PLATFORM), solaris)
#LIBSCTP = -lsctp
OTHER_LDLIBS += $(LIBSOCKET) -L$(LIBDIR)/$(LIBARCH) -lnet -lnio
endif # PLATFORM
else # windows
include $(BUILDDIR)/common/Classes.gmk
endif # ifneq windows
clean clobber::
$(RM) -r $(CLASSDESTDIR)/com/sun/nio/sctp
$(RM) -r $(CLASSDESTDIR)/sun/nio/ch

View File

@ -0,0 +1,49 @@
#
# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Sun designates this
# particular file as subject to the "Classpath" exception as provided
# by Sun in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
# CA 95054 USA or visit www.sun.com if you need additional information or
# have any questions.
#
SUNWprivate_1.1 {
global:
Java_sun_nio_ch_SctpNet_socket0;
Java_sun_nio_ch_SctpNet_bindx;
Java_sun_nio_ch_SctpNet_getLocalAddresses0;
Java_sun_nio_ch_SctpNet_getRemoteAddresses0;
Java_sun_nio_ch_SctpNet_getPrimAddrOption0;
Java_sun_nio_ch_SctpNet_setPrimAddrOption0;
Java_sun_nio_ch_SctpNet_setPeerPrimAddrOption0;
Java_sun_nio_ch_SctpNet_getInitMsgOption0;
Java_sun_nio_ch_SctpNet_setInitMsgOption0;
Java_sun_nio_ch_SctpNet_getIntOption0;
Java_sun_nio_ch_SctpNet_setIntOption0;
Java_sun_nio_ch_SctpNet_shutdown0;
Java_sun_nio_ch_SctpChannelImpl_initIDs;
Java_sun_nio_ch_SctpChannelImpl_checkConnect;
Java_sun_nio_ch_SctpChannelImpl_receive0;
Java_sun_nio_ch_SctpChannelImpl_send0;
Java_sun_nio_ch_SctpServerChannelImpl_initIDs;
Java_sun_nio_ch_SctpServerChannelImpl_accept0;
JNI_OnLoad;
local:
*;
};

View File

@ -86,6 +86,8 @@ TREEAPI_PKGS = com.sun.source.tree \
SMARTCARDIO_PKGS = javax.smartcardio
SCTPAPI_PKGS = com.sun.nio.sctp
TRACING_PKGS = com.sun.tracing \
com.sun.tracing.dtrace
@ -98,4 +100,6 @@ NON_CORE_PKGS = $(DOMAPI_PKGS) \
$(OLD_JSSE_PKGS) \
$(HTTPSERVER_PKGS) \
$(SMARTCARDIO_PKGS) \
$(TRACING_PKGS)
$(TRACING_PKGS) \
$(SCTPAPI_PKGS)

View File

@ -189,6 +189,7 @@ SUNWprivate_1.1 {
Java_sun_nio_fs_UnixNativeDispatcher_getgrnam0;
Java_sun_nio_fs_UnixNativeDispatcher_getextmntent;
Java_sun_nio_fs_UnixCopyFile_transfer;
handleSocketError;
local:
*;

View File

@ -175,6 +175,7 @@ SUNWprivate_1.1 {
Java_sun_nio_fs_SolarisWatchService_portDissociate;
Java_sun_nio_fs_SolarisWatchService_portSend;
Java_sun_nio_fs_SolarisWatchService_portGetn;
handleSocketError;
local:
*;

View File

@ -0,0 +1,140 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.nio.sctp;
/**
* A skeletal handler that consumes notifications and continues.
*
* <P> This class trivially implements the {@code handleNotification} methods to
* return {@link HandlerResult#CONTINUE CONTINUE} so that all notifications are
* consumed and the channel continues to try and receive a message.
*
* <P> It also provides overloaded versions of the {@code handleNotification}
* methods, one for each of the required supported notification types, {@link
* AssociationChangeNotification}, {@link PeerAddressChangeNotification},
* {@link SendFailedNotification}, and {@link ShutdownNotification}. The
* appropriate method will be invoked when the notification is received.
*
* @since 1.7
*/
public class AbstractNotificationHandler<T>
implements NotificationHandler<T>
{
/**
* Initializes a new instance of this class.
*/
protected AbstractNotificationHandler() {}
/**
* Invoked when an implementation specific notification is received from the
* SCTP stack.
*
* @param notification
* The notification
*
* @param attachment
* The object attached to the {@code receive} operation when it was
* initiated.
*
* @return The handler result
*/
@Override
public HandlerResult handleNotification(Notification notification,
T attachment) {
return HandlerResult.CONTINUE;
}
/**
* Invoked when an {@link AssociationChangeNotification} is received from
* the SCTP stack.
*
* @param notification
* The notification
*
* @param attachment
* The object attached to the {@code receive} operation when it was
* initiated.
*
* @return The handler result
*/
public HandlerResult handleNotification(AssociationChangeNotification notification,
T attachment) {
return HandlerResult.CONTINUE;
}
/**
* Invoked when an {@link PeerAddressChangeNotification} is received from
* the SCTP stack.
*
* @param notification
* The notification
*
* @param attachment
* The object attached to the {@code receive} operation when it was
* initiated.
*
* @return The handler result
*/
public HandlerResult handleNotification(PeerAddressChangeNotification notification,
T attachment) {
return HandlerResult.CONTINUE;
}
/**
* Invoked when an {@link SendFailedNotification} is received from
* the SCTP stack.
*
* @param notification
* The notification
*
* @param attachment
* The object attached to the {@code receive} operation when it was
* initiated.
*
* @return The handler result
*/
public HandlerResult handleNotification(SendFailedNotification notification,
T attachment) {
return HandlerResult.CONTINUE;
}
/**
* Invoked when an {@link ShutdownNotification} is received from
* the SCTP stack.
*
* @param notification
* The notification
*
* @param attachment
* The object attached to the {@code receive} operation when it was
* initiated.
*
* @return The handler result
*/
public HandlerResult handleNotification(ShutdownNotification notification,
T attachment) {
return HandlerResult.CONTINUE;
}
}

View File

@ -0,0 +1,104 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.nio.sctp;
/**
* A class that represents an SCTP association.
*
* <P> An association exists between two SCTP endpoints. Each endpoint is
* represented by a list of transport addresses through which that endpoint can
* be reached and from which it will originate SCTP messages. The association
* spans over all of the possible source/destination combinations which may be
* generated from each endpoint's lists of addresses.
*
* <P> Associations are identified by their Association ID.
* Association ID's are guaranteed to be unique for the lifetime of the
* association. An association ID may be reused after the association has been
* shutdown. An association ID is not unique across multiple SCTP channels.
* An Association's local and remote addresses may change if the SCTP
* implementation supports <I>Dynamic Address Reconfiguration</I> as defined by
* <A HREF="http://tools.ietf.org/html/rfc5061">RFC5061</A>, see the
* {@code bindAddress} and {@code unbindAddress} methods of {@link SctpChannel},
* {@link SctpServerChannel}, and {@link SctpMultiChannel}.
*
* <P> An {@code Association} is returned from an {@link
* SctpChannel#association SctpChannel} or an {@link
* SctpMultiChannel#associations SctpMultiChannel}, as well
* as being given as a parameter to {@link NotificationHandler
* NotificationHandler} methods.
*
* @since 1.7
*/
public class Association {
private final int associationID;
private final int maxInStreams;
private final int maxOutStreams;
/**
* Initializes a new instance of this class.
*/
protected Association(int associationID,
int maxInStreams,
int maxOutStreams) {
this.associationID = associationID;
this.maxInStreams = maxInStreams;
this.maxOutStreams = maxOutStreams;
}
/**
* Returns the associationID.
*
* @return The association ID
*/
public final int associationID() {
return associationID;
};
/**
* Returns the maximum number of inbound streams that this association
* supports.
*
* <P> Data received on this association will be on stream number
* {@code s}, where {@code 0 <= s < maxInboundStreams()}.
*
* @return The maximum number of inbound streams
*/
public final int maxInboundStreams() {
return maxInStreams;
};
/**
* Returns the maximum number of outbound streams that this association
* supports.
*
* <P> Data sent on this association must be on stream number
* {@code s}, where {@code 0 <= s < maxOutboundStreams()}.
*
* @return The maximum number of outbound streams
*/
public final int maxOutboundStreams() {
return maxOutStreams;
};
}

View File

@ -0,0 +1,92 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.nio.sctp;
/**
* Notification emitted when an association has either opened or closed.
*
* @since 1.7
*/
public abstract class AssociationChangeNotification
implements Notification
{
/**
* Defines the type of change event that happened to the association.
*
* @since 1.7
*/
public enum AssocChangeEvent
{
/**
* A new association is now ready and data may be exchanged with this peer.
*/
COMM_UP,
/**
* The association has failed. A series of SCTP send failed notifications
* will follow this notification, one for each outstanding message.
*/
COMM_LOST,
/**
* SCTP has detected that the peer has restarted.
*/
RESTART,
/**
* The association has gracefully closed.
*/
SHUTDOWN,
/**
* The association failed to setup. If a message was sent on a {@link
* SctpMultiChannel} in non-blocking mode, an
* SCTP send failed notification will follow this notification for the
* outstanding message.
*/
CANT_START
}
/**
* Initializes a new instance of this class.
*/
protected AssociationChangeNotification() {}
/**
* Returns the association that this notification is applicable to.
*
* @return The association whose state has changed, or {@code null} if
* there is no association, that is {@linkplain
* AssocChangeEvent#CANT_START CANT_START}
*/
public abstract Association association();
/**
* Returns the type of change event.
*
* @return The event
*/
public abstract AssocChangeEvent event();
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.nio.sctp;
/**
* Defines notification handler results.
*
* <P> The {@code HandlerResult} is used to determine the behavior of the
* channel after it handles a notification from the SCTP stack. Essentially its
* value determines if the channel should try to receive another notificaiton or
* a message before returning.
*
* @since 1.7
*/
public enum HandlerResult {
/**
* Try to receieve another message or notification.
*/
CONTINUE,
/**
* Return without trying to receive any more data.
*/
RETURN;
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.nio.sctp;
/**
* Unchecked exception thrown when an attempt is made to invoke the
* {@code receive} method of {@link SctpChannel} or {@link SctpMultiChannel}
* from a notification handler.
*
* @since 1.7
*/
public class IllegalReceiveException extends IllegalStateException {
private static final long serialVersionUID = 2296619040988576224L;
/**
* Constructs an instance of this class.
*/
public IllegalReceiveException() { }
/**
* Constructs an instance of this class with the specified message.
*/
public IllegalReceiveException(String msg) {
super(msg);
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.nio.sctp;
/**
* Unchecked exception thrown when an attempt is made to remove an
* address that is not bound to the channel, or remove an address from a
* channel that has only one address bound to it.
*
* @since 1.7
*/
public class IllegalUnbindException extends IllegalStateException {
private static final long serialVersionUID = -310540883995532224L;
/**
* Constructs an instance of this class.
*/
public IllegalUnbindException() { }
/**
* Constructs an instance of this class with the specified detailed message.
*/
public IllegalUnbindException(String msg) {
super(msg);
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.nio.sctp;
/**
* Unchecked exception thrown when an attempt is made to send a
* message to an invalid stream.
*
* @since 1.7
*/
public class InvalidStreamException extends IllegalArgumentException {
private static final long serialVersionUID = -9172703378046665558L;
/**
* Constructs an instance of this class.
*/
public InvalidStreamException() { }
/**
* Constructs an instance of this class with the specified detailed message.
*/
public InvalidStreamException(String msg) {
super(msg);
}
}

View File

@ -0,0 +1,303 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.nio.sctp;
import java.net.SocketAddress;
/**
* The {@code MessageInfo} class provides additional ancillary information about
* messages.
*
* <P> Received SCTP messages, returned by
* {@link SctpChannel#receive SctpChannel.receive} and {@link
* SctpMultiChannel#receive SctpMultiChannel.receive},
* return a {@code MessageInfo} instance that can be queried to determine
* ancillary information about the received message. Messages being sent should
* use one of the {@link #createOutgoing(java.net.SocketAddress,int)
* createOutgoing} methods to provide ancillary data for the message being
* sent, and may use the appropriate setter methods to override the default
* values provided for {@link #isUnordered() unordered}, {@link #timeToLive()
* timeToLive}, {@link #isComplete() complete} and {@link #payloadProtocolID()
* payloadProtocolID}, before sending the message.
*
* <P> For out going messages the {@code timeToLive} parameter is a time period
* that the sending side SCTP stack may expire the message if it has not been
* sent. This time period is an indication to the stack that the message is no
* longer required to be sent after the time period expires. It is not a hard
* timeout and may be influenced by whether the association supports the partial
* reliability extension, <a href=http://www.ietf.org/rfc/rfc3758.txt>RFC 3758
* <a>
*
* <P> {@code MessageInfo} instances are not safe for use by multiple concurrent
* threads. If a MessageInfo is to be used by more than one thread then access
* to the MessageInfo should be controlled by appropriate synchronization.
*
* @since 1.7
*/
public abstract class MessageInfo {
/**
* Initializes a new instance of this class.
*/
protected MessageInfo() {}
/**
* Creates a {@code MessageInfo} instance suitable for use when
* sending a message.
*
* <P> The returned instance will have its {@link #isUnordered() unordered}
* value set to {@code false}, its {@link #timeToLive() timeToLive} value
* set to {@code 0}, its {@link #isComplete() complete} value set
* to {@code true}, and its {@link #payloadProtocolID() payloadProtocolID}
* value set to {@code 0}. These values, if required, can be set through
* the appropriate setter method before sending the message.
*
* @param address
* For a connected {@code SctpChannel} the address is the
* preferred peer address of the association to send the message
* to, or {@code null} to use the peer primary address. For an
* {@code SctpMultiChannel} the address is used to determine
* the association, or if no association exists with a peer of that
* address then one is setup.
*
* @param streamNumber
* The stream number that the message will be sent on
*
* @return The outgoing message info
*
* @throws IllegalArgumentException
* If the streamNumber is negative or greater than {@code 65536}
*/
public static MessageInfo createOutgoing(SocketAddress address,
int streamNumber) {
if (streamNumber < 0 || streamNumber > 65536)
throw new IllegalArgumentException("Invalid stream number");
return new sun.nio.ch.SctpMessageInfoImpl(null, address, streamNumber);
}
/**
* Creates a {@code MessageInfo} instance suitable for use when
* sending a message to a given association. Typically used for
* {@code SctpMultiChannel} when an association has already been setup.
*
* <P> The returned instance will have its {@link #isUnordered() unordered}
* value set to {@code false}, its {@link #timeToLive() timeToLive} value
* set to {@code 0}, its {@link #isComplete() complete} value set
* to {@code true}, and its {@link #payloadProtocolID() payloadProtocolID}
* value set to {@code 0}. These values, if required, can be set through
* the appropriate setter method before sending the message.
*
* @param association
* The association to send the message on
*
* @param address
* The preferred peer address of the association to send the message
* to, or {@code null} to use the peer primary address
*
* @param streamNumber
* The stream number that the message will be sent on.
*
* @return The outgoing message info
*
* @throws IllegalArgumentException
* If {@code association} is {@code null}, or the streamNumber is
* negative or greater than {@code 65536}
*/
public static MessageInfo createOutgoing(Association association,
SocketAddress address,
int streamNumber) {
if (association == null)
throw new IllegalArgumentException("association cannot be null");
if (streamNumber < 0 || streamNumber > 65536)
throw new IllegalArgumentException("Invalid stream number");
return new sun.nio.ch.SctpMessageInfoImpl(association, address,
streamNumber);
}
/**
* Returns the source socket address if the message has been received,
* otherwise the preferred destination of the message to be sent.
*
* @return The socket address, or {@code null} if this instance is to be
* used for sending a message and has been construced without
* specifying a preferred destination address
*
*/
public abstract SocketAddress address();
/**
* Returns the association that the message was received on, if the message
* has been received, otherwise the association that the message is to be
* sent on.
*
* @return The association, or {@code null} if this instance is to be
* used for sending a message and has been construced using the
* the {@link #createOutgoing(SocketAddress,int)
* createOutgoing(SocketAddress,int)} static factory method
*/
public abstract Association association();
/**
* Returns the number of bytes read for the received message.
*
* <P> This method is only appicable for received messages, it has no
* meaning for messages being sent.
*
* @return The number of bytes read, {@code -1} if the channel is an {@link
* SctpChannel} that has reached end-of-stream, otherwise
* {@code 0}
*/
public abstract int bytes();
/**
* Tells whether or not the message is complete.
*
* <P> For received messages {@code true} indicates that the message was
* completely received. For messages being sent {@code true} indicates that
* the message is complete, {@code false} indicates that the message is not
* complete. How the send channel interprets this value depends on the value
* of its {@link SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE
* SCTP_EXPLICIT_COMPLETE} socket option.
*
* @return {@code true} if, and only if, the message is complete
*/
public abstract boolean isComplete();
/**
* Sets whether or not the message is complete.
*
* <P> For messages being sent {@code true} indicates that
* the message is complete, {@code false} indicates that the message is not
* complete. How the send channel interprets this value depends on the value
* of its {@link SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE
* SCTP_EXPLICIT_COMPLETE} socket option.
*
* @param complete
* {@code true} if, and only if, the message is complete
*
* @return This MessageInfo
*
* @see MessageInfo#isComplete()
*/
public abstract MessageInfo complete(boolean complete);
/**
* Tells whether or not the message is unordered. For received messages
* {@code true} indicates that the message was sent non-ordered. For
* messages being sent {@code true} requests the un-ordered delivery of the
* message, {@code false} indicates that the message is ordered.
*
* @return {@code true} if the message is unordered, otherwise
* {@code false}
*/
public abstract boolean isUnordered();
/**
* Sets whether or not the message is unordered.
*
* @param unordered
* {@code true} requests the un-ordered delivery of the message,
* {@code false} indicates that the message is ordered.
*
* @return This MessageInfo
*
* @see MessageInfo#isUnordered()
*/
public abstract MessageInfo unordered(boolean unordered);
/**
* Returns the payload protocol Identifier.
*
* <P> A value indicating the type of payload protocol data being
* transmitted/received. This value is passed as opaque data by SCTP.
* {@code 0} indicates an unspecified payload protocol identifier.
*
* @return The Payload Protocol Identifier
*/
public abstract int payloadProtocolID();
/**
* Sets the payload protocol Identifier.
*
* <P> A value indicating the type of payload protocol data being
* transmitted. This value is passed as opaque data by SCTP.
*
* @param ppid
* The Payload Protocol Identifier, or {@code 0} indicate an
* unspecified payload protocol identifier.
*
* @return This MessageInfo
*
* @see MessageInfo#payloadProtocolID()
*/
public abstract MessageInfo payloadProtocolID(int ppid);
/**
* Returns the stream number that the message was received on, if the
* message has been received, otherwise the stream number that the message
* is to be sent on.
*
* @return The stream number
*/
public abstract int streamNumber();
/**
* Sets the stream number that the message is to be sent on.
*
* @param streamNumber
* The stream number
*
* @throws IllegalArgumentException
* If the streamNumber is negative or greater than {@code 65536}
*
* @return This MessageInfo
*/
public abstract MessageInfo streamNumber(int streamNumber);
/**
* The time period that the sending side may expire the message if it has
* not been sent, or {@code 0} to indicate that no timeout should occur. This
* value is only applicable for messages being sent, it has no meaning for
* received messages.
*
* @return The time period in milliseconds, or {@code 0}
*/
public abstract long timeToLive();
/**
* Sets the time period that the sending side may expire the message if it
* has not been sent.
*
* @param millis
* The time period in milliseconds, or {@code 0} to indicate that no
* timeout should occur
*
* @return This MessageInfo
*
* @see MessageInfo#timeToLive()
*/
public abstract MessageInfo timeToLive(long millis);
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.nio.sctp;
/**
* A notification from the SCTP stack.
*
* <P> Objects of this type are passed to the {@link NotificationHandler} when
* a notification is received.
*
* <P> An SCTP channel supports the following notifications: {@link
* AssociationChangeNotification}, {@link PeerAddressChangeNotification},
* {@link SendFailedNotification}, {@link ShutdownNotification}, and may support
* additional implementation specific notifications.
*
* @since 1.7
*/
public interface Notification {
/**
* Returns the association that this notification is applicable to.
*/
public Association association();
}

View File

@ -0,0 +1,65 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.nio.sctp;
/**
* A handler for consuming notifications from the SCTP stack.
*
* <P> The SCTP channels defined in this package allow a notification handler to
* be specified to consume notifications from the SCTP stack. When a
* notification is received the {@linkplain #handleNotification
* handleNotification} method of the handler is invoked to handle that
* notification.
*
* <P> Additionally, an attachment object can be attached to the {@code receive}
* operation to provide context when consuming the notification. The
* attachment is important for cases where a <i>state-less</i> {@code
* NotificationHandler} is used to consume the result of many {@code receive}
* operations.
*
* <P> Handler implementations are encouraged to extend the {@link
* AbstractNotificationHandler} class which implements this interface and
* provide notification specific methods. However, an API should generally use
* this handler interface as the type for parameters, return type, etc. rather
* than the abstract class.
*
* @param T The type of the object attached to the receive operation
*
* @since 1.7
*/
public interface NotificationHandler<T> {
/**
* Invoked when a notification is received from the SCTP stack.
*
* @param notification
* The notification
*
* @param attachment
* The object attached to the receive operation when it was initiated.
*
* @return The handler result
*/
HandlerResult handleNotification(Notification notification, T attachment);
}

View File

@ -0,0 +1,107 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.nio.sctp;
import java.net.SocketAddress;
/**
* Notification emitted when a destination address on a multi-homed peer
* encounters a change.
*
* @since 1.7
*/
public abstract class PeerAddressChangeNotification
implements Notification
{
/**
* Defines the type of address change event that occurred to the destination
* address on a multi-homed peer when it encounters a change of interface
* details.
*
* <P> Some of these events types are only generated when the association
* supports dynamic address reconfiguration, e.g. {@code SCTP_ADDR_ADDED},
* {@code SCTP_ADDR_REMOVED}, etc.
*
* @since 1.7
*/
public enum AddressChangeEvent {
/**
* This address is now reachable.
*/
ADDR_AVAILABLE,
/**
* The address specified can no longer be reached. Any data sent to this
* address is rerouted to an alternate until this address becomes reachable.
*/
ADDR_UNREACHABLE,
/**
* The address is no longer part of the association.
*/
ADDR_REMOVED,
/**
* The address is now part of the association.
*/
ADDR_ADDED,
/**
* This address has now been made to be the primary destination address.
*/
ADDR_MADE_PRIMARY,
/**
* This address has now been confirmed as a valid address.
*/
ADDR_CONFIRMED;
}
/**
* Initializes a new instance of this class.
*/
protected PeerAddressChangeNotification() {}
/**
* Returns the peer address.
*
* @return The peer address
*/
public abstract SocketAddress address();
/**
* Returns the association that this notification is applicable to.
*
* @return The association whose peer address changed
*/
public abstract Association association();
/**
* Returns the type of change event.
*
* @return The event
*/
public abstract AddressChangeEvent event();
}

View File

@ -0,0 +1,859 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.nio.sctp;
import java.net.SocketAddress;
import java.net.InetAddress;
import java.io.IOException;
import java.util.Set;
import java.nio.ByteBuffer;
import java.nio.channels.spi.AbstractSelectableChannel;
import java.nio.channels.spi.SelectorProvider;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
/**
* A selectable channel for message-oriented connected SCTP sockets.
*
* <P> An SCTP channel can only control one SCTP association.
* An {@code SCTPChannel} is created by invoking one of the
* {@link #open open} methods of this class. A newly-created channel is open but
* not yet connected, that is, there is no association setup with a remote peer.
* An attempt to invoke an I/O operation upon an unconnected
* channel will cause a {@link java.nio.channels.NotYetConnectedException} to be
* thrown. An association can be setup by connecting the channel using one of
* its {@link #connect connect} methods. Once connected, the channel remains
* connected until it is closed. Whether or not a channel is connected may be
* determined by invoking {@link #getRemoteAddresses getRemoteAddresses}.
*
* <p> SCTP channels support <i>non-blocking connection:</i>&nbsp;A
* channel may be created and the process of establishing the link to
* the remote socket may be initiated via the {@link #connect connect} method
* for later completion by the {@link #finishConnect finishConnect} method.
* Whether or not a connection operation is in progress may be determined by
* invoking the {@link #isConnectionPending isConnectionPending} method.
*
* <p> Socket options are configured using the
* {@link #setOption(SctpSocketOption,Object) setOption} method. An SCTP
* channel support the following options:
* <blockquote>
* <table border>
* <tr>
* <th>Option Name</th>
* <th>Description</th>
* </tr>
* <tr>
* <td> {@link SctpStandardSocketOption#SCTP_DISABLE_FRAGMENTS
* SCTP_DISABLE_FRAGMENTS} </td>
* <td> Enables or disables message fragmentation </td>
* </tr>
* <tr>
* <td> {@link SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE
* SCTP_EXPLICIT_COMPLETE} </td>
* <td> Enables or disables explicit message completion </td>
* </tr>
* <tr>
* <td> {@link SctpStandardSocketOption#SCTP_FRAGMENT_INTERLEAVE
* SCTP_FRAGMENT_INTERLEAVE} </td>
* <td> Controls how the presentation of messages occur for the message
* receiver </td>
* </tr>
* <tr>
* <td> {@link SctpStandardSocketOption#SCTP_INIT_MAXSTREAMS
* SCTP_INIT_MAXSTREAMS} </td>
* <td> The maximum number of streams requested by the local endpoint during
* association initialization </td>
* </tr>
* <tr>
* <td> {@link SctpStandardSocketOption#SCTP_NODELAY SCTP_NODELAY} </td>
* <td> Enables or disable a Nagle-like algorithm </td>
* </tr>
* <tr>
* <td> {@link SctpStandardSocketOption#SCTP_PRIMARY_ADDR
* SCTP_PRIMARY_ADDR} </td>
* <td> Requests that the local SCTP stack use the given peer address as the
* association primary </td>
* </tr>
* <tr>
* <td> {@link SctpStandardSocketOption#SCTP_SET_PEER_PRIMARY_ADDR
* SCTP_SET_PEER_PRIMARY_ADDR} </td>
* <td> Requests that the peer mark the enclosed address as the association
* primary </td>
* </tr>
* <tr>
* <td> {@link SctpStandardSocketOption#SO_SNDBUF
* SO_SNDBUF} </td>
* <td> The size of the socket send buffer </td>
* </tr>
* <tr>
* <td> {@link SctpStandardSocketOption#SO_RCVBUF
* SO_RCVBUF} </td>
* <td> The size of the socket receive buffer </td>
* </tr>
* <tr>
* <td> {@link SctpStandardSocketOption#SO_LINGER
* SO_LINGER} </td>
* <td> Linger on close if data is present (when configured in blocking mode
* only) </td>
* </tr>
* </table>
* </blockquote>
* Additional (implementation specific) options may also be supported. The list
* of options supported is obtained by invoking the {@link #supportedOptions()
* supportedOptions} method.
*
* <p> SCTP channels are safe for use by multiple concurrent threads.
* They support concurrent reading and writing, though at most one thread may be
* reading and at most one thread may be writing at any given time. The
* {@link #connect connect} and {@link #finishConnect
* finishConnect} methods are mutually synchronized against each other, and
* an attempt to initiate a send or receive operation while an invocation of one
* of these methods is in progress will block until that invocation is complete.
*
* @since 1.7
*/
public abstract class SctpChannel
extends AbstractSelectableChannel
{
/**
* Initializes a new instance of this class.
*
* @param provider
* The selector provider for this channel
*/
protected SctpChannel(SelectorProvider provider) {
super(provider);
}
/**
* Opens an SCTP channel.
*
* <P> The new channel is unbound and unconnected.
*
* @return A new SCTP channel
*
* @throws UnsupportedOperationException
* If the SCTP protocol is not supported
*
* @throws IOException
* If an I/O error occurs
*/
public static SctpChannel open() throws
IOException {
return new sun.nio.ch.SctpChannelImpl((SelectorProvider)null);
}
/**
* Opens an SCTP channel and connects it to a remote address.
*
* <P> This is a convenience method and is equivalent to evaluating the
* following expression:
* <blockquote><pre>
* open().connect(remote, maxOutStreams, maxInStreams);
* </pre></blockquote>
*
* @param remote
* The remote address to which the new channel is to be connected
*
* @param maxOutStreams
* The number of streams that the application wishes to be able
* to send to. Must be non negative and no larger than {@code 65536}.
* {@code 0} to use the endpoints default value.
*
* @param maxInStreams
* The maximum number of inbound streams the application is prepared
* to support. Must be non negative and no larger than {@code 65536}.
* {@code 0} to use the endpoints default value.
*
* @return A new SCTP channel connected to the given address
*
* @throws java.nio.channels.AsynchronousCloseException
* If another thread closes this channel
* while the connect operation is in progress
*
* @throws java.nio.channels.ClosedByInterruptException
* If another thread interrupts the current thread
* while the connect operation is in progress, thereby
* closing the channel and setting the current thread's
* interrupt status
*
* @throws java.nio.channels.UnresolvedAddressException
* If the given remote address is not fully resolved
*
* @throws java.nio.channels.UnsupportedAddressTypeException
* If the type of the given remote address is not supported
*
* @throws SecurityException
* If a security manager has been installed
* and it does not permit access to the given remote peer
*
* @throws UnsupportedOperationException
* If the SCTP protocol is not supported
*
* @throws IOException
* If some other I/O error occurs
*/
public static SctpChannel open(SocketAddress remote, int maxOutStreams,
int maxInStreams) throws IOException {
SctpChannel ssc = SctpChannel.open();
ssc.connect(remote, maxOutStreams, maxInStreams);
return ssc;
}
/**
* Returns the association on this channel's socket.
*
* @return the association, or {@code null} if the channel's socket is not
* connected.
*
* @throws ClosedChannelException
* If the channel is closed
*
* @throws IOException
* If some other I/O error occurs
*/
public abstract Association association() throws IOException;
/**
* Binds the channel's socket to a local address.
*
* <P> This method is used to establish a relationship between the socket
* and the local addresses. Once a relationship is established then
* the socket remains bound until the channel is closed. This relationship
* may not necesssarily be with the address {@code local} as it may be removed
* by {@link #unbindAddress unbindAddress}, but there will always be at least
* one local address bound to the channel's socket once an invocation of
* this method successfully completes.
*
* <P> Once the channel's socket has been successfully bound to a specific
* address, that is not automatically assigned, more addresses
* may be bound to it using {@link #bindAddress bindAddress}, or removed
* using {@link #unbindAddress unbindAddress}.
*
* @param local
* The local address to bind the socket, or {@code null} to
* bind the socket to an automatically assigned socket address
*
* @return This channel
*
* @throws java.nio.channels.AlreadyConnectedException
* If this channel is already connected
*
* @throws java.nio.channels.ClosedChannelException
* If this channel is closed
*
* @throws java.nio.channels.ConnectionPendingException
* If a non-blocking connection operation is already in progress on this channel
*
* @throws java.nio.channels.AlreadyBoundException
* If this channel is already bound
*
* @throws java.nio.channels.UnsupportedAddressTypeException
* If the type of the given address is not supported
*
* @throws IOException
* If some other I/O error occurs
*/
public abstract SctpChannel bind(SocketAddress local)
throws IOException;
/**
* Adds the given address to the bound addresses for the channel's
* socket.
*
* <P> The given address must not be the {@link
* java.net.InetAddress#isAnyLocalAddress wildcard} address.
* The channel must be first bound using {@link #bind bind} before
* invoking this method, otherwise {@link
* java.nio.channels.NotYetBoundException} is thrown. The {@link #bind bind}
* method takes a {@code SocketAddress} as its argument which typically
* contains a port number as well as an address. Addresses subquently bound
* using this method are simply addresses as the SCTP port number remains
* the same for the lifetime of the channel.
*
* <P> Adding addresses to a connected association is optional functionality.
* If the endpoint supports dynamic address reconfiguration then it may
* send the appropriate message to the peer to change the peers address
* lists.
*
* @param address
* The address to add to the bound addresses for the socket
*
* @return This channel
*
* @throws java.nio.channels.ClosedChannelException
* If this channel is closed
*
* @throws java.nio.channels.ConnectionPendingException
* If a non-blocking connection operation is already in progress on
* this channel
*
* @throws java.nio.channels.NotYetBoundException
* If this channel is not yet bound
*
* @throws java.nio.channels.AlreadyBoundException
* If this channel is already bound to the given address
*
* @throws IllegalArgumentException
* If address is {@code null} or the {@link
* java.net.InetAddress#isAnyLocalAddress wildcard} address
*
* @throws IOException
* If some other I/O error occurs
*/
public abstract SctpChannel bindAddress(InetAddress address)
throws IOException;
/**
* Removes the given address from the bound addresses for the channel's
* socket.
*
* <P> The given address must not be the {@link
* java.net.InetAddress#isAnyLocalAddress wildcard} address.
* The channel must be first bound using {@link #bind bind} before
* invoking this method, otherwise {@link java.nio.channels.NotYetBoundException}
* is thrown. If this method is invoked on a channel that does not have
* {@code address} as one of its bound addresses or that has only one
* local address bound to it, then this method throws
* {@link IllegalUnbindException}.
* The initial address that the channel's socket is bound to using {@link
* #bind bind} may be removed from the bound addresses for the channel's socket.
*
* <P> Removing addresses from a connected association is optional
* functionality. If the endpoint supports dynamic address reconfiguration
* then it may send the appropriate message to the peer to change the peers
* address lists.
*
* @param address
* The address to remove from the bound addresses for the socket
*
* @return This channel
*
* @throws java.nio.channels.ClosedChannelException
* If this channel is closed
*
* @throws java.nio.channels.ConnectionPendingException
* If a non-blocking connection operation is already in progress on
* this channel
*
* @throws java.nio.channels.NotYetBoundException
* If this channel is not yet bound
*
* @throws IllegalArgumentException
* If address is {@code null} or the {@link
* java.net.InetAddress#isAnyLocalAddress wildcard} address
*
* @throws IllegalUnbindException
* If {@code address} is not bound to the channel's socket. or
* the channel has only one address bound to it
*
* @throws IOException
* If some other I/O error occurs
*/
public abstract SctpChannel unbindAddress(InetAddress address)
throws IOException;
/**
* Connects this channel's socket.
*
* <P> If this channel is in non-blocking mode then an invocation of this
* method initiates a non-blocking connection operation. If the connection
* is established immediately, as can happen with a local connection, then
* this method returns {@code true}. Otherwise this method returns
* {@code false} and the connection operation must later be completed by
* invoking the {@link #finishConnect finishConnect} method.
*
* <P> If this channel is in blocking mode then an invocation of this
* method will block until the connection is established or an I/O error
* occurs.
*
* <P> If a security manager has been installed then this method verifies
* that its {@link java.lang.SecurityManager#checkConnect checkConnect}
* method permits connecting to the address and port number of the given
* remote peer.
*
* <p> This method may be invoked at any time. If a {@link #send send} or
* {@link #receive receive} operation upon this channel is invoked while an
* invocation of this method is in progress then that operation will first
* block until this invocation is complete. If a connection attempt is
* initiated but fails, that is, if an invocation of this method throws a
* checked exception, then the channel will be closed.
*
* @param remote
* The remote peer to which this channel is to be connected
*
* @return {@code true} if a connection was established, {@code false} if
* this channel is in non-blocking mode and the connection
* operation is in progress
*
* @throws java.nio.channels.AlreadyConnectedException
* If this channel is already connected
*
* @throws java.nio.channels.ConnectionPendingException
* If a non-blocking connection operation is already in progress on
* this channel
*
* @throws java.nio.channels.ClosedChannelException
* If this channel is closed
*
* @throws java.nio.channels.AsynchronousCloseException
* If another thread closes this channel
* while the connect operation is in progress
*
* @throws java.nio.channels.ClosedByInterruptException
* If another thread interrupts the current thread
* while the connect operation is in progress, thereby
* closing the channel and setting the current thread's
* interrupt status
*
* @throws java.nio.channels.UnresolvedAddressException
* If the given remote address is not fully resolved
*
* @throws java.nio.channels.UnsupportedAddressTypeException
* If the type of the given remote address is not supported
*
* @throws SecurityException
* If a security manager has been installed
* and it does not permit access to the given remote peer
*
* @throws IOException
* If some other I/O error occurs
*/
public abstract boolean connect(SocketAddress remote) throws IOException;
/**
* Connects this channel's socket.
*
* <P> This is a convience method and is equivalent to evaluating the
* following expression:
* <blockquote><pre>
* setOption(SctpStandardSocketOption.SCTP_INIT_MAXSTREAMS, SctpStandardSocketOption.InitMaxStreams.create(maxInStreams, maxOutStreams))
* .connect(remote);
* </pre></blockquote>
*
* <P> The {@code maxOutStreams} and {@code maxInStreams} parameters
* represent the maximum number of streams that the application wishes to be
* able to send to and receive from. They are negotiated with the remote
* peer and may be limited by the operating system.
*
* @param remote
* The remote peer to which this channel is to be connected
*
* @param maxOutStreams
* Must be non negative and no larger than {@code 65536}.
* {@code 0} to use the endpoints default value.
*
* @param maxInStreams
* Must be non negative and no larger than {@code 65536}.
* {@code 0} to use the endpoints default value.
*
* @return {@code true} if a connection was established, {@code false} if
* this channel is in non-blocking mode and the connection operation
* is in progress
*
* @throws java.nio.channels.AlreadyConnectedException
* If this channel is already connected
*
* @throws java.nio.channels.ConnectionPendingException
* If a non-blocking connection operation is already in progress on
* this channel
*
* @throws java.nio.channels.ClosedChannelException
* If this channel is closed
*
* @throws java.nio.channels.AsynchronousCloseException
* If another thread closes this channel
* while the connect operation is in progress
*
* @throws java.nio.channels.ClosedByInterruptException
* If another thread interrupts the current thread
* while the connect operation is in progress, thereby
* closing the channel and setting the current thread's
* interrupt status
*
* @throws java.nio.channels.UnresolvedAddressException
* If the given remote address is not fully resolved
*
* @throws java.nio.channels.UnsupportedAddressTypeException
* If the type of the given remote address is not supported
*
* @throws SecurityException
* If a security manager has been installed
* and it does not permit access to the given remote peer
*
* @throws IOException
* If some other I/O error occurs
*/
public abstract boolean connect(SocketAddress remote,
int maxOutStreams,
int maxInStreams)
throws IOException;
/**
* Tells whether or not a connection operation is in progress on this channel.
*
* @return {@code true} if, and only if, a connection operation has been initiated
* on this channel but not yet completed by invoking the
* {@link #finishConnect} method
*/
public abstract boolean isConnectionPending();
/**
* Finishes the process of connecting an SCTP channel.
*
* <P> A non-blocking connection operation is initiated by placing a socket
* channel in non-blocking mode and then invoking one of its {@link #connect
* connect} methods. Once the connection is established, or the attempt has
* failed, the channel will become connectable and this method may
* be invoked to complete the connection sequence. If the connection
* operation failed then invoking this method will cause an appropriate
* {@link java.io.IOException} to be thrown.
*
* <P> If this channel is already connected then this method will not block
* and will immediately return <tt>true</tt>. If this channel is in
* non-blocking mode then this method will return <tt>false</tt> if the
* connection process is not yet complete. If this channel is in blocking
* mode then this method will block until the connection either completes
* or fails, and will always either return <tt>true</tt> or throw a checked
* exception describing the failure.
*
* <P> This method may be invoked at any time. If a {@link #send send} or {@link #receive receive}
* operation upon this channel is invoked while an invocation of this
* method is in progress then that operation will first block until this
* invocation is complete. If a connection attempt fails, that is, if an
* invocation of this method throws a checked exception, then the channel
* will be closed.
*
* @return {@code true} if, and only if, this channel's socket is now
* connected
*
* @throws java.nio.channels.NoConnectionPendingException
* If this channel is not connected and a connection operation
* has not been initiated
*
* @throws java.nio.channels.ClosedChannelException
* If this channel is closed
*
* @throws java.nio.channels.AsynchronousCloseException
* If another thread closes this channel
* while the connect operation is in progress
*
* @throws java.nio.channels.ClosedByInterruptException
* If another thread interrupts the current thread
* while the connect operation is in progress, thereby
* closing the channel and setting the current thread's
* interrupt status
*
* @throws IOException
* If some other I/O error occurs
*/
public abstract boolean finishConnect() throws IOException;
/**
* Returns all of the socket addresses to which this channel's socket is
* bound.
*
* @return All the socket addresses that this channel's socket is
* bound to, or an empty {@code Set} if the channel's socket is not
* bound
*
* @throws ClosedChannelException
* If the channel is closed
*
* @throws IOException
* If an I/O error occurs
*/
public abstract Set<SocketAddress> getAllLocalAddresses()
throws IOException;
/**
* Returns all of the remote addresses to which this channel's socket
* is connected.
*
* <P> If the channel is connected to a remote peer that is bound to
* multiple addresses then it is these addresses that the channel's socket
* is connected.
*
* @return All of the remote addresses to which this channel's socket
* is connected, or an empty {@code Set} if the channel's socket is
* not connected
*
* @throws ClosedChannelException
* If the channel is closed
*
* @throws IOException
* If an I/O error occurs
*/
public abstract Set<SocketAddress> getRemoteAddresses()
throws IOException;
/**
* Shutdown a connection without closing the channel.
*
* <P> Sends a shutdown command to the remote peer, effectively preventing
* any new data from being written to the socket by either peer. Further
* sends will throw {@link java.nio.channels.ClosedChannelException}. The
* channel remains open to allow the for any data (and notifications) to be
* received that may have been sent by the peer before it received the
* shutdown command. If the channel is already shutdown then invoking this
* method has no effect.
*
* @return This channel
*
* @throws java.nio.channels.NotYetConnectedException
* If this channel is not yet connected
*
* @throws java.nio.channels.ClosedChannelException
* If this channel is closed
*
* @throws IOException
* If some other I/O error occurs
*/
public abstract SctpChannel shutdown() throws IOException;
/**
* Returns the value of a socket option.
*
* @param name
* The socket option
*
* @return The value of the socket option. A value of {@code null} may be
* a valid value for some socket options.
*
* @throws UnsupportedOperationException
* If the socket option is not supported by this channel
*
* @throws ClosedChannelException
* If this channel is closed
*
* @throws IOException
* If an I/O error occurs
*
* @see SctpStandardSocketOption
*/
public abstract <T> T getOption(SctpSocketOption<T> name)
throws IOException;
/**
* Sets the value of a socket option.
*
* @param name
* The socket option
*
* @param value
* The value of the socket option. A value of {@code null} may be
* a valid value for some socket options.
*
* @return This channel
*
* @throws UnsupportedOperationException
* If the socket option is not supported by this channel
*
* @throws IllegalArgumentException
* If the value is not a valid value for this socket option
*
* @throws ClosedChannelException
* If this channel is closed
*
* @throws IOException
* If an I/O error occurs
*
* @see SctpStandardSocketOption
*/
public abstract <T> SctpChannel setOption(SctpSocketOption<T> name, T value)
throws IOException;
/**
* Returns a set of the socket options supported by this channel.
*
* <P> This method will continue to return the set of options even after the
* channel has been closed.
*
* @return A set of the socket options supported by this channel
*/
public abstract Set<SctpSocketOption<?>> supportedOptions();
/**
* Returns an operation set identifying this channel's supported operations.
*
* <P> SCTP channels support connecting, reading, and writing, so this
* method returns <tt>(</tt>{@link SelectionKey#OP_CONNECT}
* <tt>|</tt>&nbsp;{@link SelectionKey#OP_READ} <tt>|</tt>&nbsp;{@link
* SelectionKey#OP_WRITE}<tt>)</tt>. </p>
*
* @return The valid-operation set
*/
@Override
public final int validOps() {
return (SelectionKey.OP_READ |
SelectionKey.OP_WRITE |
SelectionKey.OP_CONNECT);
}
/**
* Receives a message into the given buffer and/or handles a notification.
*
* <P> If a message or notification is immediately available, or if this
* channel is in blocking mode and one eventually becomes available, then
* the message or notification is returned or handled, respectively. If this
* channel is in non-blocking mode and a message or notification is not
* immediately available then this method immediately returns {@code null}.
*
* <P> If this method receives a message it is copied into the given byte
* buffer. The message is transferred into the given byte buffer starting at
* its current position and the buffers position is incremented by the
* number of bytes read. If there are fewer bytes remaining in the buffer
* than are required to hold the message, or the underlying input buffer
* does not contain the complete message, then an invocation of {@link
* MessageInfo#isComplete isComplete} on the returned {@code
* MessageInfo} will return {@code false}, and more invocations of this
* method will be necessary to completely consume the messgae. Only
* one message at a time will be partially delivered in any stream. The
* socket option {@link SctpStandardSocketOption#SCTP_FRAGMENT_INTERLEAVE
* SCTP_FRAGMENT_INTERLEAVE} controls various aspects of what interlacing of
* messages occurs.
*
* <P> If this method receives a notification then the appropriate method of
* the given handler, if there is one, is invoked. If the handler returns
* {@link HandlerResult#CONTINUE CONTINUE} then this method will try to
* receive another message/notification, otherwise, if {@link
* HandlerResult#RETURN RETURN} is returned this method will return {@code
* null}. If an uncaught exception is thrown by the handler it will be
* propagated up the stack through this method.
*
* <P> This method may be invoked at any time. If another thread has
* already initiated a receive operation upon this channel, then an
* invocation of this method will block until the first operation is
* complete. The given handler is invoked without holding any locks used
* to enforce the above synchronization policy, that way handlers
* will not stall other threads from receiving. A handler should not invoke
* the {@code receive} method of this channel, if it does an
* {@link IllegalReceiveException} will be thrown.
*
* @param dst
* The buffer into which message bytes are to be transferred
*
* @param attachment
* The object to attach to the receive operation; can be
* {@code null}
*
* @param handler
* A handler to handle notifications from the SCTP stack, or {@code
* null} to ignore any notifications.
*
* @return The {@code MessageInfo}, {@code null} if this channel is in
* non-blocking mode and no messages are immediately available or
* the notification handler returns {@link HandlerResult#RETURN
* RETURN} after handling a notification
*
* @throws java.nio.channels.ClosedChannelException
* If this channel is closed
*
* @throws java.nio.channels.AsynchronousCloseException
* If another thread closes this channel
* while the read operation is in progress
*
* @throws java.nio.channels.ClosedByInterruptException
* If another thread interrupts the current thread
* while the read operation is in progress, thereby
* closing the channel and setting the current thread's
* interrupt status
*
* @throws java.nio.channels.NotYetConnectedException
* If this channel is not yet connected
*
* @throws IllegalReceiveException
* If the given handler invokes the {@code receive} method of this
* channel
*
* @throws IOException
* If some other I/O error occurs
*/
public abstract <T> MessageInfo receive(ByteBuffer dst,
T attachment,
NotificationHandler<T> handler)
throws IOException;
/**
* Sends a message via this channel.
*
* <P> If this channel is in non-blocking mode and there is sufficient room
* in the underlying output buffer, or if this channel is in blocking mode
* and sufficient room becomes available, then the remaining bytes in the
* given byte buffer are transmitted as a single message. Sending a message
* is atomic unless explicit message completion {@link
* SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE SCTP_EXPLICIT_COMPLETE}
* socket option is enabled on this channel's socket.
*
* <P> The message is transferred from the byte buffer as if by a regular
* {@link java.nio.channels.WritableByteChannel#write(java.nio.ByteBuffer)
* write} operation.
*
* <P> The bytes will be written to the stream number that is specified by
* {@link MessageInfo#streamNumber streamNumber} in the given {@code
* messageInfo}.
*
* <P> This method may be invoked at any time. If another thread has already
* initiated a send operation upon this channel, then an invocation of
* this method will block until the first operation is complete.
*
* @param src
* The buffer containing the message to be sent
*
* @param messageInfo
* Ancillary data about the message to be sent
*
* @return The number of bytes sent, which will be either the number of
* bytes that were remaining in the messages buffer when this method
* was invoked or, if this channel is non-blocking, may be zero if
* there was insufficient room for the message in the underlying
* output buffer
*
* @throws InvalidStreamExcepton
* If {@code streamNumner} is negative or greater than or equal to
* the maximum number of outgoing streams
*
* @throws java.nio.channels.ClosedChannelException
* If this channel is closed
*
* @throws java.nio.channels.AsynchronousCloseException
* If another thread closes this channel
* while the read operation is in progress
*
* @throws java.nio.channels.ClosedByInterruptException
* If another thread interrupts the current thread
* while the read operation is in progress, thereby
* closing the channel and setting the current thread's
* interrupt status
*
* @throws java.nio.channels.NotYetConnectedException
* If this channel is not yet connected
*
* @throws IOException
* If some other I/O error occurs
*/
public abstract int send(ByteBuffer src, MessageInfo messageInfo)
throws IOException;
}

View File

@ -0,0 +1,731 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.nio.sctp;
import java.net.SocketAddress;
import java.net.InetAddress;
import java.io.IOException;
import java.util.Set;
import java.nio.ByteBuffer;
import java.nio.channels.spi.AbstractSelectableChannel;
import java.nio.channels.spi.SelectorProvider;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.NotYetBoundException;
import java.nio.channels.SelectionKey;
/**
* A selectable channel for message-oriented SCTP sockets.
*
* <P> An SCTP multi channel supports many associations on a single socket.
* An {@code SctpMultiChannel} is created by invoking the
* {@link #open open} method of this class. A newly-created channel is open but
* not yet bound. An attempt to invoke the {@link #receive receive} method of an
* unbound channel will cause the {@link NotYetBoundException}
* to be thrown. An attempt to invoke the {@link #send send} method of an
* unbound channel will cause it to first invoke the {@link #bind bind} method.
* The address(es) that the channel's socket is bound to can be retrieved by
* calling {@link #getAllLocalAddresses getAllLocalAddresses}.
*
* <P> Messages may be sent and received without explicitly setting up an
* association with the remote peer. The channel will implicitly setup
* a new association whenever it sends or receives a message from a remote
* peer if there is not already an association with that peer. Upon successful
* association setup, an {@link AssociationChangeNotification
* association changed} notification will be put to the SCTP stack with its
* {@code event} parameter set to {@link
* AssociationChangeNotification.AssocChangeEvent#COMM_UP
* COMM_UP}. This notification can be received by invoking {@link #receive
* receive}.
*
* <P> Socket options are configured using the
* {@link #setOption(SctpSocketOption,Object,Association) setOption} method. An
* {@code SctpMultiChannel} supports the following options:
* <blockquote>
* <table border>
* <tr>
* <th>Option Name</th>
* <th>Description</th>
* </tr>
* <tr>
* <td> {@link SctpStandardSocketOption#SCTP_DISABLE_FRAGMENTS
* SCTP_DISABLE_FRAGMENTS} </td>
* <td> Enables or disables message fragmentation </td>
* </tr>
* <tr>
* <td> {@link SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE
* SCTP_EXPLICIT_COMPLETE} </td>
* <td> Enables or disables explicit message completion </td>
* </tr>
* <tr>
* <td> {@link SctpStandardSocketOption#SCTP_FRAGMENT_INTERLEAVE
* SCTP_FRAGMENT_INTERLEAVE} </td>
* <td> Controls how the presentation of messages occur for the message
* receiver </td>
* </tr>
* <tr>
* <td> {@link SctpStandardSocketOption#SCTP_INIT_MAXSTREAMS
* SCTP_INIT_MAXSTREAMS} </td>
* <td> The maximum number of streams requested by the local endpoint during
* association initialization </td>
* </tr>
* <tr>
* <td> {@link SctpStandardSocketOption#SCTP_NODELAY SCTP_NODELAY} </td>
* <td> Enables or disable a Nagle-like algorithm </td>
* </tr>
* <tr>
* <td> {@link SctpStandardSocketOption#SCTP_PRIMARY_ADDR
* SCTP_PRIMARY_ADDR} </td>
* <td> Requests that the local SCTP stack use the given peer address as the
* association primary </td>
* </tr>
* <tr>
* <td> {@link SctpStandardSocketOption#SCTP_SET_PEER_PRIMARY_ADDR
* SCTP_SET_PEER_PRIMARY_ADDR} </td>
* <td> Requests that the peer mark the enclosed address as the association
* primary </td>
* </tr>
* <tr>
* <td> {@link SctpStandardSocketOption#SO_SNDBUF
* SO_SNDBUF} </td>
* <td> The size of the socket send buffer </td>
* </tr>
* <tr>
* <td> {@link SctpStandardSocketOption#SO_RCVBUF
* SO_RCVBUF} </td>
* <td> The size of the socket receive buffer </td>
* </tr>
* <tr>
* <td> {@link SctpStandardSocketOption#SO_LINGER
* SO_LINGER} </td>
* <td> Linger on close if data is present (when configured in blocking mode
* only) </td>
* </tr>
* </table>
* </blockquote>
* Additional (implementation specific) options may also be supported. The list
* of options supported is obtained by invoking the {@link #supportedOptions()
* supportedOptions} method.
*
* <p> SCTP multi channels are safe for use by multiple concurrent threads.
* They support concurrent sending and receiving, though at most one thread may be
* sending and at most one thread may be receiving at any given time.
*
* @since 1.7
*/
public abstract class SctpMultiChannel
extends AbstractSelectableChannel
{
/**
* Initializes a new instance of this class.
*
* @param provider
* The selector provider for this channel
*/
protected SctpMultiChannel(SelectorProvider provider) {
super(provider);
}
/**
* Opens an SCTP multi channel.
*
* <P> The new channel is unbound.
*
* @return A new SCTP multi channel
*
* @throws UnsupportedOperationException
* If the SCTP protocol is not supported
*
* @throws IOException
* If an I/O error occurs
*/
public static SctpMultiChannel open() throws
IOException {
return new sun.nio.ch.SctpMultiChannelImpl((SelectorProvider)null);
}
/**
* Returns the open associations on this channel's socket.
*
* <P> Only associations whose {@link AssociationChangeNotification.AssocChangeEvent#COMM_UP
* COMM_UP} association change event has been received are included
* in the returned set of associations. Associations for which a
* {@link AssociationChangeNotification.AssocChangeEvent#COMM_LOST COMM_LOST} or {@link
* AssociationChangeNotification.AssocChangeEvent#SHUTDOWN SHUTDOWN} association change
* event have been receive are removed from the set of associations.
*
* <P> The returned set of associations is a snapshot of the open
* associations at the time that this method is invoked.
*
* @return A {@code Set} containing the open associations, or an empty
* {@code Set} if there are none.
*
* @throws ClosedChannelException
* If this channel is closed
*
* @throws IOException
* If some other I/O error occurs
*/
public abstract Set<Association> associations()
throws IOException;
/**
* Binds the channel's socket to a local address and configures the socket
* to listen for connections.
*
* <P> This method is used to establish a relationship between the socket
* and the local address. Once a relationship is established then
* the socket remains bound until the channel is closed. This relationship
* may not necesssarily be with the address {@code local} as it may be removed
* by {@link #unbindAddress unbindAddress}, but there will always be at least one local
* address bound to the channel's socket once an invocation of this method
* successfully completes.
*
* <P> Once the channel's socket has been successfully bound to a specific
* address, that is not automatically assigned, more addresses
* may be bound to it using {@link #bindAddress bindAddress}, or removed
* using {@link #unbindAddress unbindAddress}.
*
* <P> The backlog parameter is the maximum number of pending connections on
* the socket. Its exact semantics are implementation specific. An implementation
* may impose an implementation specific maximum length or may choose to ignore
* the parameter. If the backlog parameter has the value {@code 0}, or a negative
* value, then an implementation specific default is used.
*
* @param local
* The local address to bind the socket, or {@code null} to
* bind the socket to an automatically assigned socket address
*
* @param backlog
* The maximum number number of pending connections
*
* @return This channel
*
* @throws ClosedChannelException
* If this channel is closed
*
* @throws java.nio.channels.AlreadyBoundException
* If this channel is already bound
*
* @throws java.nio.channels.UnsupportedAddressTypeException
* If the type of the given address is not supported
*
* @throws SecurityException
* If a security manager has been installed and its {@link
* java.lang.SecurityManager#checkListen(int) checkListen} method
* denies the operation
*
* @throws IOException
* If some other I/O error occurs
*/
public abstract SctpMultiChannel bind(SocketAddress local,
int backlog)
throws IOException;
/**
* Binds the channel's socket to a local address and configures the socket
* to listen for connections.
*
* <P> This method works as if invoking it were equivalent to evaluating the
* expression:
* <blockquote><pre>
* bind(local, 0);
* </pre></blockquote>
*
* @param local
* The local address to bind the socket, or {@code null} to
* bind the socket to an automatically assigned socket address
*
* @return This channel
*
* @throws ClosedChannelException
* If this channel is closed
*
* @throws java.nio.channels.AlreadyBoundException
* If this channel is already bound
*
* @throws java.nio.channels.UnsupportedAddressTypeException
* If the type of the given address is not supported
*
* @throws SecurityException
* If a security manager has been installed and its {@link
* java.lang.SecurityManager#checkListen(int) checkListen} method
* denies the operation
*
* @throws IOException
* If some other I/O error occurs
*/
public final SctpMultiChannel bind(SocketAddress local)
throws IOException {
return bind(local, 0);
}
/**
* Adds the given address to the bound addresses for the channel's
* socket.
*
* <P> The given address must not be the {@link
* java.net.InetAddress#isAnyLocalAddress wildcard} address.
* The channel must be first bound using {@link #bind bind} before
* invoking this method, otherwise {@link NotYetBoundException} is thrown.
* The {@link #bind bind} method takes a {@code SocketAddress} as its
* argument which typically contains a port number as well as an address.
* Addresses subquently bound using this method are simply addresses as the
* SCTP port number remains the same for the lifetime of the channel.
*
* <P> New associations setup after this method successfully completes
* will be associated with the given address. Adding addresses to existing
* associations is optional functionality. If the endpoint supports
* dynamic address reconfiguration then it may send the appropriate message
* to the peer to change the peers address lists.
*
* @param address
* The address to add to the bound addresses for the socket
*
* @return This channel
*
* @throws ClosedChannelException
* If this channel is closed
*
* @throws NotYetBoundException
* If this channel is not yet bound
*
* @throws java.nio.channels.AlreadyBoundException
* If this channel is already bound to the given address
*
* @throws IllegalArgumentException
* If address is {@code null} or the {@link
* java.net.InetAddress#isAnyLocalAddress wildcard} address
*
* @throws IOException
* If some other I/O error occurs
*/
public abstract SctpMultiChannel bindAddress(InetAddress address)
throws IOException;
/**
* Removes the given address from the bound addresses for the channel's
* socket.
*
* <P> The given address must not be the {@link
* java.net.InetAddress#isAnyLocalAddress wildcard} address.
* The channel must be first bound using {@link #bind bind} before
* invoking this method, otherwise {@link NotYetBoundException} is thrown.
*
* <P> If this method is invoked on a channel that does
* not have {@code address} as one of its bound addresses, or that has only
* one local address bound to it, then this method throws
* {@link IllegalUnbindException}.
*
* <P> The initial address that the channel's socket is bound to using
* {@link #bind bind} may be removed from the bound addresses for the
* channel's socket.
*
* <P> New associations setup after this method successfully completes
* will not be associated with the given address. Removing addresses from
* existing associations is optional functionality. If the endpoint supports
* dynamic address reconfiguration then it may send the appropriate message
* to the peer to change the peers address lists.
*
* @param address
* The address to remove from the bound addresses for the socket
*
* @return This channel
*
* @throws ClosedChannelException
* If this channel is closed
*
* @throws NotYetBoundException
* If this channel is not yet bound
*
* @throws IllegalUnbindException
* {@code address} is not bound to the channel's socket, or the
* channel has only one address bound to it
*
* @throws IllegalArgumentException
* If address is {@code null} or the {@link
* java.net.InetAddress#isAnyLocalAddress wildcard} address
*
* @throws IOException
* If some other I/O error occurs
*/
public abstract SctpMultiChannel unbindAddress(InetAddress address)
throws IOException;
/**
* Returns all of the socket addresses to which this channel's socket is
* bound.
*
* @return All the socket addresses that this channel's socket is
* bound to, or an empty {@code Set} if the channel's socket is not
* bound
*
* @throws ClosedChannelException
* If the channel is closed
*
* @throws IOException
* If an I/O error occurs
*/
public abstract Set<SocketAddress> getAllLocalAddresses()
throws IOException;
/**
* Returns all of the remote addresses to which the given association on
* this channel's socket is connected.
*
* @return All of the remote addresses for the given association, or
* an empty {@code Set} if the association has been shutdown
*
* @throws ClosedChannelException
* If the channel is closed
*
* @throws IOException
* If an I/O error occurs
*/
public abstract Set<SocketAddress> getRemoteAddresses(Association association)
throws IOException;
/**
* Shutdown an association without closing the channel.
*
* @param association
* The association to shutdown
*
* @return This channel
*
* @throws ClosedChannelException
* If this channel is closed
*
* @throws IOException
* If some other I/O error occurs
*/
public abstract SctpMultiChannel shutdown(Association association)
throws IOException;
/**
* Returns the value of a socket option.
*
* <P> Note that some options are retrieved on the channel's socket,
* therefore the {@code association} parameter is not applicable and will be
* ignored if given. However, if the option is association specific then the
* association must be given.
*
* @param name
* The socket option
*
* @param association
* The association whose option should be retrieved, or {@code null}
* if this option should be retrieved at the channel's socket level.
*
* @return The value of the socket option. A value of {@code null} may be
* a valid value for some socket options.
*
* @throws UnsupportedOperationException
* If the socket option is not supported by this channel
*
* @throws ClosedChannelException
* If this channel is closed
*
* @throws IOException
* If an I/O error occurs
*
* @see SctpStandardSocketOption
*/
public abstract <T> T getOption(SctpSocketOption<T> name,
Association association)
throws IOException;
/**
* Sets the value of a socket option.
*
* <P> Note that some options are retrieved on the channel's socket,
* therefore the {@code association} parameter is not applicable and will be
* ignored if given. However, if the option is association specific then the
* association must be given.
*
* @param name
* The socket option
*
* @param association
* The association whose option should be set, or {@code null}
* if this option should be set at the channel's socket level.
*
* @param value
* The value of the socket option. A value of {@code null} may be
* a valid value for some socket options.
*
* @return This channel
*
* @throws UnsupportedOperationException
* If the socket option is not supported by this channel
*
* @throws IllegalArgumentException
* If the value is not a valid value for this socket option
*
* @throws ClosedChannelException
* If this channel is closed
*
* @throws IOException
* If an I/O error occurs
*
* @see SctpStandardSocketOption
*/
public abstract <T> SctpMultiChannel setOption(SctpSocketOption<T> name,
T value,
Association association)
throws IOException;
/**
* Returns a set of the socket options supported by this channel.
*
* <P> This method will continue to return the set of options even after the
* channel has been closed.
*
* @return A set of the socket options supported by this channel
*/
public abstract Set<SctpSocketOption<?>> supportedOptions();
/**
* Returns an operation set identifying this channel's supported operations.
*
* <P> SCTP multi channels support reading, and writing, so this
* method returns
* {@code (}{@link SelectionKey#OP_READ} {@code |}&nbsp;{@link
* SelectionKey#OP_WRITE}{@code )}. </p>
*
* @return The valid-operation set
*/
@Override
public final int validOps() {
return (SelectionKey.OP_READ |
SelectionKey.OP_WRITE );
}
/**
* Receives a message and/or handles a notification via this channel.
*
* <P> If a message or notification is immediately available, or if this
* channel is in blocking mode and one eventually becomes available, then
* the message or notification is returned or handled, respectively. If this
* channel is in non-blocking mode and a message or notification is not
* immediately available then this method immediately returns {@code null}.
*
* <P> If this method receives a message it is copied into the given byte
* buffer and an {@link MessageInfo} is returned.
* The message is transferred into the given byte buffer starting at its
* current position and the buffers position is incremented by the number of
* bytes read. If there are fewer bytes remaining in the buffer than are
* required to hold the message, or the underlying input buffer does not
* contain the complete message, then an invocation of {@link
* MessageInfo#isComplete isComplete} on the returned {@code
* MessageInfo} will return {@code false}, and more invocations of this
* method will be necessary to completely consume the messgae. Only
* one message at a time will be partially delivered in any stream. The
* socket option {@link SctpStandardSocketOption#SCTP_FRAGMENT_INTERLEAVE
* SCTP_FRAGMENT_INTERLEAVE} controls various aspects of what interlacing of
* messages occurs.
*
* <P> If this method receives a notification then the appropriate method of
* the given handler, if there is one, is invoked. If the handler returns {@link
* HandlerResult#CONTINUE CONTINUE} then this method will try to receive another
* message/notification, otherwise, if {@link HandlerResult#RETURN RETURN} is returned
* this method will return {@code null}. If an uncaught exception is thrown by the
* handler it will be propagated up the stack through this method.
*
* <P> If a security manager has been installed then for each new association
* setup this method verifies that the associations source address and port
* number are permitted by the security manager's {@link
* java.lang.SecurityManager#checkAccept(String,int) checkAccept} method.
*
* <P> This method may be invoked at any time. If another thread has
* already initiated a receive operation upon this channel, then an
* invocation of this method will block until the first operation is
* complete. The given handler is invoked without holding any locks used
* to enforce the above synchronization policy, that way handlers
* will not stall other threads from receiving. A handler should not invoke
* the {@code receive} method of this channel, if it does an
* {@link IllegalReceiveException} will be thrown.
*
* @param buffer
* The buffer into which bytes are to be transferred
*
* @param attachment
* The object to attach to the receive operation; can be
* {@code null}
*
* @param handler
* A handler to handle notifications from the SCTP stack, or
* {@code null} to ignore any notifications.
*
* @return The {@code MessageInfo}, {@code null} if this channel is in
* non-blocking mode and no messages are immediately available or
* the notification handler returns {@code RETURN} after handling
* a notification
*
* @throws java.nio.channels.ClosedChannelException
* If this channel is closed
*
* @throws java.nio.channels.AsynchronousCloseException
* If another thread closes this channel
* while the read operation is in progress
*
* @throws java.nio.channels.ClosedByInterruptException
* If another thread interrupts the current thread
* while the read operation is in progress, thereby
* closing the channel and setting the current thread's
* interrupt status
*
* @throws NotYetBoundException
* If this channel is not yet bound
*
* @throws IllegalReceiveException
* If the given handler invokes the {@code receive} method of this
* channel
*
* @throws SecurityException
* If a security manager has been installed and it does not permit
* new associations to be accepted from the message's sender
*
* @throws IOException
* If some other I/O error occurs
*/
public abstract <T> MessageInfo receive(ByteBuffer buffer,
T attachment,
NotificationHandler<T> handler)
throws IOException;
/**
* Sends a message via this channel.
*
* <P> If this channel is unbound then this method will invoke {@link
* #bind(SocketAddress, int) bind(null, 0)} before sending any data.
*
* <P> If there is no association existing between this channel's socket
* and the intended receiver, identified by the address in the given messageInfo, then one
* will be automatically setup to the intended receiver. This is considered
* to be Implicit Association Setup. Upon successful association setup, an
* {@link AssociationChangeNotification association changed}
* notification will be put to the SCTP stack with its {@code event} parameter set
* to {@link AssociationChangeNotification.AssocChangeEvent#COMM_UP COMM_UP}
* . This notification can be received by invoking {@link #receive
* receive}.
*
* <P> If this channel is in blocking mode, there is sufficient room in the
* underlying output buffer, then the remaining bytes in the given byte
* buffer are transmitted as a single message. Sending a message
* is atomic unless explicit message completion {@link
* SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE SCTP_EXPLICIT_COMPLETE}
* socket option is enabled on this channel's socket.
*
* <P> If this channel is in non-blocking mode, there is sufficient room
* in the underlying output buffer, and an implicit association setup is
* required, then the remaining bytes in the given byte buffer are
* transmitted as a single message, subject to {@link
* SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE SCTP_EXPLICIT_COMPLETE}.
* If for any reason the message cannot
* be delivered an {@link AssociationChangeNotification association
* changed} notification is put on the SCTP stack with its {@code event} parameter set
* to {@link AssociationChangeNotification.AssocChangeEvent#CANT_START CANT_START}.
*
* <P> The message is transferred from the byte buffer as if by a regular
* {@link java.nio.channels.WritableByteChannel#write(java.nio.ByteBuffer)
* write} operation.
*
* <P> If a security manager has been installed then for each new association
* setup this method verifies that the given remote peers address and port
* number are permitted by the security manager's {@link
* java.lang.SecurityManager#checkConnect(String,int) checkConnect} method.
*
* <P> This method may be invoked at any time. If another thread has already
* initiated a send operation upon this channel, then an invocation of
* this method will block until the first operation is complete.
*
* @param buffer
* The buffer containing the message to be sent
*
* @param messageInfo
* Ancillary data about the message to be sent
*
* @return The number of bytes sent, which will be either the number of
* bytes that were remaining in the messages buffer when this method
* was invoked or, if this channel is non-blocking, may be zero if
* there was insufficient room for the message in the underlying
* output buffer
*
* @throws InvalidStreamExcepton
* If {@code streamNumber} is negative, or if an association already
* exists and {@code streamNumber} is greater than the maximum number
* of outgoing streams
*
* @throws java.nio.channels.ClosedChannelException
* If this channel is closed
*
* @throws java.nio.channels.AsynchronousCloseException
* If another thread closes this channel
* while the read operation is in progress
*
* @throws java.nio.channels.ClosedByInterruptException
* If another thread interrupts the current thread
* while the read operation is in progress, thereby
* closing the channel and setting the current thread's
* interrupt status
*
* @throws SecurityException
* If a security manager has been installed and it does not permit
* new associations to be setup with the the messages's address
*
* @throws IOException
* If some other I/O error occurs
*/
public abstract int send(ByteBuffer buffer, MessageInfo messageInfo)
throws IOException;
/**
* Branches off an association.
*
* <P> An application can invoke this method to branch off an association
* into a separate channel. The new bound and connected {@link SctpChannel}
* will be created for the association. The branched off association will no
* longer be part of this channel.
*
* <P> This is particularly useful when, for instance, the application
* wishes to have a number of sporadic message senders/receivers remain
* under the original SCTP multi channel but branch off those
* associations carrying high volume data traffic into their own
* separate SCTP channels.
*
* @param association
* The association to branch off
*
* @return The {@code SctpChannel}
*
* @throws java.nio.channels.ClosedChannelException
* If this channel is closed
*
* @throws IOException
* If some other I/O error occurs
*/
public abstract SctpChannel branch(Association association)
throws IOException;
}

View File

@ -0,0 +1,421 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.nio.sctp;
import java.net.SocketAddress;
import java.net.InetAddress;
import java.io.IOException;
import java.util.Set;
import java.nio.channels.SelectionKey;
import java.nio.channels.spi.SelectorProvider;
import java.nio.channels.spi.AbstractSelectableChannel;
/**
* A selectable channel for message-oriented listening SCTP sockets.
*
* <p> An {@code SCTPServerChannel} is created by invoking the
* {@link #open open} method of this class. A newly-created SCTP server
* channel is open but not yet bound. An attempt to invoke the
* {@link #accept accept} method of an unbound channel will cause the
* {@link java.nio.channels.NotYetBoundException} to be thrown. An SCTP server
* channel can be bound by invoking one of the
* {@link #bind(java.net.SocketAddress,int) bind} methods defined by this class.
*
* <p> Socket options are configured using the
* {@link #setOption(SctpSocketOption,Object) setOption} method. SCTP server socket
* channels support the following options:
* <blockquote>
* <table border>
* <tr>
* <th>Option Name</th>
* <th>Description</th>
* </tr>
* <tr>
* <td> {@link SctpStandardSocketOption#SCTP_INIT_MAXSTREAMS
* SCTP_INIT_MAXSTREAMS} </td>
* <td> The maximum number of streams requested by the local endpoint during
* association initialization </td>
* </tr>
* </table>
* </blockquote>
* Additional (implementation specific) options may also be supported. The list
* of options supported is obtained by invoking the {@link #supportedOptions()
* supportedOptions} method.
*
* <p>SCTP server channels are safe for use by multiple concurrent threads.
*
* @since 1.7
*/
public abstract class SctpServerChannel
extends AbstractSelectableChannel
{
/**
* Initializes a new instance of this class.
*
* @param provider
* The selector provider for this channel
*/
protected SctpServerChannel(SelectorProvider provider) {
super(provider);
}
/**
* Opens an SCTP server channel.
*
* <P> The new channel's socket is initially unbound; it must be bound
* to a specific address via one of its socket's {@link #bind bind}
* methods before associations can be accepted.
*
* @return A new SCTP server channel
*
* @throws UnsupportedOperationException
* If the SCTP protocol is not supported
*
* @throws IOException
* If an I/O error occurs
*/
public static SctpServerChannel open() throws
IOException {
return new sun.nio.ch.SctpServerChannelImpl((SelectorProvider)null);
}
/**
* Accepts an association on this channel's socket.
*
* <P> If this channel is in non-blocking mode then this method will
* immediately return {@code null} if there are no pending associations.
* Otherwise it will block indefinitely until a new association is
* available or an I/O error occurs.
*
* <P> The {@code SCTPChannel} returned by this method, if any, will be in
* blocking mode regardless of the blocking mode of this channel.
*
* <P> If a security manager has been installed then for each new
* association this method verifies that the address and port number of the
* assocaitions's remote peer are permitted by the security manager's {@link
* java.lang.SecurityManager#checkAccept(String,int) checkAccept} method.
*
* @return The SCTP channel for the new association, or {@code null}
* if this channel is in non-blocking mode and no association is
* available to be accepted
*
* @throws java.nio.channels.ClosedChannelException
* If this channel is closed
*
* @throws java.nio.channels.AsynchronousCloseException
* If another thread closes this channel
* while the accept operation is in progress
*
* @throws java.nio.channels.ClosedByInterruptException
* If another thread interrupts the current thread
* while the accept operation is in progress, thereby
* closing the channel and setting the current thread's
* interrupt status
*
* @throws java.nio.channels.NotYetBoundException
* If this channel's socket has not yet been bound
*
* @throws SecurityException
* If a security manager has been installed and it does not permit
* access to the remote peer of the new association
*
* @throws IOException
* If some other I/O error occurs
*/
public abstract SctpChannel accept() throws IOException;
/**
* Binds the channel's socket to a local address and configures the socket
* to listen for associations.
*
* <P> This method works as if invoking it were equivalent to evaluating the
* expression:
* <blockquote><pre>
* bind(local, 0);
* </pre></blockquote>
*
* @param local
* The local address to bind the socket, or {@code null} to
* bind the socket to an automatically assigned socket address
*
* @return This channel
*
* @throws java.nio.channels.ClosedChannelException
* If this channel is closed
*
* @throws java.nio.channels.AlreadyBoundException
* If this channel is already bound
*
* @throws java.nio.channels.UnsupportedAddressTypeException
* If the type of the given address is not supported
*
* @throws SecurityException
* If a security manager has been installed and its {@link
* java.lang.SecurityManager#checkListen(int) checkListen} method
* denies the operation
*
* @throws IOException
* If some other I/O error occurs
*/
public final SctpServerChannel bind(SocketAddress local)
throws IOException {
return bind(local, 0);
}
/**
* Binds the channel's socket to a local address and configures the socket
* to listen for associations.
*
* <P> This method is used to establish a relationship between the socket
* and the local address. Once a relationship is established then
* the socket remains bound until the channel is closed. This relationship
* may not necesssarily be with the address {@code local} as it may be
* removed by {@link #unbindAddress unbindAddress}, but there will always be
* at least one local address bound to the channel's socket once an
* invocation of this method successfully completes.
*
* <P> Once the channel's socket has been successfully bound to a specific
* address, that is not automatically assigned, more addresses
* may be bound to it using {@link #bindAddress bindAddress}, or removed
* using {@link #unbindAddress unbindAddress}.
*
* <P> The backlog parameter is the maximum number of pending associations
* on the socket. Its exact semantics are implementation specific. An
* implementation may impose an implementation specific maximum length or
* may choose to ignore the parameter. If the backlog parameter has the
* value {@code 0}, or a negative value, then an implementation specific
* default is used.
*
* @param local
* The local address to bind the socket, or {@code null} to
* bind the socket to an automatically assigned socket address
*
* @param backlog
* The maximum number number of pending associations
*
* @return This channel
*
* @throws java.nio.channels.ClosedChannelException
* If this channel is closed
*
* @throws java.nio.channels.AlreadyBoundException
* If this channel is already bound
*
* @throws java.nio.channels.UnsupportedAddressTypeException
* If the type of the given address is not supported
*
* @throws SecurityException
* If a security manager has been installed and its {@link
* java.lang.SecurityManager#checkListen(int) checkListen} method
* denies the operation
*
* @throws IOException
* If some other I/O error occurs
*/
public abstract SctpServerChannel bind(SocketAddress local,
int backlog)
throws IOException;
/**
* Adds the given address to the bound addresses for the channel's
* socket.
*
* <P> The given address must not be the {@link
* java.net.InetAddress#isAnyLocalAddress wildcard} address.
* The channel must be first bound using {@link #bind bind} before
* invoking this method, otherwise {@link
* java.nio.channels.NotYetBoundException} is thrown. The {@link #bind bind}
* method takes a {@code SocketAddress} as its argument which typically
* contains a port number as well as an address. Addresses subquently bound
* using this method are simply addresses as the SCTP port number remains
* the same for the lifetime of the channel.
*
* <P> New associations accepted after this method successfully completes
* will be associated with the given address.
*
* @param address
* The address to add to the bound addresses for the socket
*
* @return This channel
*
* @throws java.nio.channels.ClosedChannelException
* If this channel is closed
*
* @throws java.nio.channels.NotYetBoundException
* If this channel is not yet bound
*
* @throws java.nio.channels.AlreadyBoundException
* If this channel is already bound to the given address
*
* @throws IllegalArgumentException
* If address is {@code null} or the {@link
* java.net.InetAddress#isAnyLocalAddress wildcard} address
*
* @throws IOException
* If some other I/O error occurs
*/
public abstract SctpServerChannel bindAddress(InetAddress address)
throws IOException;
/**
* Removes the given address from the bound addresses for the channel's
* socket.
*
* <P> The given address must not be the {@link
* java.net.InetAddress#isAnyLocalAddress wildcard} address.
* The channel must be first bound using {@link #bind bind} before
* invoking this method, otherwise
* {@link java.nio.channels.NotYetBoundException} is thrown.
* If this method is invoked on a channel that does not have
* {@code address} as one of its bound addresses, or that has only one
* local address bound to it, then this method throws {@link
* IllegalUnbindException}.
* The initial address that the channel's socket is bound to using
* {@link #bind bind} may be removed from the bound addresses for the
* channel's socket.
*
* <P> New associations accepted after this method successfully completes
* will not be associated with the given address.
*
* @param address
* The address to remove from the bound addresses for the socket
*
* @return This channel
*
* @throws java.nio.channels.ClosedChannelException
* If this channel is closed
*
* @throws java.nio.channels.NotYetBoundException
* If this channel is not yet bound
*
* @throws IllegalArgumentException
* If address is {@code null} or the {@link
* java.net.InetAddress#isAnyLocalAddress wildcard} address
*
* @throws IllegalUnbindException
* If the implementation does not support removing addresses from a
* listening socket, {@code address} is not bound to the channel's
* socket, or the channel has only one address bound to it
*
* @throws IOException
* If some other I/O error occurs
*/
public abstract SctpServerChannel unbindAddress(InetAddress address)
throws IOException;
/**
* Returns all of the socket addresses to which this channel's socket is
* bound.
*
* @return All the socket addresses that this channel's socket is
* bound to, or an empty {@code Set} if the channel's socket is not
* bound
*
* @throws java.nio.channels.ClosedChannelException
* If the channel is closed
*
* @throws IOException
* If an I/O error occurs
*/
public abstract Set<SocketAddress> getAllLocalAddresses()
throws IOException;
/**
* Returns the value of a socket option.
*
* @param name
* The socket option
*
* @return The value of the socket option. A value of {@code null} may be
* a valid value for some socket options.
*
* @throws UnsupportedOperationException
* If the socket option is not supported by this channel
*
* @throws java.nio.channels.ClosedChannelException
* If this channel is closed
*
* @throws IOException
* If an I/O error occurs
*
* @see SctpStandardSocketOption
*/
public abstract <T> T getOption(SctpSocketOption<T> name) throws IOException;
/**
* Sets the value of a socket option.
*
* @param name
* The socket option
*
* @param value
* The value of the socket option. A value of {@code null} may be
* a valid value for some socket options.
*
* @return This channel
*
* @throws UnsupportedOperationException
* If the socket option is not supported by this channel
*
* @throws IllegalArgumentException
* If the value is not a valid value for this socket option
*
* @throws java.nio.channels.ClosedChannelException
* If this channel is closed
*
* @throws IOException
* If an I/O error occurs
*
* @see SctpStandardSocketOption
*/
public abstract <T> SctpServerChannel setOption(SctpSocketOption<T> name,
T value)
throws IOException;
/**
* Returns a set of the socket options supported by this channel.
*
* <P> This method will continue to return the set of options even after the
* channel has been closed.
*
* @return A set of the socket options supported by this channel
*/
public abstract Set<SctpSocketOption<?>> supportedOptions();
/**
* Returns an operation set identifying this channel's supported
* operations.
*
* <P> SCTP server channels only support the accepting of new
* associations, so this method returns
* {@link java.nio.channels.SelectionKey#OP_ACCEPT}.
*
* @return The valid-operation set
*/
@Override
public final int validOps() {
return SelectionKey.OP_ACCEPT;
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.nio.sctp;
import java.net.SocketOption;
/**
* A socket option associated with an SCTP channel.
*
* @param <T> The type of the socket option value.
*
* @since 1.7
*
* @see SctpStandardSocketOption
*/
public interface SctpSocketOption<T> extends SocketOption<T> { }

View File

@ -0,0 +1,419 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.nio.sctp;
import java.net.SocketAddress;
import sun.nio.ch.SctpStdSocketOption;
/**
* SCTP channels supports the socket options defined by this class
* (as well as those listed in the particular channel class) and may support
* additional Implementation specific socket options.
*
* @since 1.7
*/
public class SctpStandardSocketOption {
private SctpStandardSocketOption() {}
/**
* Enables or disables message fragmentation.
*
* <P> The value of this socket option is a {@code Boolean} that represents
* whether the option is enabled or disabled. If enabled no SCTP message
* fragmentation will be performed. Instead if a message being sent
* exceeds the current PMTU size, the message will NOT be sent and
* an error will be indicated to the user.
*
* <P> It is implementation specific whether or not this option is
* supported.
*/
public static final SctpSocketOption<Boolean> SCTP_DISABLE_FRAGMENTS = new
SctpStdSocketOption<Boolean>("SCTP_DISABLE_FRAGMENTS", Boolean.class,
sun.nio.ch.SctpStdSocketOption.SCTP_DISABLE_FRAGMENTS);
/**
* Enables or disables explicit message completion.
*
* <p> The value of this socket option is a {@code Boolean} that represents
* whether the option is enabled or disabled. When this option is enabled,
* the {@code send} method may be invoked multiple times to a send message.
* The {@code isComplete} parameter of the {@link MessageInfo} must only
* be set to {@code true} for the final send to indicate that the message is
* complete. If this option is disabled then each individual {@code send}
* invocation is considered complete.
*
* <P> The default value of the option is {@code false} indicating that the
* option is disabled. It is implementation specific whether or not this
* option is supported.
*/
public static final SctpSocketOption<Boolean> SCTP_EXPLICIT_COMPLETE = new
SctpStdSocketOption<Boolean>("SCTP_EXPLICIT_COMPLETE", Boolean.class,
sun.nio.ch.SctpStdSocketOption.SCTP_EXPLICIT_COMPLETE);
/**
* Fragmented interleave controls how the presentation of messages occur
* for the message receiver. There are three levels of fragment interleave
* defined. Two of the levels effect {@link SctpChannel}, while
* {@link SctpMultiChannel} is effected by all three levels.
*
* <P> This option takes an {@code Integer} value. It can be set to a value
* of {@code 0}, {@code 1} or {@code 2}.
*
* <P> Setting the three levels provides the following receiver
* interactions:
*
* <P> {@code level 0} - Prevents the interleaving of any messages. This
* means that when a partial delivery begins, no other messages will be
* received except the message being partially delivered. If another message
* arrives on a different stream (or association) that could be delivered,
* it will be blocked waiting for the user to read all of the partially
* delivered message.
*
* <P> {@code level 1} - Allows interleaving of messages that are from
* different associations. For {@code SctpChannel}, level 0 and
* level 1 have the same meaning since an {@code SctpChannel} always
* receives messages from the same association. Note that setting an {@code
* SctpMultiChannel} to this level may cause multiple partial
* delivers from different associations but for any given association, only
* one message will be delivered until all parts of a message have been
* delivered. This means that one large message, being read with an
* association identification of "X", will block other messages from
* association "X" from being delivered.
*
* <P> {@code level 2} - Allows complete interleaving of messages. This
* level requires that the sender carefully observe not only the peer
* {@code Association} but also must pay careful attention to the stream
* number. With this option enabled a partially delivered message may begin
* being delivered for association "X" stream "Y" and the next subsequent
* receive may return a message from association "X" stream "Z". Note that
* no other messages would be delivered for association "X" stream "Y"
* until all of stream "Y"'s partially delivered message was read.
* Note that this option effects both channel types. Also note that
* for an {@code SctpMultiChannel} not only may another streams
* message from the same association be delivered from the next receive,
* some other associations message may be delivered upon the next receive.
*
* <P> It is implementation specific whether or not this option is
* supported.
*/
public static final SctpSocketOption<Integer> SCTP_FRAGMENT_INTERLEAVE =
new SctpStdSocketOption<Integer>("SCTP_FRAGMENT_INTERLEAVE",
Integer.class,
sun.nio.ch.SctpStdSocketOption.SCTP_FRAGMENT_INTERLEAVE);
/**
* The maximum number of streams requested by the local endpoint during
* association initialization.
*
* <P> The value of this socket option is an {@link
* SctpStandardSocketOption.InitMaxStreams InitMaxStreams}, that represents
* the maximum number of inbound and outbound streams that an association
* on the channel is prepared to support.
*
* <P> For an {@link SctpChannel} this option may only be used to
* change the number of inbound/outbound streams prior to connecting.
*
* <P> For an {@link SctpMultiChannel} this option determines
* the maximum number of inbound/outbound streams new associations setup
* on the channel will be prepared to support.
*
* <P> For an {@link SctpServerChannel} this option determines the
* maximum number of inbound/outbound streams accepted sockets will
* negotiate with their connecting peer.
*
* <P> In all cases the value set by this option is used in the negotiation
* of new associations setup on the channel's socket and the actual
* maximum number of inbound/outbound streams that have been negotiated
* with the peer can be retrieved from the appropriate {@link
* Association}. The {@code Association} can be retrieved from the
* {@link AssociationChangeNotification.AssocChangeEvent#COMM_UP COMM_UP}
* {@link AssociationChangeNotification} belonging to that association.
*
* <p> This value is bounded by the actual implementation. In other
* words the user may be able to support more streams than the Operating
* System. In such a case, the Operating System limit may override the
* value requested by the user. The default value of 0 indicates to use
* the endpoints default value.
*/
public static final SctpSocketOption
<SctpStandardSocketOption.InitMaxStreams> SCTP_INIT_MAXSTREAMS =
new SctpStdSocketOption<SctpStandardSocketOption.InitMaxStreams>(
"SCTP_INIT_MAXSTREAMS", SctpStandardSocketOption.InitMaxStreams.class);
/**
* Enables or disables a Nagle-like algorithm.
*
* <P> The value of this socket option is a {@code Boolean} that represents
* whether the option is enabled or disabled. SCTP uses an algorithm like
* <em>The Nagle Algorithm</em> to coalesce short segments and
* improve network efficiency.
*/
public static final SctpSocketOption<Boolean> SCTP_NODELAY =
new SctpStdSocketOption<Boolean>("SCTP_NODELAY", Boolean.class,
sun.nio.ch.SctpStdSocketOption.SCTP_NODELAY);
/**
* Requests that the local SCTP stack use the given peer address as
* the association primary.
*
* <P> The value of this socket option is a {@code SocketAddress}
* that represents the peer address that the local SCTP stack should use as
* the association primary. The address must be one of the association
* peer's addresses.
*
* <P> An {@code SctpMultiChannel} can control more than one
* association, the association parameter must be given when setting or
* retrieving this option.
*
* <P> Since {@code SctpChannel} only controls one association,
* the association parameter is not required and this option can be
* set or queried directly.
*/
public static final SctpSocketOption<SocketAddress> SCTP_PRIMARY_ADDR =
new SctpStdSocketOption<SocketAddress>
("SCTP_PRIMARY_ADDR", SocketAddress.class);
/**
* Requests that the peer mark the enclosed address as the association
* primary.
*
* <P> The value of this socket option is a {@code SocketAddress}
* that represents the local address that the peer should use as its
* primary address. The given address must be one of the association's
* locally bound addresses.
*
* <P> An {@code SctpMultiChannel} can control more than one
* association, the association parameter must be given when setting or
* retrieving this option.
*
* <P> Since {@code SctpChannel} only controls one association,
* the association parameter is not required and this option can be
* queried directly.
*
* <P> Note, this is a set only option and cannot be retrieved by {@code
* getOption}. It is implementation specific whether or not this
* option is supported.
*/
public static final SctpSocketOption<SocketAddress> SCTP_SET_PEER_PRIMARY_ADDR =
new SctpStdSocketOption<SocketAddress>
("SCTP_SET_PEER_PRIMARY_ADDR", SocketAddress.class);
/**
* The size of the socket send buffer.
*
* <p> The value of this socket option is an {@code Integer} that is the
* size of the socket send buffer in bytes. The socket send buffer is an
* output buffer used by the networking implementation. It may need to be
* increased for high-volume connections. The value of the socket option is
* a <em>hint</em> to the implementation to size the buffer and the actual
* size may differ. The socket option can be queried to retrieve the actual
* size.
*
* <p> For {@code SctpChannel}, this controls the amount of data
* the SCTP stack may have waiting in internal buffers to be sent. This
* option therefore bounds the maximum size of data that can be sent in a
* single send call.
*
* <P> For {@code SctpMultiChannel}, the effect is the same as for {@code
* SctpChannel}, except that it applies to all associations. The option
* applies to each association's window size separately.
*
* <p> An implementation allows this socket option to be set before the
* socket is bound or connected. Whether an implementation allows the
* socket send buffer to be changed after the socket is bound is system
* dependent.
*/
public static final SctpSocketOption<Integer> SO_SNDBUF =
new SctpStdSocketOption<Integer>("SO_SNDBUF", Integer.class,
sun.nio.ch.SctpStdSocketOption.SO_SNDBUF);
/**
* The size of the socket receive buffer.
*
* <P> The value of this socket option is an {@code Integer} that is the
* size of the socket receive buffer in bytes. The socket receive buffer is
* an input buffer used by the networking implementation. It may need to be
* increased for high-volume connections or decreased to limit the possible
* backlog of incoming data. The value of the socket option is a
* <em>hint</em> to the implementation to size the buffer and the actual
* size may differ.
*
* <P> For {@code SctpChannel}, this controls the receiver window size.
*
* <P> For {@code SctpMultiChannel}, the meaning is implementation
* dependent. It might control the receive buffer for each association bound
* to the socket descriptor or it might control the receive buffer for the
* whole socket.
*
* <p> An implementation allows this socket option to be set before the
* socket is bound or connected. Whether an implementation allows the
* socket receive buffer to be changed after the socket is bound is system
* dependent.
*/
public static final SctpSocketOption<Integer> SO_RCVBUF =
new SctpStdSocketOption<Integer>("SO_RCVBUF", Integer.class,
sun.nio.ch.SctpStdSocketOption.SO_RCVBUF);
/**
* Linger on close if data is present.
*
* <p> The value of this socket option is an {@code Integer} that controls
* the action taken when unsent data is queued on the socket and a method
* to close the socket is invoked. If the value of the socket option is zero
* or greater, then it represents a timeout value, in seconds, known as the
* <em>linger interval</em>. The linger interval is the timeout for the
* {@code close} method to block while the operating system attempts to
* transmit the unsent data or it decides that it is unable to transmit the
* data. If the value of the socket option is less than zero then the option
* is disabled. In that case the {@code close} method does not wait until
* unsent data is transmitted; if possible the operating system will transmit
* any unsent data before the connection is closed.
*
* <p> This socket option is intended for use with sockets that are configured
* in {@link java.nio.channels.SelectableChannel#isBlocking() blocking} mode
* only. The behavior of the {@code close} method when this option is
* enabled on a non-blocking socket is not defined.
*
* <p> The initial value of this socket option is a negative value, meaning
* that the option is disabled. The option may be enabled, or the linger
* interval changed, at any time. The maximum value of the linger interval
* is system dependent. Setting the linger interval to a value that is
* greater than its maximum value causes the linger interval to be set to
* its maximum value.
*/
public static final SctpSocketOption<Integer> SO_LINGER =
new SctpStdSocketOption<Integer>("SO_LINGER", Integer.class,
sun.nio.ch.SctpStdSocketOption.SO_LINGER);
/**
* This class is used to set the maximum number of inbound/outbound streams
* used by the local endpoint during association initialization. An
* instance of this class is used to set the {@link
* SctpStandardSocketOption#SCTP_INIT_MAXSTREAMS SCTP_INIT_MAXSTREAMS}
* socket option.
*
* @since 1.7
*/
public static class InitMaxStreams {
private int maxInStreams;
private int maxOutStreams;
private InitMaxStreams(int maxInStreams, int maxOutStreams) {
this.maxInStreams = maxInStreams;
this.maxOutStreams = maxOutStreams;
}
/**
* Creates an InitMaxStreams instance.
*
* @param maxInStreams
* The maximum number of inbound streams, where
* {@code 0 <= maxInStreams <= 65536}
*
* @param maxOutStreams
* The maximum number of outbound streams, where
* {@code 0 <= maxOutStreams <= 65536}
*
* @return An {@code InitMaxStreams} instance
*
* @throws IllegalArgumentException
* If an argument is outside of specified bounds
*/
public static InitMaxStreams create
(int maxInStreams, int maxOutStreams) {
if (maxOutStreams < 0 || maxOutStreams > 65535)
throw new IllegalArgumentException(
"Invalid maxOutStreams value");
if (maxInStreams < 0 || maxInStreams > 65535)
throw new IllegalArgumentException(
"Invalid maxInStreams value");
return new InitMaxStreams(maxInStreams, maxOutStreams);
}
/**
* Returns the maximum number of inbound streams.
*
* @return Maximum inbound streams
*/
public int maxInStreams() {
return maxInStreams;
}
/**
* Returns the maximum number of outbound streams.
*
* @return Maximum outbound streams
*/
public int maxOutStreams() {
return maxOutStreams;
}
/**
* Returns a string representation of this init max streams, including
* the maximum in and out bound streams.
*
* @return A string representation of this init max streams
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(super.toString()).append(" [");
sb.append("maxInStreams:").append(maxInStreams);
sb.append("maxOutStreams:").append(maxOutStreams).append("]");
return sb.toString();
}
/**
* Returns true if the specified object is another {@code InitMaxStreams}
* instance with the same number of in and out bound streams.
*
* @param obj
* The object to be compared with this init max streams
*
* @return true if the specified object is another
* {@code InitMaxStreams} instance with the same number of in
* and out bound streams
*/
@Override
public boolean equals(Object obj) {
if (obj != null && obj instanceof InitMaxStreams) {
InitMaxStreams that = (InitMaxStreams) obj;
if (this.maxInStreams == that.maxInStreams &&
this.maxOutStreams == that.maxOutStreams)
return true;
}
return false;
}
/**
* Returns a hash code value for this init max streams.
*/
@Override
public int hashCode() {
int hash = 7 ^ maxInStreams ^ maxOutStreams;
return hash;
}
}
}

View File

@ -0,0 +1,89 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.nio.sctp;
import java.nio.ByteBuffer;
import java.net.SocketAddress;
/**
* Notification emitted when a send failed notification has been received.
*
* <P> A send failed notification indicates that a message cannot be delivered.
* Typically this is because the association has been shutdown with unsent data
* in the socket output buffer, or in the case of a {@link SctpMultiChannel}
* the association failed to setup.
*
* @since 1.7
*/
public abstract class SendFailedNotification implements Notification {
/**
* Initializes a new instance of this class.
*/
protected SendFailedNotification() {}
/**
* Returns the association that this notification is applicable to.
*
* @return The association that failed to send, or {@code null} if
* there is no association, that is, the notification follows a
* {@linkplain
* com.sun.nio.sctp.AssociationChangeNotification.AssocChangeEvent#CANT_START}
*/
@Override
public abstract Association association();
/**
* Returns the address.
*
* @return The peer primary address of the association or the address that
* the message was sent to
*/
public abstract SocketAddress address();
/**
* Returns the data that was to be sent.
*
* @return The user data. The buffers position will be {@code 0} and its
* limit will be set to the end of the data.
*/
public abstract ByteBuffer buffer();
/**
* Returns the error code.
*
* <P> The errorCode gives the reason why the send failed, and if set, will
* be a SCTP protocol error code as defined in RFC2960 section 3.3.10
*
* @return The error code
*/
public abstract int errorCode();
/**
* Returns the stream number that the messge was to be sent on.
*
* @return The stream number
*/
public abstract int streamNumber();
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.nio.sctp;
/**
* Notification emitted when a peers shutdowns an the association.
*
* <P> When a peer sends a <i>SHUTDOWN</i>, the SCTP stack delivers this
* notification to inform the application that it should cease sending data.
*
* @since 1.7
*/
public abstract class ShutdownNotification implements Notification {
/**
* Initializes a new instance of this class.
*/
protected ShutdownNotification() {}
/**
* Returns the association that this notification is applicable to.
*
* @return The association that received the shutdown
*/
public abstract Association association();
}

View File

@ -0,0 +1,75 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
/**
* A Java API for Stream Control Transport Protocol.
*
* <P> The Stream Control Transport Protocol (SCTP) is a reliable,
* message-oriented, transport protocol existing at an equivalent level with UDP
* (User Datagram Protocol) and TCP (Transmission Control Protocol). SCTP is
* session oriented and an association between the endpoints must be established
* before any data can be transmitted.
*
* <P> SCTP has direct support for multi-homing, meaning than an endpoint may be
* represented by more than one address and each address may be used for sending
* and receiving data, thus providing network redundancy. The connection between
* two endpoints is referred to as an association between those endpoints.
* Endpoints can exchange a list of addresses during association setup. One
* address is designated as the primary address, this is the default address that
* the peer will use for sending data. A single port number is used across the
* entire address list at an endpoint for a specific session.
*
* <P> SCTP is message based. I/O operations operate upon messages and message
* boundaries are preserved. Each association may support multiple independant
* logical streams. Each stream represents a sequence of messages within a single
* association and streams are independant of one another, meaning that stream
* identifiers and sequence numbers are included in the data packet to allow
* sequencing of messages on a per-stream basis.
*
* <P> This package provides two programming model styles. The one-to-one style
* supported by {@link com.sun.nio.sctp.SctpChannel} and {@link
* com.sun.nio.sctp.SctpServerChannel}, and the one-to-many
* style supported by {@link com.sun.nio.sctp.SctpMultiChannel}.
* The semantics of the one-to-one style interface are very similar to TCP.
* An {@code SctpChannel} can only control one SCTP association. The
* semantics of the one-to-many style interface are very similar to UDP. An
* {@code SctpMutliChannel} can control multiple SCTP associations.
*
* <P> Applications can send and receive per-message ancillary information through
* {@link com.sun.nio.sctp.MessageInfo}. For example, the stream number that
* the message it is to be sent or received from. The SCTP stack is event driven
* and applications can receive notifications of certain SCTP events by invoking
* the {@code receive} method of the SCTP channel with an appropriate {@link
* com.sun.nio.sctp.NotificationHandler notification handler}.
*
* <P> The SCTP protocol is defined by
* <A HREF="http://tools.ietf.org/html/rfc4960">RFC4960</A>, and the optional
* extension for <I>Dynamic Address Reconfiguration</I> is defined by
* <A HREF="http://tools.ietf.org/html/rfc5061">RFC5061</A>.
*
* @since 1.7
*/
package com.sun.nio.sctp;

View File

@ -0,0 +1,170 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.nio.ch;
import java.net.SocketAddress;
import com.sun.nio.sctp.MessageInfo;
import com.sun.nio.sctp.Association;
/**
* An implementation of a MessageInfo.
*/
public class SctpMessageInfoImpl extends MessageInfo {
private final SocketAddress address;
private final int bytes; /* 0 */
private Association association;
private int assocId;
private int streamNumber;
private boolean complete = true;
private boolean unordered; /* false */
private long timeToLive; /* 0L */
private int ppid; /* 0 */
public SctpMessageInfoImpl(Association association,
SocketAddress address,
int streamNumber) {
this.association = association;
this.address = address;
this.streamNumber = streamNumber;
bytes = 0;
}
/* Invoked from native */
private SctpMessageInfoImpl(int assocId,
SocketAddress address,
int bytes,
int streamNumber,
boolean complete,
boolean unordered,
int ppid) {
this.assocId = assocId;
this.address = address;
this.bytes = bytes;
this.streamNumber = streamNumber;
this.complete = complete;
this.unordered = unordered;
this.ppid = ppid;
}
@Override
public Association association() {
return association;
}
/**
* SctpMessageInfoImpl instances created from native will need to have their
* association set from the channel.
*/
void setAssociation(Association association) {
this.association = association;
}
int associationID() {
return assocId;
}
@Override
public SocketAddress address() {
return address;
}
@Override
public int bytes() {
return bytes;
}
@Override
public int streamNumber() {
return streamNumber;
}
@Override
public MessageInfo streamNumber(int streamNumber) {
if (streamNumber < 0 || streamNumber > 65536)
throw new IllegalArgumentException("Invalid stream number");
this.streamNumber = streamNumber;
return this;
}
@Override
public int payloadProtocolID() {
return ppid;
}
@Override
public MessageInfo payloadProtocolID(int ppid) {
this.ppid = ppid;
return this;
}
@Override
public boolean isComplete() {
return complete;
}
@Override
public MessageInfo complete(boolean complete) {
this.complete = complete;
return this;
}
@Override
public boolean isUnordered() {
return unordered;
}
@Override
public MessageInfo unordered(boolean unordered) {
this.unordered = unordered;
return this;
}
@Override
public long timeToLive() {
return timeToLive;
}
@Override
public MessageInfo timeToLive(long millis) {
timeToLive = millis;
return this;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(super.toString());
sb.append( "[Address: ").append(address)
.append(", Association: ").append(association)
.append(", Assoc ID: ").append(assocId)
.append(", Bytes: ").append(bytes)
.append(", Stream Number: ").append(streamNumber)
.append(", Complete: ").append(complete)
.append(", isUnordered: ").append(unordered)
.append("]");
return sb.toString();
}
}

View File

@ -0,0 +1,76 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.nio.ch;
import com.sun.nio.sctp.SctpSocketOption;
public class SctpStdSocketOption<T>
implements SctpSocketOption<T>
{
/* for native mapping of int options */
public static final int SCTP_DISABLE_FRAGMENTS = 1;
public static final int SCTP_EXPLICIT_COMPLETE = 2;
public static final int SCTP_FRAGMENT_INTERLEAVE = 3;
public static final int SCTP_NODELAY = 4;
public static final int SO_SNDBUF = 5;
public static final int SO_RCVBUF = 6;
public static final int SO_LINGER = 7;
private final String name;
private final Class<T> type;
/* for native mapping of int options */
private int constValue;
public SctpStdSocketOption(String name, Class<T> type) {
this.name = name;
this.type = type;
}
public SctpStdSocketOption(String name, Class<T> type, int constValue) {
this.name = name;
this.type = type;
this.constValue = constValue;
}
@Override
public String name() {
return name;
}
@Override
public Class<T> type() {
return type;
}
@Override
public String toString() {
return name;
}
int constValue() {
return constValue;
}
}

View File

@ -0,0 +1,120 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.nio.ch;
import com.sun.nio.sctp.Association;
import com.sun.nio.sctp.AssociationChangeNotification;
/**
* An implementation of AssociationChangeNotification
*/
public class SctpAssocChange extends AssociationChangeNotification
implements SctpNotification
{
/* static final ints so that they can be referenced from native */
private final static int SCTP_COMM_UP = 1;
private final static int SCTP_COMM_LOST = 2;
private final static int SCTP_RESTART = 3;
private final static int SCTP_SHUTDOWN = 4;
private final static int SCTP_CANT_START = 5;
private Association association;
/* assocId is used to lookup the association before the notification is
* returned to user code */
private int assocId;
private AssocChangeEvent event;
private int maxOutStreams;
private int maxInStreams;
/* Invoked from native */
private SctpAssocChange(int assocId,
int intEvent,
int maxOutStreams,
int maxInStreams) {
switch (intEvent) {
case SCTP_COMM_UP :
this.event = AssocChangeEvent.COMM_UP;
break;
case SCTP_COMM_LOST :
this.event = AssocChangeEvent.COMM_LOST;
break;
case SCTP_RESTART :
this.event = AssocChangeEvent.RESTART;
break;
case SCTP_SHUTDOWN :
this.event = AssocChangeEvent.SHUTDOWN;
break;
case SCTP_CANT_START :
this.event = AssocChangeEvent.CANT_START;
break;
default :
throw new AssertionError(
"Unknown Association Change Event type: " + intEvent);
}
this.assocId = assocId;
this.maxOutStreams = maxOutStreams;
this.maxInStreams = maxInStreams;
}
@Override
public int assocId() {
return assocId;
}
@Override
public void setAssociation(Association association) {
this.association = association;
}
@Override
public Association association() {
assert association != null;
return association;
}
@Override
public AssocChangeEvent event() {
return event;
}
int maxOutStreams() {
return maxOutStreams;
}
int maxInStreams() {
return maxInStreams;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(super.toString()).append(" [");
sb.append("Association:").append(association);
sb.append(", Event: ").append(event).append("]");
return sb.toString();
}
}

View File

@ -0,0 +1,52 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.nio.ch;
import com.sun.nio.sctp.Association;
/**
* An implementation of Association
*/
public class SctpAssociationImpl extends Association {
public SctpAssociationImpl(int associationID,
int maxInStreams,
int maxOutStreams) {
super(associationID, maxInStreams, maxOutStreams);
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer(super.toString());
return sb.append("[associationID:")
.append(associationID())
.append(", maxIn:")
.append(maxInboundStreams())
.append(", maxOut:")
.append(maxOutboundStreams())
.append("]")
.toString();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,959 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.nio.ch;
import java.net.InetAddress;
import java.net.SocketAddress;
import java.net.InetSocketAddress;
import java.io.FileDescriptor;
import java.io.IOException;
import java.util.Collections;
import java.util.Set;
import java.util.HashSet;
import java.util.HashMap;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.AlreadyBoundException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.NotYetBoundException;
import java.nio.channels.spi.SelectorProvider;
import com.sun.nio.sctp.AbstractNotificationHandler;
import com.sun.nio.sctp.Association;
import com.sun.nio.sctp.AssociationChangeNotification;
import com.sun.nio.sctp.HandlerResult;
import com.sun.nio.sctp.IllegalReceiveException;
import com.sun.nio.sctp.InvalidStreamException;
import com.sun.nio.sctp.IllegalUnbindException;
import com.sun.nio.sctp.NotificationHandler;
import com.sun.nio.sctp.MessageInfo;
import com.sun.nio.sctp.SctpChannel;
import com.sun.nio.sctp.SctpMultiChannel;
import com.sun.nio.sctp.SctpSocketOption;
import static com.sun.nio.sctp.SctpStandardSocketOption.*;
import static sun.nio.ch.SctpResultContainer.*;
/**
* An implementation of SctpMultiChannel
*/
public class SctpMultiChannelImpl extends SctpMultiChannel
implements SelChImpl
{
/* Used to make native close and preClose calls */
private static NativeDispatcher nd;
private final FileDescriptor fd;
private final int fdVal;
/* IDs of native threads doing send and receives, for signalling */
private volatile long receiverThread = 0;
private volatile long senderThread = 0;
/* Lock held by current receiving thread */
private final Object receiveLock = new Object();
/* Lock held by current sending thread */
private final Object sendLock = new Object();
/* Lock held by any thread that modifies the state fields declared below
* DO NOT invoke a blocking I/O operation while holding this lock! */
private final Object stateLock = new Object();
private enum ChannelState {
UNINITIALIZED,
KILLPENDING,
KILLED,
}
/* -- The following fields are protected by stateLock -- */
private ChannelState state = ChannelState.UNINITIALIZED;
/* Binding: Once bound the port will remain constant. */
int port = -1;
private HashSet<InetSocketAddress> localAddresses = new HashSet<InetSocketAddress>();
/* Has the channel been bound to the wildcard address */
private boolean wildcard; /* false */
/* Keeps a map of addresses to association, and visa versa */
private HashMap<SocketAddress, Association> addressMap =
new HashMap<SocketAddress, Association>();
private HashMap<Association, Set<SocketAddress>> associationMap =
new HashMap<Association, Set<SocketAddress>>();
/* -- End of fields protected by stateLock -- */
/* If an association has been shutdown mark it for removal after
* the user handler has been invoked */
private final ThreadLocal<Association> associationToRemove =
new ThreadLocal<Association>() {
@Override protected Association initialValue() {
return null;
}
};
/* A notification handler cannot invoke receive */
private final ThreadLocal<Boolean> receiveInvoked =
new ThreadLocal<Boolean>() {
@Override protected Boolean initialValue() {
return Boolean.FALSE;
}
};
public SctpMultiChannelImpl(SelectorProvider provider)
throws IOException {
//TODO: update provider, remove public modifier
super(provider);
this.fd = SctpNet.socket(false /*one-to-many*/);
this.fdVal = IOUtil.fdVal(fd);
}
@Override
public SctpMultiChannel bind(SocketAddress local, int backlog)
throws IOException {
synchronized (receiveLock) {
synchronized (sendLock) {
synchronized (stateLock) {
ensureOpen();
if (isBound())
throw new AlreadyBoundException();
InetSocketAddress isa = (local == null) ?
new InetSocketAddress(0) : Net.checkAddress(local);
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkListen(isa.getPort());
Net.bind(fd, isa.getAddress(), isa.getPort());
InetSocketAddress boundIsa = Net.localAddress(fd);
port = boundIsa.getPort();
localAddresses.add(isa);
if (isa.getAddress().isAnyLocalAddress())
wildcard = true;
Net.listen(fd, backlog < 1 ? 50 : backlog);
}
}
}
return this;
}
@Override
public SctpMultiChannel bindAddress(InetAddress address)
throws IOException {
return bindUnbindAddress(address, true);
}
@Override
public SctpMultiChannel unbindAddress(InetAddress address)
throws IOException {
return bindUnbindAddress(address, false);
}
private SctpMultiChannel bindUnbindAddress(InetAddress address,
boolean add)
throws IOException {
if (address == null)
throw new IllegalArgumentException();
synchronized (receiveLock) {
synchronized (sendLock) {
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
if (!isBound())
throw new NotYetBoundException();
if (wildcard)
throw new IllegalStateException(
"Cannot add or remove addresses from a channel that is bound to the wildcard address");
if (address.isAnyLocalAddress())
throw new IllegalArgumentException(
"Cannot add or remove the wildcard address");
if (add) {
for (InetSocketAddress addr : localAddresses) {
if (addr.getAddress().equals(address)) {
throw new AlreadyBoundException();
}
}
} else { /*removing */
/* Verify that there is more than one address
* and that address is already bound */
if (localAddresses.size() <= 1)
throw new IllegalUnbindException("Cannot remove address from a channel with only one address bound");
boolean foundAddress = false;
for (InetSocketAddress addr : localAddresses) {
if (addr.getAddress().equals(address)) {
foundAddress = true;
break;
}
}
if (!foundAddress )
throw new IllegalUnbindException("Cannot remove address from a channel that is not bound to that address");
}
SctpNet.bindx(fdVal, new InetAddress[]{address}, port, add);
/* Update our internal Set to reflect the addition/removal */
if (add)
localAddresses.add(new InetSocketAddress(address, port));
else {
for (InetSocketAddress addr : localAddresses) {
if (addr.getAddress().equals(address)) {
localAddresses.remove(addr);
break;
}
}
}
}
}
}
return this;
}
@Override
public Set<Association> associations()
throws ClosedChannelException, NotYetBoundException {
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
if (!isBound())
throw new NotYetBoundException();
return Collections.unmodifiableSet(associationMap.keySet());
}
}
private boolean isBound() {
synchronized (stateLock) {
return port == -1 ? false : true;
}
}
private void ensureOpen() throws IOException {
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
}
}
private void receiverCleanup() throws IOException {
synchronized (stateLock) {
receiverThread = 0;
if (state == ChannelState.KILLPENDING)
kill();
}
}
private void senderCleanup() throws IOException {
synchronized (stateLock) {
senderThread = 0;
if (state == ChannelState.KILLPENDING)
kill();
}
}
@Override
protected void implConfigureBlocking(boolean block) throws IOException {
IOUtil.configureBlocking(fd, block);
}
@Override
public void implCloseSelectableChannel() throws IOException {
synchronized (stateLock) {
nd.preClose(fd);
if (receiverThread != 0)
NativeThread.signal(receiverThread);
if (senderThread != 0)
NativeThread.signal(senderThread);
if (!isRegistered())
kill();
}
}
@Override
public FileDescriptor getFD() {
return fd;
}
@Override
public int getFDVal() {
return fdVal;
}
/**
* Translates native poll revent ops into a ready operation ops
*/
private boolean translateReadyOps(int ops, int initialOps,
SelectionKeyImpl sk) {
int intOps = sk.nioInterestOps();
int oldOps = sk.nioReadyOps();
int newOps = initialOps;
if ((ops & PollArrayWrapper.POLLNVAL) != 0) {
/* This should only happen if this channel is pre-closed while a
* selection operation is in progress
* ## Throw an error if this channel has not been pre-closed */
return false;
}
if ((ops & (PollArrayWrapper.POLLERR
| PollArrayWrapper.POLLHUP)) != 0) {
newOps = intOps;
sk.nioReadyOps(newOps);
return (newOps & ~oldOps) != 0;
}
if (((ops & PollArrayWrapper.POLLIN) != 0) &&
((intOps & SelectionKey.OP_READ) != 0))
newOps |= SelectionKey.OP_READ;
if (((ops & PollArrayWrapper.POLLOUT) != 0) &&
((intOps & SelectionKey.OP_WRITE) != 0))
newOps |= SelectionKey.OP_WRITE;
sk.nioReadyOps(newOps);
return (newOps & ~oldOps) != 0;
}
@Override
public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) {
return translateReadyOps(ops, sk.nioReadyOps(), sk);
}
@Override
public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) {
return translateReadyOps(ops, 0, sk);
}
@Override
public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) {
int newOps = 0;
if ((ops & SelectionKey.OP_READ) != 0)
newOps |= PollArrayWrapper.POLLIN;
if ((ops & SelectionKey.OP_WRITE) != 0)
newOps |= PollArrayWrapper.POLLOUT;
sk.selector.putEventOps(sk, newOps);
}
@Override
public void kill() throws IOException {
synchronized (stateLock) {
if (state == ChannelState.KILLED)
return;
if (state == ChannelState.UNINITIALIZED) {
state = ChannelState.KILLED;
return;
}
assert !isOpen() && !isRegistered();
/* Postpone the kill if there is a thread sending or receiving. */
if (receiverThread == 0 && senderThread == 0) {
nd.close(fd);
state = ChannelState.KILLED;
} else {
state = ChannelState.KILLPENDING;
}
}
}
@Override
public <T> SctpMultiChannel setOption(SctpSocketOption<T> name,
T value,
Association association)
throws IOException {
if (name == null)
throw new NullPointerException();
if (!(supportedOptions().contains(name)))
throw new UnsupportedOperationException("'" + name + "' not supported");
synchronized (stateLock) {
if (association != null && (name.equals(SCTP_PRIMARY_ADDR) ||
name.equals(SCTP_SET_PEER_PRIMARY_ADDR))) {
checkAssociation(association);
}
if (!isOpen())
throw new ClosedChannelException();
SctpNet.setSocketOption(fdVal, name, value,
association.associationID());
}
return this;
}
@Override
@SuppressWarnings("unchecked")
public <T> T getOption(SctpSocketOption<T> name, Association association)
throws IOException {
if (name == null)
throw new NullPointerException();
if (!supportedOptions().contains(name))
throw new UnsupportedOperationException("'" + name + "' not supported");
synchronized (stateLock) {
checkAssociation(association);
if (!isOpen())
throw new ClosedChannelException();
return (T)SctpNet.getSocketOption(fdVal, name,
association.associationID());
}
}
private static class DefaultOptionsHolder {
static final Set<SctpSocketOption<?>> defaultOptions = defaultOptions();
private static Set<SctpSocketOption<?>> defaultOptions() {
HashSet<SctpSocketOption<?>> set = new HashSet<SctpSocketOption<?>>(10);
set.add(SCTP_DISABLE_FRAGMENTS);
set.add(SCTP_EXPLICIT_COMPLETE);
set.add(SCTP_FRAGMENT_INTERLEAVE);
set.add(SCTP_INIT_MAXSTREAMS);
set.add(SCTP_NODELAY);
set.add(SCTP_PRIMARY_ADDR);
set.add(SCTP_SET_PEER_PRIMARY_ADDR);
set.add(SO_SNDBUF);
set.add(SO_RCVBUF);
set.add(SO_LINGER);
return Collections.unmodifiableSet(set);
}
}
@Override
public final Set<SctpSocketOption<?>> supportedOptions() {
return DefaultOptionsHolder.defaultOptions;
}
@Override
public <T> MessageInfo receive(ByteBuffer buffer,
T attachment,
NotificationHandler<T> handler)
throws IOException {
if (buffer == null)
throw new IllegalArgumentException("buffer cannot be null");
if (buffer.isReadOnly())
throw new IllegalArgumentException("Read-only buffer");
if (receiveInvoked.get())
throw new IllegalReceiveException(
"cannot invoke receive from handler");
receiveInvoked.set(Boolean.TRUE);
try {
SctpResultContainer resultContainer = new SctpResultContainer();
do {
resultContainer.clear();
synchronized (receiveLock) {
ensureOpen();
if (!isBound())
throw new NotYetBoundException();
int n = 0;
try {
begin();
synchronized (stateLock) {
if(!isOpen())
return null;
receiverThread = NativeThread.current();
}
do {
n = receive(fdVal, buffer, resultContainer);
} while ((n == IOStatus.INTERRUPTED) && isOpen());
} finally {
receiverCleanup();
end((n > 0) || (n == IOStatus.UNAVAILABLE));
assert IOStatus.check(n);
}
if (!resultContainer.isNotification()) {
/* message or nothing */
if (resultContainer.hasSomething()) {
/* Set the association before returning */
SctpMessageInfoImpl info =
resultContainer.getMessageInfo();
info.setAssociation(lookupAssociation(info.
associationID()));
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
InetSocketAddress isa = (InetSocketAddress)info.address();
if (!addressMap.containsKey(isa)) {
/* must be a new association */
try {
sm.checkAccept(isa.getAddress().getHostAddress(),
isa.getPort());
} catch (SecurityException se) {
buffer.clear();
throw se;
}
}
}
assert info.association() != null;
return info;
} else {
/* Non-blocking may return null if nothing available*/
return null;
}
} else { /* notification */
synchronized (stateLock) {
handleNotificationInternal(
resultContainer);
}
}
} /* receiveLock */
} while (handler == null ? true :
(invokeNotificationHandler(resultContainer, handler, attachment)
== HandlerResult.CONTINUE));
} finally {
receiveInvoked.set(Boolean.FALSE);
}
return null;
}
private int receive(int fd,
ByteBuffer dst,
SctpResultContainer resultContainer)
throws IOException {
int pos = dst.position();
int lim = dst.limit();
assert (pos <= lim);
int rem = (pos <= lim ? lim - pos : 0);
if (dst instanceof DirectBuffer && rem > 0)
return receiveIntoNativeBuffer(fd, resultContainer, dst, rem, pos);
/* Substitute a native buffer. */
int newSize = Math.max(rem, 1);
ByteBuffer bb = Util.getTemporaryDirectBuffer(newSize);
try {
int n = receiveIntoNativeBuffer(fd, resultContainer, bb, newSize, 0);
bb.flip();
if (n > 0 && rem > 0)
dst.put(bb);
return n;
} finally {
Util.releaseTemporaryDirectBuffer(bb);
}
}
private int receiveIntoNativeBuffer(int fd,
SctpResultContainer resultContainer,
ByteBuffer bb,
int rem,
int pos)
throws IOException {
int n = receive0(fd, resultContainer, ((DirectBuffer)bb).address() + pos, rem);
if (n > 0)
bb.position(pos + n);
return n;
}
private InternalNotificationHandler internalNotificationHandler =
new InternalNotificationHandler();
private void handleNotificationInternal(SctpResultContainer resultContainer)
{
invokeNotificationHandler(resultContainer,
internalNotificationHandler, null);
}
private class InternalNotificationHandler<T>
extends AbstractNotificationHandler<T>
{
@Override
public HandlerResult handleNotification(
AssociationChangeNotification not, T unused) {
SctpAssocChange sac = (SctpAssocChange) not;
/* Update map to reflect change in association */
switch (not.event()) {
case COMM_UP :
Association newAssociation = new SctpAssociationImpl
(sac.assocId(), sac.maxInStreams(), sac.maxOutStreams());
addAssociation(newAssociation);
break;
case SHUTDOWN :
case COMM_LOST :
//case RESTART: ???
/* mark association for removal after user handler invoked*/
associationToRemove.set(lookupAssociation(sac.assocId()));
}
return HandlerResult.CONTINUE;
}
}
private <T> HandlerResult invokeNotificationHandler(
SctpResultContainer resultContainer,
NotificationHandler<T> handler,
T attachment) {
HandlerResult result;
SctpNotification notification = resultContainer.notification();
notification.setAssociation(lookupAssociation(notification.assocId()));
if (!(handler instanceof AbstractNotificationHandler)) {
result = handler.handleNotification(notification, attachment);
} else { /* AbstractNotificationHandler */
AbstractNotificationHandler absHandler =
(AbstractNotificationHandler)handler;
switch(resultContainer.type()) {
case ASSOCIATION_CHANGED :
result = absHandler.handleNotification(
resultContainer.getAssociationChanged(), attachment);
case PEER_ADDRESS_CHANGED :
result = absHandler.handleNotification(
resultContainer.getPeerAddressChanged(), attachment);
case SEND_FAILED :
result = absHandler.handleNotification(
resultContainer.getSendFailed(), attachment);
case SHUTDOWN :
result = absHandler.handleNotification(
resultContainer.getShutdown(), attachment);
default :
/* implementation specific handlers */
result = absHandler.handleNotification(
resultContainer.notification(), attachment);
}
}
if (!(handler instanceof InternalNotificationHandler)) {
/* Only remove associations after user handler
* has finished with them */
Association assoc = associationToRemove.get();
if (assoc != null) {
removeAssociation(assoc);
associationToRemove.set(null);
}
}
return result;
}
private Association lookupAssociation(int assocId) {
/* Lookup the association in our internal map */
synchronized (stateLock) {
Set<Association> assocs = associationMap.keySet();
for (Association a : assocs) {
if (a.associationID() == assocId) {
return a;
}
}
}
return null;
}
private void addAssociation(Association association) {
synchronized (stateLock) {
int assocId = association.associationID();
Set<SocketAddress> addresses = null;
try {
addresses = SctpNet.getRemoteAddresses(fdVal, assocId);
} catch (IOException unused) {
/* OK, determining connected addresses may not be possible
* shutdown, connection lost, etc */
}
associationMap.put(association, addresses);
if (addresses != null) {
for (SocketAddress addr : addresses)
addressMap.put(addr, association);
}
}
}
private void removeAssociation(Association association) {
synchronized (stateLock) {
int assocId = association.associationID();
Set<SocketAddress> addresses = null;
try {
addresses = SctpNet.getRemoteAddresses(fdVal, assocId);
} catch (IOException unused) {
/* OK, determining connected addresses may not be possible
* shutdown, connection lost, etc */
}
Set<Association> assocs = associationMap.keySet();
for (Association a : assocs) {
if (a.associationID() == assocId) {
associationMap.remove(a);
break;
}
}
if (addresses != null) {
for (SocketAddress addr : addresses)
addressMap.remove(addr);
} else {
/* We cannot determine the connected addresses */
Set<java.util.Map.Entry<SocketAddress, Association>> addrAssocs =
addressMap.entrySet();
for (java.util.Map.Entry<SocketAddress, Association> entry : addrAssocs) {
if (entry.getValue().equals(association)) {
addressMap.remove(entry.getKey());
}
}
}
}
}
/**
* @throws IllegalArgumentException
* If the given association is not controlled by this channel
*
* @return {@code true} if, and only if, the given association is one
* of the current associations controlled by this channel
*/
private boolean checkAssociation(Association messageAssoc) {
synchronized (stateLock) {
for (Association association : associationMap.keySet()) {
if (messageAssoc.equals(association)) {
return true;
}
}
}
throw new IllegalArgumentException(
"Given Association is not controlled by this channel");
}
private void checkStreamNumber(Association assoc, int streamNumber) {
synchronized (stateLock) {
if (streamNumber < 0 || streamNumber >= assoc.maxOutboundStreams())
throw new InvalidStreamException();
}
}
/* TODO: Add support for ttl and isComplete to both 121 12M
* SCTP_EOR not yet supported on reference platforms
* TTL support limited...
*/
@Override
public int send(ByteBuffer buffer, MessageInfo messageInfo)
throws IOException {
if (buffer == null)
throw new IllegalArgumentException("buffer cannot be null");
if (messageInfo == null)
throw new IllegalArgumentException("messageInfo cannot be null");
synchronized (sendLock) {
ensureOpen();
if (!isBound())
bind(null, 0);
int n = 0;
try {
int assocId = -1;
SocketAddress address = null;
begin();
synchronized (stateLock) {
if(!isOpen())
return 0;
senderThread = NativeThread.current();
/* Determine what address or association to send to */
Association assoc = messageInfo.association();
InetSocketAddress addr = (InetSocketAddress)messageInfo.address();
if (assoc != null) {
checkAssociation(assoc);
checkStreamNumber(assoc, messageInfo.streamNumber());
assocId = assoc.associationID();
/* have we also got a preferred address */
if (addr != null) {
if (!assoc.equals(addressMap.get(addr)))
throw new IllegalArgumentException("given preferred address is not part of this association");
address = addr;
}
} else if (addr != null) {
address = addr;
Association association = addressMap.get(addr);
if (association != null) {
checkStreamNumber(association, messageInfo.streamNumber());
assocId = association.associationID();
} else { /* must be new association */
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkConnect(addr.getAddress().getHostAddress(),
addr.getPort());
}
} else {
throw new AssertionError(
"Both association and address cannot be null");
}
}
do {
n = send(fdVal, buffer, assocId, address, messageInfo);
} while ((n == IOStatus.INTERRUPTED) && isOpen());
return IOStatus.normalize(n);
} finally {
senderCleanup();
end((n > 0) || (n == IOStatus.UNAVAILABLE));
assert IOStatus.check(n);
}
}
}
private int send(int fd,
ByteBuffer src,
int assocId,
SocketAddress target,
MessageInfo messageInfo)
throws IOException {
int streamNumber = messageInfo.streamNumber();
boolean unordered = messageInfo.isUnordered();
int ppid = messageInfo.payloadProtocolID();
int pos = src.position();
int lim = src.limit();
assert (pos <= lim && streamNumber > 0);
int rem = (pos <= lim ? lim - pos : 0);
if (src instanceof DirectBuffer)
return sendFromNativeBuffer(fd, src, rem, pos, target, assocId,
streamNumber, unordered, ppid);
/* Substitute a native buffer */
ByteBuffer bb = Util.getTemporaryDirectBuffer(rem);
try {
bb.put(src);
bb.flip();
/* Do not update src until we see how many bytes were written */
src.position(pos);
int n = sendFromNativeBuffer(fd, bb, rem, pos, target, assocId,
streamNumber, unordered, ppid);
if (n > 0) {
/* now update src */
src.position(pos + n);
}
return n;
} finally {
Util.releaseTemporaryDirectBuffer(bb);
}
}
private int sendFromNativeBuffer(int fd,
ByteBuffer bb,
int rem,
int pos,
SocketAddress target,
int assocId,
int streamNumber,
boolean unordered,
int ppid)
throws IOException {
int written = send0(fd, ((DirectBuffer)bb).address() + pos,
rem, target, assocId, streamNumber, unordered, ppid);
if (written > 0)
bb.position(pos + written);
return written;
}
@Override
public SctpMultiChannel shutdown(Association association)
throws IOException {
synchronized (stateLock) {
checkAssociation(association);
if (!isOpen())
throw new ClosedChannelException();
SctpNet.shutdown(fdVal, association.associationID());
}
return this;
}
@Override
public Set<SocketAddress> getAllLocalAddresses()
throws IOException {
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
if (!isBound())
return Collections.EMPTY_SET;
return SctpNet.getLocalAddresses(fdVal);
}
}
@Override
public Set<SocketAddress> getRemoteAddresses(Association association)
throws IOException {
synchronized (stateLock) {
checkAssociation(association);
if (!isOpen())
throw new ClosedChannelException();
return SctpNet.getRemoteAddresses(fdVal, association.associationID());
}
}
@Override
public SctpChannel branch(Association association)
throws IOException {
synchronized (stateLock) {
return null; //TODO: implement
}
}
/* Use common native implementation shared between
* one-to-one and one-to-many */
private static int receive0(int fd,
SctpResultContainer resultContainer,
long address,
int length)
throws IOException{
return SctpChannelImpl.receive0(fd, resultContainer, address,
length);
}
private static int send0(int fd,
long address,
int length,
SocketAddress target,
int assocId,
int streamNumber,
boolean unordered,
int ppid)
throws IOException {
return SctpChannelImpl.send0(fd, address, length, target, assocId,
streamNumber, unordered, ppid);
}
static {
Util.load(); /* loads nio & net native libraries */
java.security.AccessController.doPrivileged(
new sun.security.action.LoadLibraryAction("sctp"));
nd = new SctpSocketDispatcher();
}
}

View File

@ -0,0 +1,268 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.nio.ch;
import java.io.FileDescriptor;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Set;
import java.util.HashSet;
import java.security.AccessController;
import sun.security.action.GetPropertyAction;
import com.sun.nio.sctp.SctpSocketOption;
import static com.sun.nio.sctp.SctpStandardSocketOption.*;
public class SctpNet {
static final String osName = AccessController.doPrivileged(
new GetPropertyAction("os.name"));
/* -- Miscellaneous SCTP utilities -- */
static boolean bindxIPv4MappedAddresses() {
if ("SunOS".equals(osName)) {
/* Solaris supports IPv4Mapped Addresses with bindx */
return true;
} /* else { //other OS/implementations */
/* lksctp/linux requires Ipv4 addresses */
return false;
}
/**
* @param oneToone
* if {@code true} returns a one-to-one sctp socket, otherwise
* returns a one-to-many sctp socket
*/
static FileDescriptor socket(boolean oneToOne) throws IOException {
int nativefd = socket0(oneToOne);
return IOUtil.newFD(nativefd);
}
static void bindx(int fd, InetAddress[] addrs, int port, boolean add)
throws IOException {
bindx(fd, addrs, port, addrs.length, add,
bindxIPv4MappedAddresses());
}
static Set<SocketAddress> getLocalAddresses(int fd)
throws IOException {
HashSet<SocketAddress> set = null;
SocketAddress[] saa = getLocalAddresses0(fd);
if (saa != null) {
set = new HashSet<SocketAddress>(saa.length);
for (SocketAddress sa : saa)
set.add(sa);
}
return set;
}
static Set<SocketAddress> getRemoteAddresses(int fd, int assocId)
throws IOException {
HashSet<SocketAddress> set = null;
SocketAddress[] saa = getRemoteAddresses0(fd, assocId);
if (saa != null) {
set = new HashSet<SocketAddress>(saa.length);
for (SocketAddress sa : saa)
set.add(sa);
}
return set;
}
static void setSocketOption(int fd,
SctpSocketOption name,
Object value,
int assocId)
throws IOException {
if (value == null)
throw new IllegalArgumentException("Invalid option value");
Class<?> type = name.type();
if (!type.isInstance(value))
throw new IllegalArgumentException("Invalid option value");
if (name.equals(SCTP_INIT_MAXSTREAMS)) {
InitMaxStreams maxStreamValue = (InitMaxStreams)value;
SctpNet.setInitMsgOption0(fd,
maxStreamValue.maxInStreams(), maxStreamValue.maxOutStreams());
} else if (name.equals(SCTP_PRIMARY_ADDR) ||
name.equals(SCTP_SET_PEER_PRIMARY_ADDR)) {
SocketAddress addr = (SocketAddress) value;
if (addr == null)
throw new IllegalArgumentException("Invalid option value");
Net.checkAddress(addr);
InetSocketAddress netAddr = (InetSocketAddress)addr;
if (name.equals(SCTP_PRIMARY_ADDR)) {
setPrimAddrOption0(fd, assocId,
netAddr.getAddress(), netAddr.getPort());
} else {
setPeerPrimAddrOption0(fd, assocId,
netAddr.getAddress(), netAddr.getPort());
}
} else if (name.equals(SCTP_DISABLE_FRAGMENTS) ||
name.equals(SCTP_EXPLICIT_COMPLETE) ||
name.equals(SCTP_FRAGMENT_INTERLEAVE) ||
name.equals(SCTP_NODELAY) ||
name.equals(SO_SNDBUF) ||
name.equals(SO_RCVBUF) ||
name.equals(SO_LINGER)) {
setIntOption(fd, name, value);
} else {
throw new AssertionError("Unknown socket option");
}
}
static Object getSocketOption(int fd, SctpSocketOption name, int assocId)
throws IOException {
if (name.equals(SCTP_SET_PEER_PRIMARY_ADDR)) {
throw new IllegalArgumentException(
"SCTP_SET_PEER_PRIMARY_ADDR cannot be retrieved");
} else if (name.equals(SCTP_INIT_MAXSTREAMS)) {
/* container for holding maxIn/Out streams */
int[] values = new int[2];
SctpNet.getInitMsgOption0(fd, values);
return InitMaxStreams.create(values[0], values[1]);
} else if (name.equals(SCTP_PRIMARY_ADDR)) {
return getPrimAddrOption0(fd, assocId);
} else if (name.equals(SCTP_DISABLE_FRAGMENTS) ||
name.equals(SCTP_EXPLICIT_COMPLETE) ||
name.equals(SCTP_FRAGMENT_INTERLEAVE) ||
name.equals(SCTP_NODELAY) ||
name.equals(SO_SNDBUF) ||
name.equals(SO_RCVBUF) ||
name.equals(SO_LINGER)) {
return getIntOption(fd, name);
} else {
throw new AssertionError("Unknown socket option");
}
}
static void setIntOption(int fd, SctpSocketOption name, Object value)
throws IOException {
if (value == null)
throw new IllegalArgumentException("Invalid option value");
Class<?> type = name.type();
if (type != Integer.class && type != Boolean.class)
throw new AssertionError("Should not reach here");
if (name == SO_RCVBUF ||
name == SO_SNDBUF)
{
int i = ((Integer)value).intValue();
if (i < 0)
throw new IllegalArgumentException(
"Invalid send/receive buffer size");
} else if (name == SO_LINGER) {
int i = ((Integer)value).intValue();
if (i < 0)
value = Integer.valueOf(-1);
if (i > 65535)
value = Integer.valueOf(65535);
} else if (name.equals(SCTP_FRAGMENT_INTERLEAVE)) {
int i = ((Integer)value).intValue();
if (i < 0 || i > 2)
throw new IllegalArgumentException(
"Invalid value for SCTP_FRAGMENT_INTERLEAVE");
}
int arg;
if (type == Integer.class) {
arg = ((Integer)value).intValue();
} else {
boolean b = ((Boolean)value).booleanValue();
arg = (b) ? 1 : 0;
}
setIntOption0(fd, ((SctpStdSocketOption)name).constValue(), arg);
}
static Object getIntOption(int fd, SctpSocketOption name)
throws IOException {
Class<?> type = name.type();
if (type != Integer.class && type != Boolean.class)
throw new AssertionError("Should not reach here");
if (!(name instanceof SctpStdSocketOption))
throw new AssertionError("Should not reach here");
int value = getIntOption0(fd,
((SctpStdSocketOption)name).constValue());
if (type == Integer.class) {
return Integer.valueOf(value);
} else {
return (value == 0) ? Boolean.FALSE : Boolean.TRUE;
}
}
static void shutdown(int fd, int assocId)
throws IOException {
shutdown0(fd, assocId);
}
/* Native Methods */
static native int socket0(boolean oneToOne) throws IOException;
static native void bindx(int fd, InetAddress[] addrs, int port, int length,
boolean add, boolean preferIPv6) throws IOException;
static native int getIntOption0(int fd, int opt) throws IOException;
static native void setIntOption0(int fd, int opt, int arg)
throws IOException;
static native SocketAddress[] getLocalAddresses0(int fd) throws IOException;
static native SocketAddress[] getRemoteAddresses0(int fd, int assocId)
throws IOException;
static native void setPrimAddrOption0(int fd, int assocId, InetAddress ia,
int port) throws IOException;
static native void setPeerPrimAddrOption0(int fd, int assocId,
InetAddress ia, int port) throws IOException;
static native SocketAddress getPrimAddrOption0(int fd, int assocId)
throws IOException;
/* retVals [0] maxInStreams, [1] maxOutStreams */
static native void getInitMsgOption0(int fd, int[] retVals) throws IOException;
static native void setInitMsgOption0(int fd, int arg1, int arg2)
throws IOException;
static native void shutdown0(int fd, int assocId);
}

View File

@ -0,0 +1,37 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.nio.ch;
import com.sun.nio.sctp.Association;
import com.sun.nio.sctp.Notification;
/**
* All Notification implemenations MUST implement this interface to provide
* access to the native association identidier.
*/
interface SctpNotification extends Notification {
int assocId();
void setAssociation(Association association);
}

View File

@ -0,0 +1,119 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.nio.ch;
import java.net.SocketAddress;
import com.sun.nio.sctp.Association;
import com.sun.nio.sctp.PeerAddressChangeNotification;
/**
* An implementation of PeerAddressChangeNotification
*/
public class SctpPeerAddrChange extends PeerAddressChangeNotification
implements SctpNotification
{
/* static final ints so that they can be referenced from native */
private final static int SCTP_ADDR_AVAILABLE = 1;
private final static int SCTP_ADDR_UNREACHABLE = 2;
private final static int SCTP_ADDR_REMOVED = 3;
private final static int SCTP_ADDR_ADDED = 4;
private final static int SCTP_ADDR_MADE_PRIM = 5;
private final static int SCTP_ADDR_CONFIRMED =6;
private Association association;
/* assocId is used to lookup the association before the notification is
* returned to user code */
private int assocId;
private SocketAddress address;
private AddressChangeEvent event;
/* Invoked from native */
private SctpPeerAddrChange(int assocId, SocketAddress address, int intEvent) {
switch (intEvent) {
case SCTP_ADDR_AVAILABLE :
this.event = AddressChangeEvent.ADDR_AVAILABLE;
break;
case SCTP_ADDR_UNREACHABLE :
this.event = AddressChangeEvent.ADDR_UNREACHABLE;
break;
case SCTP_ADDR_REMOVED :
this.event = AddressChangeEvent.ADDR_REMOVED;
break;
case SCTP_ADDR_ADDED :
this.event = AddressChangeEvent.ADDR_ADDED;
break;
case SCTP_ADDR_MADE_PRIM :
this.event = AddressChangeEvent.ADDR_MADE_PRIMARY;
break;
case SCTP_ADDR_CONFIRMED :
this.event = AddressChangeEvent.ADDR_CONFIRMED;
break;
default:
throw new AssertionError("Unknown event type");
}
this.assocId = assocId;
this.address = address;
}
@Override
public int assocId() {
return assocId;
}
@Override
public void setAssociation(Association association) {
this.association = association;
}
@Override
public SocketAddress address() {
assert address != null;
return address;
}
@Override
public Association association() {
assert association != null;
return association;
}
@Override
public AddressChangeEvent event() {
assert event != null;
return event;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(super.toString()).append(" [");
sb.append("Address: ").append(address);
sb.append(", Association:").append(association);
sb.append(", Event: ").append(event).append("]");
return sb.toString();
}
}

View File

@ -0,0 +1,126 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.nio.ch;
/**
* Wraps the actual message or notification so that it can be
* set and returned from the native receive implementation.
*/
public class SctpResultContainer {
/* static final ints so that they can be referenced from native */
static final int NOTHING = 0;
static final int MESSAGE = 1;
static final int SEND_FAILED = 2;
static final int ASSOCIATION_CHANGED = 3;
static final int PEER_ADDRESS_CHANGED = 4;
static final int SHUTDOWN = 5;
private Object value;
private int type;
int type() {
return type;
}
boolean hasSomething() {
return type() != NOTHING;
}
boolean isNotification() {
return type() != MESSAGE && type() != NOTHING ? true : false;
}
void clear() {
type = NOTHING;
value = null;
}
SctpNotification notification() {
assert type() != MESSAGE && type() != NOTHING;
return (SctpNotification) value;
}
SctpMessageInfoImpl getMessageInfo() {
assert type() == MESSAGE;
if (value instanceof SctpMessageInfoImpl)
return (SctpMessageInfoImpl) value;
return null;
}
SctpSendFailed getSendFailed() {
assert type() == SEND_FAILED;
if (value instanceof SctpSendFailed)
return (SctpSendFailed) value;
return null;
}
SctpAssocChange getAssociationChanged() {
assert type() == ASSOCIATION_CHANGED;
if (value instanceof SctpAssocChange)
return (SctpAssocChange) value;
return null;
}
SctpPeerAddrChange getPeerAddressChanged() {
assert type() == PEER_ADDRESS_CHANGED;
if (value instanceof SctpPeerAddrChange)
return (SctpPeerAddrChange) value;
return null;
}
SctpShutdown getShutdown() {
assert type() == SHUTDOWN;
if (value instanceof SctpShutdown)
return (SctpShutdown) value;
return null;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Type: ");
switch (type) {
case NOTHING: sb.append("NOTHING"); break;
case MESSAGE: sb.append("MESSAGE"); break;
case SEND_FAILED: sb.append("SEND FAILED"); break;
case ASSOCIATION_CHANGED: sb.append("ASSOCIATION CHANGE"); break;
case PEER_ADDRESS_CHANGED: sb.append("PEER ADDRESS CHANGE"); break;
case SHUTDOWN: sb.append("SHUTDOWN"); break;
default : sb.append("Unknown result type");
}
return sb.append(", Value: ").append(value.toString()).toString();
}
}

View File

@ -0,0 +1,110 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.nio.ch;
import java.nio.ByteBuffer;
import java.net.SocketAddress;
import com.sun.nio.sctp.Association;
import com.sun.nio.sctp.SendFailedNotification;
/**
* An implementation of SendFailedNotification
*/
public class SctpSendFailed extends SendFailedNotification
implements SctpNotification
{
private Association association;
/* assocId is used to lookup the association before the notification is
* returned to user code */
private int assocId;
private SocketAddress address;
private ByteBuffer buffer;
private int errorCode;
private int streamNumber;
/* Invoked from native */
private SctpSendFailed(int assocId,
SocketAddress address,
ByteBuffer buffer,
int errorCode,
int streamNumber) {
this.assocId = assocId;
this.errorCode = errorCode;
this.streamNumber = streamNumber;
this.address = address;
this.buffer = buffer;
}
@Override
public int assocId() {
return assocId;
}
@Override
public void setAssociation(Association association) {
this.association = association;
}
@Override
public Association association() {
/* may be null */
return association;
}
@Override
public SocketAddress address() {
assert address != null;
return address;
}
@Override
public ByteBuffer buffer() {
assert buffer != null;
return buffer;
}
@Override
public int errorCode() {
return errorCode;
}
@Override
public int streamNumber() {
return streamNumber;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(super.toString()).append(" [");
sb.append("Association:").append(association);
sb.append(", Address: ").append(address);
sb.append(", buffer: ").append(buffer);
sb.append(", errorCode: ").append(errorCode);
sb.append(", streamNumber: ").append(streamNumber);
sb.append("]");
return sb.toString();
}
}

View File

@ -0,0 +1,429 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.nio.ch;
import java.net.SocketAddress;
import java.net.InetSocketAddress;
import java.net.InetAddress;
import java.io.FileDescriptor;
import java.io.IOException;
import java.util.Collections;
import java.util.Set;
import java.util.HashSet;
import java.nio.channels.SelectionKey;
import java.nio.channels.AlreadyBoundException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.NotYetBoundException;
import java.nio.channels.spi.SelectorProvider;
import com.sun.nio.sctp.IllegalUnbindException;
import com.sun.nio.sctp.SctpChannel;
import com.sun.nio.sctp.SctpServerChannel;
import com.sun.nio.sctp.SctpSocketOption;
import com.sun.nio.sctp.SctpStandardSocketOption;
/**
* An implementation of SctpServerChannel
*/
public class SctpServerChannelImpl extends SctpServerChannel
implements SelChImpl
{
/* Used to make native close and preClose calls */
private static NativeDispatcher nd;
private final FileDescriptor fd;
private final int fdVal;
/* IDs of native thread doing accept, for signalling */
private volatile long thread = 0;
/* Lock held by thread currently blocked in this channel */
private final Object lock = new Object();
/* Lock held by any thread that modifies the state fields declared below
* DO NOT invoke a blocking I/O operation while holding this lock! */
private final Object stateLock = new Object();
private enum ChannelState {
UNINITIALIZED,
INUSE,
KILLPENDING,
KILLED,
}
/* -- The following fields are protected by stateLock -- */
private ChannelState state = ChannelState.UNINITIALIZED;
/* Binding: Once bound the port will remain constant. */
int port = -1;
private HashSet<InetSocketAddress> localAddresses = new HashSet<InetSocketAddress>();
/* Has the channel been bound to the wildcard address */
private boolean wildcard; /* false */
/* -- End of fields protected by stateLock -- */
/**
* Initializes a new instance of this class.
*/
public SctpServerChannelImpl(SelectorProvider provider)
throws IOException {
//TODO: update provider remove public modifier
super(provider);
this.fd = SctpNet.socket(true);
this.fdVal = IOUtil.fdVal(fd);
this.state = ChannelState.INUSE;
}
@Override
public SctpServerChannel bind(SocketAddress local, int backlog)
throws IOException {
synchronized (lock) {
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
if (isBound())
throw new AlreadyBoundException();
InetSocketAddress isa = (local == null) ?
new InetSocketAddress(0) : Net.checkAddress(local);
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkListen(isa.getPort());
Net.bind(fd, isa.getAddress(), isa.getPort());
InetSocketAddress boundIsa = Net.localAddress(fd);
port = boundIsa.getPort();
localAddresses.add(isa);
if (isa.getAddress().isAnyLocalAddress())
wildcard = true;
Net.listen(fd, backlog < 1 ? 50 : backlog);
}
}
return this;
}
@Override
public SctpServerChannel bindAddress(InetAddress address)
throws IOException {
return bindUnbindAddress(address, true);
}
@Override
public SctpServerChannel unbindAddress(InetAddress address)
throws IOException {
return bindUnbindAddress(address, false);
}
private SctpServerChannel bindUnbindAddress(InetAddress address, boolean add)
throws IOException {
if (address == null)
throw new IllegalArgumentException();
synchronized (lock) {
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
if (!isBound())
throw new NotYetBoundException();
if (wildcard)
throw new IllegalStateException(
"Cannot add or remove addresses from a channel that is bound to the wildcard address");
if (address.isAnyLocalAddress())
throw new IllegalArgumentException(
"Cannot add or remove the wildcard address");
if (add) {
for (InetSocketAddress addr : localAddresses) {
if (addr.getAddress().equals(address)) {
throw new AlreadyBoundException();
}
}
} else { /*removing */
/* Verify that there is more than one address
* and that address is already bound */
if (localAddresses.size() <= 1)
throw new IllegalUnbindException("Cannot remove address from a channel with only one address bound");
boolean foundAddress = false;
for (InetSocketAddress addr : localAddresses) {
if (addr.getAddress().equals(address)) {
foundAddress = true;
break;
}
}
if (!foundAddress )
throw new IllegalUnbindException("Cannot remove address from a channel that is not bound to that address");
}
SctpNet.bindx(fdVal, new InetAddress[]{address}, port, add);
/* Update our internal Set to reflect the addition/removal */
if (add)
localAddresses.add(new InetSocketAddress(address, port));
else {
for (InetSocketAddress addr : localAddresses) {
if (addr.getAddress().equals(address)) {
localAddresses.remove(addr);
break;
}
}
}
}
}
return this;
}
private boolean isBound() {
synchronized (stateLock) {
return port == -1 ? false : true;
}
}
private void acceptCleanup() throws IOException {
synchronized (stateLock) {
thread = 0;
if (state == ChannelState.KILLPENDING)
kill();
}
}
@Override
public SctpChannel accept() throws IOException {
synchronized (lock) {
if (!isOpen())
throw new ClosedChannelException();
if (!isBound())
throw new NotYetBoundException();
SctpChannel sc = null;
int n = 0;
FileDescriptor newfd = new FileDescriptor();
InetSocketAddress[] isaa = new InetSocketAddress[1];
try {
begin();
if (!isOpen())
return null;
thread = NativeThread.current();
for (;;) {
n = accept0(fd, newfd, isaa);
if ((n == IOStatus.INTERRUPTED) && isOpen())
continue;
break;
}
} finally {
acceptCleanup();
end(n > 0);
assert IOStatus.check(n);
}
if (n < 1)
return null;
IOUtil.configureBlocking(newfd, true);
InetSocketAddress isa = isaa[0];
sc = new SctpChannelImpl(provider(), newfd);
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkAccept(isa.getAddress().getHostAddress(),
isa.getPort());
return sc;
}
}
@Override
protected void implConfigureBlocking(boolean block) throws IOException {
IOUtil.configureBlocking(fd, block);
}
@Override
public void implCloseSelectableChannel() throws IOException {
synchronized (stateLock) {
nd.preClose(fd);
if (thread != 0)
NativeThread.signal(thread);
if (!isRegistered())
kill();
}
}
@Override
public void kill() throws IOException {
synchronized (stateLock) {
if (state == ChannelState.KILLED)
return;
if (state == ChannelState.UNINITIALIZED) {
state = ChannelState.KILLED;
return;
}
assert !isOpen() && !isRegistered();
// Postpone the kill if there is a thread in accept
if (thread == 0) {
nd.close(fd);
state = ChannelState.KILLED;
} else {
state = ChannelState.KILLPENDING;
}
}
}
@Override
public FileDescriptor getFD() {
return fd;
}
@Override
public int getFDVal() {
return fdVal;
}
/**
* Translates native poll revent ops into a ready operation ops
*/
private boolean translateReadyOps(int ops, int initialOps,
SelectionKeyImpl sk) {
int intOps = sk.nioInterestOps();
int oldOps = sk.nioReadyOps();
int newOps = initialOps;
if ((ops & PollArrayWrapper.POLLNVAL) != 0) {
/* This should only happen if this channel is pre-closed while a
* selection operation is in progress
* ## Throw an error if this channel has not been pre-closed */
return false;
}
if ((ops & (PollArrayWrapper.POLLERR
| PollArrayWrapper.POLLHUP)) != 0) {
newOps = intOps;
sk.nioReadyOps(newOps);
return (newOps & ~oldOps) != 0;
}
if (((ops & PollArrayWrapper.POLLIN) != 0) &&
((intOps & SelectionKey.OP_ACCEPT) != 0))
newOps |= SelectionKey.OP_ACCEPT;
sk.nioReadyOps(newOps);
return (newOps & ~oldOps) != 0;
}
@Override
public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) {
return translateReadyOps(ops, sk.nioReadyOps(), sk);
}
@Override
public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) {
return translateReadyOps(ops, 0, sk);
}
@Override
public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) {
int newOps = 0;
/* Translate ops */
if ((ops & SelectionKey.OP_ACCEPT) != 0)
newOps |= PollArrayWrapper.POLLIN;
/* Place ops into pollfd array */
sk.selector.putEventOps(sk, newOps);
}
@Override
public <T> SctpServerChannel setOption(SctpSocketOption<T> name, T value)
throws IOException {
if (name == null)
throw new NullPointerException();
if (!supportedOptions().contains(name))
throw new UnsupportedOperationException("'" + name + "' not supported");
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
SctpNet.setSocketOption(fdVal, name, value, 0 /*oneToOne*/);
return this;
}
}
@Override
public <T> T getOption(SctpSocketOption<T> name) throws IOException {
if (name == null)
throw new NullPointerException();
if (!supportedOptions().contains(name))
throw new UnsupportedOperationException("'" + name + "' not supported");
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
return (T) SctpNet.getSocketOption(fdVal, name, 0 /*oneToOne*/);
}
}
private static class DefaultOptionsHolder {
static final Set<SctpSocketOption<?>> defaultOptions = defaultOptions();
private static Set<SctpSocketOption<?>> defaultOptions() {
HashSet<SctpSocketOption<?>> set = new HashSet<SctpSocketOption<?>>(1);
set.add(SctpStandardSocketOption.SCTP_INIT_MAXSTREAMS);
return Collections.unmodifiableSet(set);
}
}
@Override
public final Set<SctpSocketOption<?>> supportedOptions() {
return DefaultOptionsHolder.defaultOptions;
}
@Override
public Set<SocketAddress> getAllLocalAddresses()
throws IOException {
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
if (!isBound())
return null;
return SctpNet.getLocalAddresses(fdVal);
}
}
/* Native */
private static native void initIDs();
private static native int accept0(FileDescriptor ssfd,
FileDescriptor newfd, InetSocketAddress[] isaa) throws IOException;
static {
Util.load(); // loads nio & net native libraries
java.security.AccessController.doPrivileged(
new sun.security.action.LoadLibraryAction("sctp"));
nd = new SctpSocketDispatcher();
initIDs();
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.nio.ch;
import com.sun.nio.sctp.Association;
import com.sun.nio.sctp.ShutdownNotification;
/**
* An implementation of ShutdownNotification
*/
public class SctpShutdown extends ShutdownNotification
implements SctpNotification
{
private Association association;
/* assocId is used to lookup the association before the notification is
* returned to user code */
private int assocId;
/* Invoked from native */
private SctpShutdown(int assocId) {
this.assocId = assocId;
}
@Override
public int assocId() {
return assocId;
}
@Override
public void setAssociation(Association association) {
this.association = association;
}
@Override
public Association association() {
assert association != null;
return association;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(super.toString()).append(" [");
sb.append("Association:").append(association).append("]");
return sb.toString();
}
}

View File

@ -0,0 +1,68 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.nio.ch;
import java.io.IOException;
import java.io.FileDescriptor;
/**
* Only used for {@code close} and {@code preclose}. All other methods
* throw {@code IOException}.
*/
class SctpSocketDispatcher extends NativeDispatcher {
@Override
@SuppressWarnings("unused")
int read(FileDescriptor fd, long address, int len) throws IOException {
throw new IOException("Operation Unsupported");
}
@Override
@SuppressWarnings("unused")
long readv(FileDescriptor fd, long address, int len) throws IOException {
throw new IOException("Operation Unsupported");
}
@Override
@SuppressWarnings("unused")
int write(FileDescriptor fd, long address, int len) throws IOException {
throw new IOException("Operation Unsupported");
}
@Override
@SuppressWarnings("unused")
long writev(FileDescriptor fd, long address, int len) throws IOException {
throw new IOException("Operation Unsupported");
}
@Override
void close(FileDescriptor fd) throws IOException {
FileDispatcherImpl.close0(fd);
}
@Override
void preClose(FileDescriptor fd) throws IOException {
FileDispatcherImpl.preClose0(fd);
}
}

View File

@ -0,0 +1,329 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
#ifndef SUN_NIO_CH_SCTP_H
#define SUN_NIO_CH_SCTP_H
#ifdef __solaris__
#define _XPG4_2
#define __EXTENSIONS__
#include <sys/socket.h>
#include <netinet/sctp.h>
#include "jni.h"
/* Current Solaris headers don't comply with draft rfc */
#ifndef SCTP_EOF
#define SCTP_EOF MSG_EOF
#endif
#ifndef SCTP_UNORDERED
#define SCTP_UNORDERED MSG_UNORDERED
#endif
/* The current version of the socket API extension shipped with Solaris does
* not define the following options that the Java API (optionally) supports */
#ifndef SCTP_EXPLICIT_EOR
#define SCTP_EXPLICIT_EOR -1
#endif
#ifndef SCTP_FRAGMENT_INTERLEAVE
#define SCTP_FRAGMENT_INTERLEAVE -1
#endif
#ifndef SCTP_SET_PEER_PRIMARY_ADDR
#define SCTP_SET_PEER_PRIMARY_ADDR -1
#endif
/* Function types to support dynamic linking of socket API extension functions
* for SCTP. This is so that there is no linkage depandancy during build or
* runtime for libsctp.*/
typedef int sctp_getladdrs_func(int sock, sctp_assoc_t id, void **addrs);
typedef int sctp_freeladdrs_func(void* addrs);
typedef int sctp_getpaddrs_func(int sock, sctp_assoc_t id, void **addrs);
typedef int sctp_freepaddrs_func(void *addrs);
typedef int sctp_bindx_func(int sock, void *addrs, int addrcnt, int flags);
#else /* __linux__ */
#include <stdint.h>
#include <linux/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "jni.h"
//Causes compiler error if not found, should make warning and uncomment
/*#include <netinet/sctp.h>*/
#ifndef IPPROTO_SCTP
#define IPPROTO_SCTP 132
#endif
/* The current version of lksctp does
* not define the following option that the Java API (optionally) supports */
#ifndef SCTP_EXPLICIT_EOR
#define SCTP_EXPLICIT_EOR -1
#endif
/* Definitions taken from lksctp-tools-1.0.8/src/include/netinet/sctp.h */
#ifndef SCTP_INITMSG
enum sctp_optname {
SCTP_RTOINFO,
#define SCTP_RTOINFO SCTP_RTOINFO
SCTP_ASSOCINFO,
#define SCTP_ASSOCINFO SCTP_ASSOCINFO
SCTP_INITMSG,
#define SCTP_INITMSG SCTP_INITMSG
SCTP_NODELAY, /* Get/set nodelay option. */
#define SCTP_NODELAY SCTP_NODELAY
SCTP_AUTOCLOSE,
#define SCTP_AUTOCLOSE SCTP_AUTOCLOSE
SCTP_SET_PEER_PRIMARY_ADDR,
#define SCTP_SET_PEER_PRIMARY_ADDR SCTP_SET_PEER_PRIMARY_ADDR
SCTP_PRIMARY_ADDR,
#define SCTP_PRIMARY_ADDR SCTP_PRIMARY_ADDR
SCTP_ADAPTATION_LAYER,
#define SCTP_ADAPTATION_LAYER SCTP_ADAPTATION_LAYER
SCTP_DISABLE_FRAGMENTS,
#define SCTP_DISABLE_FRAGMENTS SCTP_DISABLE_FRAGMENTS
SCTP_PEER_ADDR_PARAMS,
#define SCTP_PEER_ADDR_PARAMS SCTP_PEER_ADDR_PARAMS
SCTP_DEFAULT_SEND_PARAM,
#define SCTP_DEFAULT_SEND_PARAM SCTP_DEFAULT_SEND_PARAM
SCTP_EVENTS,
#define SCTP_EVENTS SCTP_EVENTS
SCTP_I_WANT_MAPPED_V4_ADDR, /* Turn on/off mapped v4 addresses */
#define SCTP_I_WANT_MAPPED_V4_ADDR SCTP_I_WANT_MAPPED_V4_ADDR
SCTP_MAXSEG, /* Get/set maximum fragment. */
#define SCTP_MAXSEG SCTP_MAXSEG
SCTP_STATUS,
#define SCTP_STATUS SCTP_STATUS
SCTP_GET_PEER_ADDR_INFO,
#define SCTP_GET_PEER_ADDR_INFO SCTP_GET_PEER_ADDR_INFO
SCTP_DELAYED_ACK_TIME,
#define SCTP_DELAYED_ACK_TIME SCTP_DELAYED_ACK_TIME
SCTP_CONTEXT, /* Receive Context */
#define SCTP_CONTEXT SCTP_CONTEXT
SCTP_FRAGMENT_INTERLEAVE,
#define SCTP_FRAGMENT_INTERLEAVE SCTP_FRAGMENT_INTERLEAVE
SCTP_PARTIAL_DELIVERY_POINT, /* Set/Get partial delivery point */
#define SCTP_PARTIAL_DELIVERY_POINT SCTP_PARTIAL_DELIVERY_POINT
SCTP_MAX_BURST, /* Set/Get max burst */
#define SCTP_MAX_BURST SCTP_MAX_BURST
};
enum sctp_sac_state {
SCTP_COMM_UP,
SCTP_COMM_LOST,
SCTP_RESTART,
SCTP_SHUTDOWN_COMP,
SCTP_CANT_STR_ASSOC,
};
enum sctp_spc_state {
SCTP_ADDR_AVAILABLE,
SCTP_ADDR_UNREACHABLE,
SCTP_ADDR_REMOVED,
SCTP_ADDR_ADDED,
SCTP_ADDR_MADE_PRIM,
SCTP_ADDR_CONFIRMED,
};
enum sctp_sinfo_flags {
SCTP_UNORDERED = 1, /* Send/receive message unordered. */
SCTP_ADDR_OVER = 2, /* Override the primary destination. */
SCTP_ABORT=4, /* Send an ABORT message to the peer. */
SCTP_EOF=MSG_FIN, /* Initiate graceful shutdown process. */
};
enum sctp_sn_type {
SCTP_SN_TYPE_BASE = (1<<15),
SCTP_ASSOC_CHANGE,
SCTP_PEER_ADDR_CHANGE,
SCTP_SEND_FAILED,
SCTP_REMOTE_ERROR,
SCTP_SHUTDOWN_EVENT,
SCTP_PARTIAL_DELIVERY_EVENT,
SCTP_ADAPTATION_INDICATION,
};
typedef enum sctp_cmsg_type {
SCTP_INIT, /* 5.2.1 SCTP Initiation Structure */
#define SCTP_INIT SCTP_INIT
SCTP_SNDRCV, /* 5.2.2 SCTP Header Information Structure */
#define SCTP_SNDRCV SCTP_SNDRCV
} sctp_cmsg_t;
enum sctp_msg_flags {
MSG_NOTIFICATION = 0x8000,
#define MSG_NOTIFICATION MSG_NOTIFICATION
};
#define SCTP_BINDX_ADD_ADDR 0x01
#define SCTP_BINDX_REM_ADDR 0x02
typedef __s32 sctp_assoc_t;
struct sctp_initmsg {
__u16 sinit_num_ostreams;
__u16 sinit_max_instreams;
__u16 sinit_max_attempts;
__u16 sinit_max_init_timeo;
};
struct sctp_sndrcvinfo {
__u16 sinfo_stream;
__u16 sinfo_ssn;
__u16 sinfo_flags;
__u32 sinfo_ppid;
__u32 sinfo_context;
__u32 sinfo_timetolive;
__u32 sinfo_tsn;
__u32 sinfo_cumtsn;
sctp_assoc_t sinfo_assoc_id;
};
struct sctp_event_subscribe {
__u8 sctp_data_io_event;
__u8 sctp_association_event;
__u8 sctp_address_event;
__u8 sctp_send_failure_event;
__u8 sctp_peer_error_event;
__u8 sctp_shutdown_event;
__u8 sctp_partial_delivery_event;
__u8 sctp_adaptation_layer_event;
};
struct sctp_send_failed {
__u16 ssf_type;
__u16 ssf_flags;
__u32 ssf_length;
__u32 ssf_error;
struct sctp_sndrcvinfo ssf_info;
sctp_assoc_t ssf_assoc_id;
__u8 ssf_data[0];
};
struct sctp_assoc_change {
__u16 sac_type;
__u16 sac_flags;
__u32 sac_length;
__u16 sac_state;
__u16 sac_error;
__u16 sac_outbound_streams;
__u16 sac_inbound_streams;
sctp_assoc_t sac_assoc_id;
__u8 sac_info[0];
};
struct sctp_shutdown_event {
__u16 sse_type;
__u16 sse_flags;
__u32 sse_length;
sctp_assoc_t sse_assoc_id;
};
struct sctp_paddr_change {
__u16 spc_type;
__u16 spc_flags;
__u32 spc_length;
struct sockaddr_storage spc_aaddr;
int spc_state;
int spc_error;
sctp_assoc_t spc_assoc_id;
} __attribute__((packed, aligned(4)));
struct sctp_remote_error {
__u16 sre_type;
__u16 sre_flags;
__u32 sre_length;
__u16 sre_error;
sctp_assoc_t sre_assoc_id;
__u8 sre_data[0];
};
struct sctp_adaptation_event {
__u16 sai_type;
__u16 sai_flags;
__u32 sai_length;
__u32 sai_adaptation_ind;
sctp_assoc_t sai_assoc_id;
};
struct sctp_setprim {
sctp_assoc_t ssp_assoc_id;
struct sockaddr_storage ssp_addr;
} __attribute__((packed, aligned(4)));
struct sctp_setpeerprim {
sctp_assoc_t sspp_assoc_id;
struct sockaddr_storage sspp_addr;
} __attribute__((packed, aligned(4)));
struct sctp_pdapi_event {
__u16 pdapi_type;
__u16 pdapi_flags;
__u32 pdapi_length;
__u32 pdapi_indication;
sctp_assoc_t pdapi_assoc_id;
};
union sctp_notification {
struct {
__u16 sn_type; /* Notification type. */
__u16 sn_flags;
__u32 sn_length;
} sn_header;
struct sctp_assoc_change sn_assoc_change;
struct sctp_paddr_change sn_paddr_change;
struct sctp_remote_error sn_remote_error;
struct sctp_send_failed sn_send_failed;
struct sctp_shutdown_event sn_shutdown_event;
struct sctp_adaptation_event sn_adaptation_event;
struct sctp_pdapi_event sn_pdapi_event;
};
#endif /* SCTP_INITMSG */
/* Function types to support dynamic linking of socket API extension functions
* for SCTP. This is so that there is no linkage depandancy during build or
* runtime for libsctp.*/
typedef int sctp_getladdrs_func(int sd, sctp_assoc_t id, struct sockaddr **addrs);
typedef int sctp_freeladdrs_func(struct sockaddr *addrs);
typedef int sctp_getpaddrs_func(int sd, sctp_assoc_t id, struct sockaddr **addrs);
typedef int sctp_freepaddrs_func(struct sockaddr *addrs);
typedef int sctp_bindx_func(int sd, struct sockaddr *addrs, int addrcnt, int flags);
#endif /* __linux__ */
sctp_getladdrs_func* nio_sctp_getladdrs;
sctp_freeladdrs_func* nio_sctp_freeladdrs;
sctp_getpaddrs_func* nio_sctp_getpaddrs;
sctp_freepaddrs_func* nio_sctp_freepaddrs;
sctp_bindx_func* nio_sctp_bindx;
jboolean loadSocketExtensionFuncs(JNIEnv* env);
#endif /* !SUN_NIO_CH_SCTP_H */

View File

@ -0,0 +1,594 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
#include <stdlib.h>
#include <string.h>
#include "Sctp.h"
#include "jni.h"
#include "nio_util.h"
#include "nio.h"
#include "net_util.h"
#include "net_util_md.h"
#include "sun_nio_ch_SctpNet.h"
#include "sun_nio_ch_SctpChannelImpl.h"
#include "sun_nio_ch_SctpAssocChange.h"
#include "sun_nio_ch_SctpResultContainer.h"
#include "sun_nio_ch_SctpPeerAddrChange.h"
/* sizeof(union sctp_notification */
#define NOTIFICATION_BUFFER_SIZE 280
#define MESSAGE_IMPL_CLASS "sun/nio/ch/SctpMessageInfoImpl"
#define RESULT_CONTAINER_CLASS "sun/nio/ch/SctpResultContainer"
#define SEND_FAILED_CLASS "sun/nio/ch/SctpSendFailed"
#define ASSOC_CHANGE_CLASS "sun/nio/ch/SctpAssocChange"
#define PEER_CHANGE_CLASS "sun/nio/ch/SctpPeerAddrChange"
#define SHUTDOWN_CLASS "sun/nio/ch/SctpShutdown"
struct controlData {
int assocId;
unsigned short streamNumber;
jboolean unordered;
unsigned int ppid;
};
static jclass smi_class; /* sun.nio.ch.SctpMessageInfoImpl */
static jmethodID smi_ctrID; /* sun.nio.ch.SctpMessageInfoImpl.<init> */
static jfieldID src_valueID; /* sun.nio.ch.SctpResultContainer.value */
static jfieldID src_typeID; /* sun.nio.ch.SctpResultContainer.type */
static jclass ssf_class; /* sun.nio.ch.SctpSendFailed */
static jmethodID ssf_ctrID; /* sun.nio.ch.SctpSendFailed.<init> */
static jclass sac_class; /* sun.nio.ch.SctpAssociationChanged */
static jmethodID sac_ctrID; /* sun.nio.ch.SctpAssociationChanged.<init> */
static jclass spc_class; /* sun.nio.ch.SctpPeerAddressChanged */
static jmethodID spc_ctrID; /* sun.nio.ch.SctpPeerAddressChanged.<init> */
static jclass ss_class; /* sun.nio.ch.SctpShutdown */
static jmethodID ss_ctrID; /* sun.nio.ch.SctpShutdown.<init> */
static jfieldID isa_addrID; /* java.net.InetSocketAddress.addr */
static jfieldID isa_portID; /* java.net.InetSocketAddress.port */
/* defined in SctpNet.c */
jobject SockAddrToInetSocketAddress(JNIEnv* env, struct sockaddr* addr);
/* use SocketChannelImpl's checkConnect implementation */
extern jint Java_sun_nio_ch_SocketChannelImpl_checkConnect(JNIEnv* env,
jobject this, jobject fdo, jboolean block, jboolean ready);
/*
* Class: sun_nio_ch_SctpChannelImpl
* Method: initIDs
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_sun_nio_ch_SctpChannelImpl_initIDs
(JNIEnv *env, jclass klass) {
jclass cls;
/* SctpMessageInfoImpl */
cls = (*env)->FindClass(env, MESSAGE_IMPL_CLASS);
CHECK_NULL(cls);
smi_class = (*env)->NewGlobalRef(env, cls);
CHECK_NULL(smi_class);
smi_ctrID = (*env)->GetMethodID(env, cls, "<init>",
"(ILjava/net/SocketAddress;IIZZI)V");
CHECK_NULL(smi_ctrID);
/* SctpResultContainer */
cls = (*env)->FindClass(env, RESULT_CONTAINER_CLASS);
CHECK_NULL(cls);
src_valueID = (*env)->GetFieldID(env, cls, "value", "Ljava/lang/Object;");
CHECK_NULL(src_valueID);
src_typeID = (*env)->GetFieldID(env, cls, "type", "I");
CHECK_NULL(src_typeID);
/* SctpSendFailed */
cls = (*env)->FindClass(env, SEND_FAILED_CLASS);
CHECK_NULL(cls);
ssf_class = (*env)->NewGlobalRef(env, cls);
CHECK_NULL(ssf_class);
ssf_ctrID = (*env)->GetMethodID(env, cls, "<init>",
"(ILjava/net/SocketAddress;Ljava/nio/ByteBuffer;II)V");
CHECK_NULL(ssf_ctrID);
/* SctpAssocChange */
cls = (*env)->FindClass(env, ASSOC_CHANGE_CLASS);
CHECK_NULL(cls);
sac_class = (*env)->NewGlobalRef(env, cls);
CHECK_NULL(sac_class);
sac_ctrID = (*env)->GetMethodID(env, cls, "<init>", "(IIII)V");
CHECK_NULL(sac_ctrID);
/* SctpPeerAddrChange */
cls = (*env)->FindClass(env, PEER_CHANGE_CLASS);
CHECK_NULL(cls);
spc_class = (*env)->NewGlobalRef(env, cls);
CHECK_NULL(spc_class);
spc_ctrID = (*env)->GetMethodID(env, cls, "<init>",
"(ILjava/net/SocketAddress;I)V");
CHECK_NULL(spc_ctrID);
/* sun.nio.ch.SctpShutdown */
cls = (*env)->FindClass(env, SHUTDOWN_CLASS);
CHECK_NULL(cls);
ss_class = (*env)->NewGlobalRef(env, cls);
CHECK_NULL(ss_class);
ss_ctrID = (*env)->GetMethodID(env, cls, "<init>", "(I)V");
CHECK_NULL(ss_ctrID);
/* InetSocketAddress */
cls = (*env)->FindClass(env, "java/net/InetSocketAddress");
CHECK_NULL(cls);
isa_addrID = (*env)->GetFieldID(env, cls, "addr", "Ljava/net/InetAddress;");
CHECK_NULL(isa_addrID);
isa_portID = (*env)->GetFieldID(env, cls, "port", "I");
}
void getControlData
(struct msghdr* msg, struct controlData* cdata) {
struct cmsghdr* cmsg;
for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) {
if (cmsg->cmsg_level == IPPROTO_SCTP && cmsg->cmsg_type == SCTP_SNDRCV) {
struct sctp_sndrcvinfo *sri;
sri = (struct sctp_sndrcvinfo *) CMSG_DATA(cmsg);
cdata->assocId = sri->sinfo_assoc_id;
cdata->streamNumber = sri->sinfo_stream;
cdata->unordered = (sri->sinfo_flags & SCTP_UNORDERED) ? JNI_TRUE :
JNI_FALSE;
cdata->ppid = ntohl(sri->sinfo_ppid);
return;
}
}
return;
}
void setControlData
(struct msghdr* msg, struct controlData* cdata) {
struct cmsghdr* cmsg;
struct sctp_sndrcvinfo *sri;
cmsg = CMSG_FIRSTHDR(msg);
cmsg->cmsg_level = IPPROTO_SCTP;
cmsg->cmsg_type = SCTP_SNDRCV;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
/* Initialize the payload */
sri = (struct sctp_sndrcvinfo*) CMSG_DATA(cmsg);
memset(sri, 0, sizeof (*sri));
if (cdata->streamNumber > 0) {
sri->sinfo_stream = cdata->streamNumber;
}
if (cdata->assocId > 0) {
sri->sinfo_assoc_id = cdata->assocId;
}
if (cdata->unordered == JNI_TRUE) {
sri->sinfo_flags = sri->sinfo_flags | SCTP_UNORDERED;
}
if (cdata->ppid > 0) {
sri->sinfo_ppid = htonl(cdata->ppid);
}
/* Sum of the length of all control messages in the buffer. */
msg->msg_controllen = cmsg->cmsg_len;
}
// TODO: test: can create send failed without any data? if so need to
// update API so that buffer can be null if no data.
void handleSendFailed
(JNIEnv* env, int fd, jobject resultContainerObj, struct sctp_send_failed *ssf,
int read, jboolean isEOR, struct sockaddr* sap) {
jobject bufferObj = NULL, resultObj, isaObj;
char *addressP;
struct sctp_sndrcvinfo *sri;
int remaining, dataLength;
/* the actual undelivered message data is directly after the ssf */
int dataOffset = sizeof(struct sctp_send_failed);
sri = (struct sctp_sndrcvinfo*) &ssf->ssf_info;
/* the number of bytes remaining to be read in the sctp_send_failed notif*/
remaining = ssf->ssf_length - read;
/* the size of the actual undelivered message */
dataLength = ssf->ssf_length - dataOffset;
/* retrieved address from sockaddr */
isaObj = SockAddrToInetSocketAddress(env, sap);
/* data retrieved from sff_data */
if (dataLength > 0) {
struct iovec iov[1];
struct msghdr msg[1];
int rv, alreadyRead;
char *dataP = (char*) ssf;
dataP += dataOffset;
if ((addressP = malloc(dataLength)) == NULL) {
JNU_ThrowOutOfMemoryError(env, "handleSendFailed");
return;
}
memset(msg, 0, sizeof (*msg));
msg->msg_iov = iov;
msg->msg_iovlen = 1;
bufferObj = (*env)->NewDirectByteBuffer(env, addressP, dataLength);
CHECK_NULL(bufferObj);
alreadyRead = read - dataOffset;
if (alreadyRead > 0) {
memcpy(addressP, /*ssf->ssf_data*/ dataP, alreadyRead);
iov->iov_base = addressP + alreadyRead;
iov->iov_len = dataLength - alreadyRead;
} else {
iov->iov_base = addressP;
iov->iov_len = dataLength;
}
if (remaining > 0) {
if ((rv = recvmsg(fd, msg, 0)) < 0) {
fprintf(stdout, "\nNative: handleSFN: recvmsg failed: errno = %d ", errno);
handleSocketError(env, errno);
return;
}
if (rv != (dataLength - alreadyRead) || !(msg->msg_flags & MSG_EOR)) {
//TODO: assert false: "should not reach here";
return;
}
// TODO: Set and document (in API) buffers position.
}
}
/* create SctpSendFailed */
resultObj = (*env)->NewObject(env, ssf_class, ssf_ctrID, ssf->ssf_assoc_id,
sri->sinfo_stream, ssf->ssf_error, isaObj, bufferObj);
CHECK_NULL(resultObj);
(*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj);
(*env)->SetIntField(env, resultContainerObj, src_typeID,
sun_nio_ch_SctpResultContainer_SEND_FAILED);
}
void handleAssocChange
(JNIEnv* env, jobject resultContainerObj, struct sctp_assoc_change *sac) {
jobject resultObj;
int state = 0;
switch (sac->sac_state) {
case SCTP_COMM_UP :
state = sun_nio_ch_SctpAssocChange_SCTP_COMM_UP;
break;
case SCTP_COMM_LOST :
state = sun_nio_ch_SctpAssocChange_SCTP_COMM_LOST;
break;
case SCTP_RESTART :
state = sun_nio_ch_SctpAssocChange_SCTP_RESTART;
break;
case SCTP_SHUTDOWN_COMP :
state = sun_nio_ch_SctpAssocChange_SCTP_SHUTDOWN;
break;
case SCTP_CANT_STR_ASSOC :
state = sun_nio_ch_SctpAssocChange_SCTP_CANT_START;
}
/* create SctpAssociationChanged */
resultObj = (*env)->NewObject(env, sac_class, sac_ctrID, sac->sac_assoc_id,
state, sac->sac_outbound_streams, sac->sac_inbound_streams);
CHECK_NULL(resultObj);
(*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj);
(*env)->SetIntField(env, resultContainerObj, src_typeID,
sun_nio_ch_SctpResultContainer_ASSOCIATION_CHANGED);
}
void handleShutdown
(JNIEnv* env, jobject resultContainerObj, struct sctp_shutdown_event* sse) {
/* create SctpShutdown */
jobject resultObj = (*env)->NewObject(env, ss_class, ss_ctrID, sse->sse_assoc_id);
CHECK_NULL(resultObj);
(*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj);
(*env)->SetIntField(env, resultContainerObj, src_typeID,
sun_nio_ch_SctpResultContainer_SHUTDOWN);
}
void handlePeerAddrChange
(JNIEnv* env, jobject resultContainerObj, struct sctp_paddr_change* spc) {
int event = 0;
jobject addressObj, resultObj;
unsigned int state = spc->spc_state;
switch (state) {
case SCTP_ADDR_AVAILABLE :
event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_AVAILABLE;
break;
case SCTP_ADDR_UNREACHABLE :
event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_UNREACHABLE;
break;
case SCTP_ADDR_REMOVED :
event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_REMOVED;
break;
case SCTP_ADDR_ADDED :
event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_ADDED;
break;
case SCTP_ADDR_MADE_PRIM :
event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_MADE_PRIM;
#ifdef __linux__ /* Solaris currently doesn't support SCTP_ADDR_CONFIRMED */
break;
case SCTP_ADDR_CONFIRMED :
event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_CONFIRMED;
#endif /* __linux__ */
}
addressObj = SockAddrToInetSocketAddress(env, (struct sockaddr*)&spc->spc_aaddr);
/* create SctpPeerAddressChanged */
resultObj = (*env)->NewObject(env, spc_class, spc_ctrID, spc->spc_assoc_id,
addressObj, event);
CHECK_NULL(resultObj);
(*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj);
(*env)->SetIntField(env, resultContainerObj, src_typeID,
sun_nio_ch_SctpResultContainer_PEER_ADDRESS_CHANGED);
}
void handleUninteresting
(union sctp_notification *snp) {
//fprintf(stdout,"\nNative: handleUninterestingNotification: Receive notification type [%u]", snp->sn_header.sn_type);
}
/**
* Handle notifications from the SCTP stack.
* Returns JNI_TRUE if the notification is one that is of interest to the
* Java API, otherwise JNI_FALSE.
*/
jboolean handleNotification
(JNIEnv* env, int fd, jobject resultContainerObj, union sctp_notification* snp,
int read, jboolean isEOR, struct sockaddr* sap) {
switch (snp->sn_header.sn_type) {
case SCTP_SEND_FAILED:
handleSendFailed(env, fd, resultContainerObj, &snp->sn_send_failed,
read, isEOR, sap);
return JNI_TRUE;
case SCTP_ASSOC_CHANGE:
handleAssocChange(env, resultContainerObj, &snp->sn_assoc_change);
return JNI_TRUE;
case SCTP_SHUTDOWN_EVENT:
handleShutdown(env, resultContainerObj, &snp->sn_shutdown_event);
return JNI_TRUE;
case SCTP_PEER_ADDR_CHANGE:
handlePeerAddrChange(env, resultContainerObj, &snp->sn_paddr_change);
return JNI_TRUE;
default :
/* the Java API is not interested in this event, maybe we are? */
handleUninteresting(snp);
}
return JNI_FALSE;
}
void handleMessage
(JNIEnv* env, jobject resultContainerObj, struct msghdr* msg,int read,
jboolean isEOR, struct sockaddr* sap) {
jobject isa, resultObj;
struct controlData cdata[1];
if (read == 0) {
/* we reached EOF */
read = -1;
}
isa = SockAddrToInetSocketAddress(env, sap);
getControlData(msg, cdata);
/* create SctpMessageInfoImpl */
resultObj = (*env)->NewObject(env, smi_class, smi_ctrID, cdata->assocId,
isa, read, cdata->streamNumber,
isEOR ? JNI_TRUE : JNI_FALSE,
cdata->unordered, cdata->ppid);
CHECK_NULL(resultObj);
(*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj);
(*env)->SetIntField(env, resultContainerObj, src_typeID,
sun_nio_ch_SctpResultContainer_MESSAGE);
}
/*
* Class: sun_nio_ch_SctpChannelImpl
* Method: receive0
* Signature: (ILsun/nio/ch/SctpResultContainer;JI)I
*/
JNIEXPORT jint JNICALL Java_sun_nio_ch_SctpChannelImpl_receive0
(JNIEnv *env, jclass klass, jint fd, jobject resultContainerObj,
jlong address, jint length) {
SOCKADDR sa;
int sa_len = sizeof(sa);
ssize_t rv = 0;
jlong *addr = jlong_to_ptr(address);
struct iovec iov[1];
struct msghdr msg[1];
char cbuf[CMSG_SPACE(sizeof (struct sctp_sndrcvinfo))];
/* Set up the msghdr structure for receiving */
memset(msg, 0, sizeof (*msg));
msg->msg_name = &sa;
msg->msg_namelen = sa_len;
iov->iov_base = addr;
iov->iov_len = length;
msg->msg_iov = iov;
msg->msg_iovlen = 1;
msg->msg_control = cbuf;
msg->msg_controllen = sizeof(cbuf);
msg->msg_flags = 0;
do {
if ((rv = recvmsg(fd, msg, 0)) < 0) {
if (errno == EWOULDBLOCK) {
return IOS_UNAVAILABLE;
} else if (errno == EINTR) {
return IOS_INTERRUPTED;
#ifdef __linux__
} else if (errno == ENOTCONN) {
/* ENOTCONN when EOF reached */
rv = 0;
/* there will be no control data */
msg->msg_controllen = 0;
#endif /* __linux__ */
} else {
handleSocketError(env, errno);
return 0;
}
}
if (msg->msg_flags & MSG_NOTIFICATION) {
char *bufp = (char*)addr;
union sctp_notification *snp;
if (!(msg->msg_flags & MSG_EOR) && length < NOTIFICATION_BUFFER_SIZE) {
char buf[NOTIFICATION_BUFFER_SIZE];
int rvSAVE = rv;
memcpy(buf, addr, rv);
iov->iov_base = buf + rv;
iov->iov_len = NOTIFICATION_BUFFER_SIZE - rv;
if ((rv = recvmsg(fd, msg, 0)) < 0) {
handleSocketError(env, errno);
return 0;
}
bufp = buf;
rv += rvSAVE;
}
snp = (union sctp_notification *) bufp;
if (handleNotification(env, fd, resultContainerObj, snp, rv,
(msg->msg_flags & MSG_EOR),
(struct sockaddr*)&sa ) == JNI_TRUE) {
/* We have received a notification that is of interest to
to the Java API. The appropriate notification will be
set in the result container. */
return 0;
}
// set iov back to addr, and reset msg_controllen
iov->iov_base = addr;
iov->iov_len = length;
msg->msg_control = cbuf;
msg->msg_controllen = sizeof(cbuf);
}
} while (msg->msg_flags & MSG_NOTIFICATION);
handleMessage(env, resultContainerObj, msg, rv,
(msg->msg_flags & MSG_EOR), (struct sockaddr*)&sa);
return rv;
}
/*
* Class: sun_nio_ch_SctpChannelImpl
* Method: send0
* Signature: (IJILjava/net/SocketAddress;IIZI)I
*/
JNIEXPORT jint JNICALL Java_sun_nio_ch_SctpChannelImpl_send0
(JNIEnv *env, jclass klass, jint fd, jlong address, jint length,
jobject saTarget, jint assocId, jint streamNumber, jboolean unordered,
jint ppid) {
SOCKADDR sa;
int sa_len = sizeof(sa);
ssize_t rv = 0;
jlong *addr = jlong_to_ptr(address);
struct iovec iov[1];
struct msghdr msg[1];
int cbuf_size = CMSG_SPACE(sizeof (struct sctp_sndrcvinfo));
char cbuf[CMSG_SPACE(sizeof (struct sctp_sndrcvinfo))];
struct controlData cdata[1];
/* SctpChannel:
* saTarget may contain the preferred address or NULL to use primary,
* assocId will always be -1
* SctpMultiChannell:
* Setup new association, saTarget will contain address, assocId = -1
* Association already existing, assocId != -1, saTarget = preferred addr
*/
if (saTarget != NULL /*&& assocId <= 0*/) {
jobject targetAddress = (*env)->GetObjectField(env, saTarget, isa_addrID);
jint targetPort = (*env)->GetIntField(env, saTarget, isa_portID);
if (NET_InetAddressToSockaddr(env, targetAddress, targetPort,
(struct sockaddr *)&sa,
&sa_len, JNI_TRUE) != 0) {
return IOS_THROWN;
}
} else {
memset(&sa, '\x0', sa_len);
sa_len = 0;
}
/* Set up the msghdr structure for sending */
memset(msg, 0, sizeof (*msg));
memset(cbuf, 0, cbuf_size);
msg->msg_name = &sa;
msg->msg_namelen = sa_len;
iov->iov_base = addr;
iov->iov_len = length;
msg->msg_iov = iov;
msg->msg_iovlen = 1;
msg->msg_control = cbuf;
msg->msg_controllen = cbuf_size;
msg->msg_flags = 0;
cdata->streamNumber = streamNumber;
cdata->assocId = assocId;
cdata->unordered = unordered;
cdata->ppid = ppid;
setControlData(msg, cdata);
if ((rv = sendmsg(fd, msg, 0)) < 0) {
if (errno == EWOULDBLOCK) {
return IOS_UNAVAILABLE;
} else if (errno == EINTR) {
return IOS_INTERRUPTED;
} else if (errno == EPIPE) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"Socket is shutdown for writing");
} else {
handleSocketError(env, errno);
return 0;
}
}
return rv;
}
/*
* Class: sun_nio_ch_SctpChannelImpl
* Method: checkConnect
* Signature: (Ljava/io/FileDescriptor;ZZ)I
*/
JNIEXPORT jint JNICALL Java_sun_nio_ch_SctpChannelImpl_checkConnect
(JNIEnv* env, jobject this, jobject fdo, jboolean block, jboolean ready) {
return Java_sun_nio_ch_SocketChannelImpl_checkConnect(env, this,
fdo, block, ready);
}

View File

@ -0,0 +1,609 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include "Sctp.h"
#include "jni.h"
#include "jni_util.h"
#include "nio_util.h"
#include "nio.h"
#include "net_util.h"
#include "net_util_md.h"
#include "sun_nio_ch_SctpNet.h"
#include "sun_nio_ch_SctpStdSocketOption.h"
static jclass isaCls = 0;
static jmethodID isaCtrID = 0;
static const char* nativeSctpLib = "libsctp.so.1";
static jboolean funcsLoaded = JNI_FALSE;
JNIEXPORT jint JNICALL JNI_OnLoad
(JavaVM *vm, void *reserved) {
return JNI_VERSION_1_2;
}
/**
* Loads the native sctp library that contains the socket extension
* functions, as well as locating the individual functions.
* There will be a pending exception if this method returns false.
*/
jboolean loadSocketExtensionFuncs
(JNIEnv* env) {
if (dlopen(nativeSctpLib, RTLD_GLOBAL | RTLD_LAZY) == NULL) {
JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
dlerror());
return JNI_FALSE;
}
if ((nio_sctp_getladdrs = (sctp_getladdrs_func*)
dlsym(RTLD_DEFAULT, "sctp_getladdrs")) == NULL) {
JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
dlerror());
return JNI_FALSE;
}
if ((nio_sctp_freeladdrs = (sctp_freeladdrs_func*)
dlsym(RTLD_DEFAULT, "sctp_freeladdrs")) == NULL) {
JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
dlerror());
return JNI_FALSE;
}
if ((nio_sctp_getpaddrs = (sctp_getpaddrs_func*)
dlsym(RTLD_DEFAULT, "sctp_getpaddrs")) == NULL) {
JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
dlerror());
return JNI_FALSE;
}
if ((nio_sctp_freepaddrs = (sctp_freepaddrs_func*)
dlsym(RTLD_DEFAULT, "sctp_freepaddrs")) == NULL) {
JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
dlerror());
return JNI_FALSE;
}
if ((nio_sctp_bindx = (sctp_bindx_func*)
dlsym(RTLD_DEFAULT, "sctp_bindx")) == NULL) {
JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
dlerror());
return JNI_FALSE;
}
funcsLoaded = JNI_TRUE;
return JNI_TRUE;
}
/*
* Class: sun_nio_ch_SctpNet
* Method: socket0
* Signature: (Z)I
*/
JNIEXPORT jint JNICALL Java_sun_nio_ch_SctpNet_socket0
(JNIEnv *env, jclass klass, jboolean oneToOne) {
int fd;
struct sctp_event_subscribe event;
/* Try to load the socket API extension functions */
if (!funcsLoaded && !loadSocketExtensionFuncs(env)) {
return 0;
}
fd = socket(ipv6_available() ? AF_INET6 : AF_INET,
(oneToOne ? SOCK_STREAM : SOCK_SEQPACKET), IPPROTO_SCTP);
if (fd < 0) {
return handleSocketError(env, errno);
}
/* Enable events */
memset(&event, 0, sizeof(event));
event.sctp_data_io_event = 1;
event.sctp_association_event = 1;
event.sctp_address_event = 1;
event.sctp_send_failure_event = 1;
//event.sctp_peer_error_event = 1;
event.sctp_shutdown_event = 1;
//event.sctp_partial_delivery_event = 1;
//event.sctp_adaptation_layer_event = 1;
if (setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(event)) != 0) {
handleSocketError(env, errno);
}
return fd;
}
/*
* Class: sun_nio_ch_SctpNet
* Method: bindx
* Signature: (I[Ljava/net/InetAddress;IIZ)V
*/
JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_bindx
(JNIEnv *env, jclass klass, jint fd, jobjectArray addrs, jint port,
jint addrsLength, jboolean add, jboolean preferIPv6) {
SOCKADDR *sap, *tmpSap;
int i, sa_len = sizeof(SOCKADDR);
jobject ia;
if (addrsLength < 1)
return;
if ((sap = calloc(addrsLength, sa_len)) == NULL) {
JNU_ThrowOutOfMemoryError(env, "heap allocation failure");
return;
}
tmpSap = sap;
for (i=0; i<addrsLength; i++) {
ia = (*env)->GetObjectArrayElement(env, addrs, i);
if (NET_InetAddressToSockaddr(env, ia, port, (struct sockaddr*)tmpSap,
&sa_len, preferIPv6) != 0) {
free(sap);
return;
}
tmpSap++;
}
if (nio_sctp_bindx(fd, (void*)sap, addrsLength, add ? SCTP_BINDX_ADD_ADDR :
SCTP_BINDX_REM_ADDR) != 0) {
handleSocketError(env, errno);
}
free(sap);
}
void initializeISA
(JNIEnv* env) {
if (isaCls == 0) {
jclass c = (*env)->FindClass(env, "java/net/InetSocketAddress");
CHECK_NULL(c);
isaCls = (*env)->NewGlobalRef(env, c);
CHECK_NULL(isaCls);
(*env)->DeleteLocalRef(env, c);
isaCtrID = (*env)->GetMethodID(env, isaCls, "<init>",
"(Ljava/net/InetAddress;I)V");
}
}
jobject SockAddrToInetSocketAddress
(JNIEnv *env, struct sockaddr* sap) {
int port = 0;
jobject ia = NET_SockaddrToInetAddress(env, sap, &port);
if (ia == NULL)
return NULL;
if (isaCls == 0) {
initializeISA(env);
CHECK_NULL_RETURN(isaCls, NULL);
}
return (*env)->NewObject(env, isaCls, isaCtrID, ia, port);
}
/*
* Class: sun_nio_ch_SctpNet
* Method: getLocalAddresses0
* Signature: (I)[Ljava/net/SocketAddress;
*/
JNIEXPORT jobjectArray JNICALL Java_sun_nio_ch_SctpNet_getLocalAddresses0
(JNIEnv *env, jclass klass, jint fd) {
void *addr_buf, *laddr;
struct sockaddr* sap;
int i, addrCount;
jobjectArray isaa;
#ifdef __solaris__
if ((addrCount = nio_sctp_getladdrs(fd, 0, (void **)&addr_buf)) == -1) {
#else /* __linux__ */
if ((addrCount = nio_sctp_getladdrs(fd, 0, (struct sockaddr **)&addr_buf)) == -1) {
#endif
handleSocketError(env, errno);
return NULL;
}
if (addrCount < 1)
return NULL;
if (isaCls == 0) {
initializeISA(env);
CHECK_NULL_RETURN(isaCls, NULL);
}
isaa = (*env)->NewObjectArray(env, addrCount, isaCls, NULL);
if (isaa == NULL) {
nio_sctp_freeladdrs(addr_buf);
return NULL;
}
laddr = addr_buf;
for (i=0; i<addrCount; i++) {
int port = 0;
jobject isa = NULL, ia;
sap = (struct sockaddr*)addr_buf;
ia = NET_SockaddrToInetAddress(env, sap, &port);
if (ia != NULL)
isa = (*env)->NewObject(env, isaCls, isaCtrID, ia, port);
if (isa != NULL)
(*env)->SetObjectArrayElement(env, isaa, i, isa);
if (sap->sa_family == AF_INET)
addr_buf = ((struct sockaddr_in*)addr_buf) + 1;
else
addr_buf = ((struct sockaddr_in6*)addr_buf) + 1;
}
nio_sctp_freeladdrs(laddr);
return isaa;
}
jobjectArray getRemoteAddresses
(JNIEnv *env, jint fd, sctp_assoc_t id) {
void *addr_buf, *paddr;
struct sockaddr* sap;
int i, addrCount;
jobjectArray isaa;
#if __solaris__
if ((addrCount = nio_sctp_getpaddrs(fd, id, (void **)&addr_buf)) == -1) {
#else /* __linux__ */
if ((addrCount = nio_sctp_getpaddrs(fd, id, (struct sockaddr**)&addr_buf)) == -1) {
#endif
handleSocketError(env, errno);
return NULL;
}
if (addrCount < 1)
return NULL;
if (isaCls == 0) {
initializeISA(env);
CHECK_NULL_RETURN(isaCls, NULL);
}
isaa = (*env)->NewObjectArray(env, addrCount, isaCls, NULL);
if (isaa == NULL) {
nio_sctp_freepaddrs(addr_buf);
return NULL;
}
paddr = addr_buf;
for (i=0; i<addrCount; i++) {
jobject ia, isa = NULL;
int port;
sap = (struct sockaddr*)addr_buf;
ia = NET_SockaddrToInetAddress(env, sap, &port);
if (ia != NULL)
isa = (*env)->NewObject(env, isaCls, isaCtrID, ia, port);
if (isa != NULL)
(*env)->SetObjectArrayElement(env, isaa, i, isa);
if (sap->sa_family == AF_INET)
addr_buf = ((struct sockaddr_in*)addr_buf) + 1;
else
addr_buf = ((struct sockaddr_in6*)addr_buf) + 1;
}
nio_sctp_freepaddrs(paddr);
return isaa;
}
/*
* Class: sun_nio_ch_SctpNet
* Method: getRemoteAddresses0
* Signature: (II)[Ljava/net/SocketAddress;
*/
JNIEXPORT jobjectArray JNICALL Java_sun_nio_ch_SctpNet_getRemoteAddresses0
(JNIEnv *env, jclass klass, jint fd, jint assocId) {
return getRemoteAddresses(env, fd, assocId);
}
/* Map the Java level option to the native level */
int mapSocketOption
(jint cmd, int *level, int *optname) {
static struct {
jint cmd;
int level;
int optname;
} const opts[] = {
{ sun_nio_ch_SctpStdSocketOption_SCTP_DISABLE_FRAGMENTS, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS },
{ sun_nio_ch_SctpStdSocketOption_SCTP_EXPLICIT_COMPLETE, IPPROTO_SCTP, SCTP_EXPLICIT_EOR },
{ sun_nio_ch_SctpStdSocketOption_SCTP_FRAGMENT_INTERLEAVE, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE },
{ sun_nio_ch_SctpStdSocketOption_SCTP_NODELAY, IPPROTO_SCTP, SCTP_NODELAY },
{ sun_nio_ch_SctpStdSocketOption_SO_SNDBUF, SOL_SOCKET, SO_SNDBUF },
{ sun_nio_ch_SctpStdSocketOption_SO_RCVBUF, SOL_SOCKET, SO_RCVBUF },
{ sun_nio_ch_SctpStdSocketOption_SO_LINGER, SOL_SOCKET, SO_LINGER } };
int i;
for (i=0; i<(int)(sizeof(opts) / sizeof(opts[0])); i++) {
if (cmd == opts[i].cmd) {
*level = opts[i].level;
*optname = opts[i].optname;
return 0;
}
}
/* not found */
return -1;
}
/*
* Class: sun_nio_ch_SctpNet
* Method: setIntOption0
* Signature: (III)V
*/
JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_setIntOption0
(JNIEnv *env, jclass klass, jint fd, jint opt, int arg) {
int klevel, kopt;
int result;
struct linger linger;
void *parg;
int arglen;
if (mapSocketOption(opt, &klevel, &kopt) < 0) {
JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
"Unsupported socket option");
return;
}
if (opt == sun_nio_ch_SctpStdSocketOption_SO_LINGER) {
parg = (void *)&linger;
arglen = sizeof(linger);
if (arg >= 0) {
linger.l_onoff = 1;
linger.l_linger = arg;
} else {
linger.l_onoff = 0;
linger.l_linger = 0;
}
} else {
parg = (void *)&arg;
arglen = sizeof(arg);
}
if (setsockopt(fd, klevel, kopt, parg, arglen) < 0) {
JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
"sun_nio_ch_SctpNet.setIntOption0");
}
}
/*
* Class: sun_nio_ch_SctpNet
* Method: getIntOption0
* Signature: (II)I
*/
JNIEXPORT int JNICALL Java_sun_nio_ch_SctpNet_getIntOption0
(JNIEnv *env, jclass klass, jint fd, jint opt) {
int klevel, kopt;
int result;
struct linger linger;
void *arg;
unsigned int arglen;
if (mapSocketOption(opt, &klevel, &kopt) < 0) {
JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
"Unsupported socket option");
return -1;
}
if (opt == sun_nio_ch_SctpStdSocketOption_SO_LINGER) {
arg = (void *)&linger;
arglen = sizeof(linger);
} else {
arg = (void *)&result;
arglen = sizeof(result);
}
if (getsockopt(fd, klevel, kopt, arg, &arglen) < 0) {
JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
"sun.nio.ch.Net.getIntOption");
return -1;
}
if (opt == sun_nio_ch_SctpStdSocketOption_SO_LINGER)
return linger.l_onoff ? linger.l_linger : -1;
else
return result;
}
/*
* Class: sun_nio_ch_SctpNet
* Method: getPrimAddrOption0
* Signature: (II)Ljava/net/SocketAddress;
*/
JNIEXPORT jobject JNICALL Java_sun_nio_ch_SctpNet_getPrimAddrOption0
(JNIEnv *env, jclass klass, jint fd, jint assocId) {
struct sctp_setprim prim;
struct sockaddr_storage ss;
int ss_len = sizeof(ss);
unsigned int prim_len = sizeof(prim);
prim.ssp_assoc_id = assocId;
prim.ssp_addr = ss;
if (getsockopt(fd, IPPROTO_SCTP, SCTP_PRIMARY_ADDR, &prim, &prim_len) < 0) {
JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
"sun.nio.ch.SctpNet.getPrimAddrOption0");
return NULL;
}
return SockAddrToInetSocketAddress(env, (struct sockaddr*)&ss);
}
/*
* Class: sun_nio_ch_SctpNet
* Method: setPrimAddrOption0
* Signature: (IILjava/net/InetAddress;I)V
*/
JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_setPrimAddrOption0
(JNIEnv *env, jclass klass, jint fd, jint assocId, jobject iaObj, jint port) {
struct sctp_setprim prim;
struct sockaddr_storage ss;
int ss_len = sizeof(ss);
if (NET_InetAddressToSockaddr(env, iaObj, port, (struct sockaddr *)&ss,
&ss_len, JNI_TRUE) != 0) {
return;
}
prim.ssp_assoc_id = assocId;
prim.ssp_addr = ss;
if (setsockopt(fd, IPPROTO_SCTP, SCTP_PRIMARY_ADDR, &prim, sizeof(prim)) < 0) {
JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
"sun.nio.ch.SctpNet.setPrimAddrOption0");
}
}
/*
* Class: sun_nio_ch_SctpNet
* Method: setPeerPrimAddrOption0
* Signature: (IILjava/net/InetAddress;I)V
*/
JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_setPeerPrimAddrOption0
(JNIEnv *env, jclass klass, jint fd, jint assocId, jobject iaObj, jint port) {
struct sctp_setpeerprim prim;
struct sockaddr_storage ss;
int ss_len = sizeof(ss);
if (NET_InetAddressToSockaddr(env, iaObj, port, (struct sockaddr *)&ss,
&ss_len, JNI_TRUE) != 0) {
return;
}
prim.sspp_assoc_id = assocId;
prim.sspp_addr = ss;
if (setsockopt(fd, IPPROTO_SCTP, SCTP_SET_PEER_PRIMARY_ADDR, &prim,
sizeof(prim)) < 0) {
JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
"sun.nio.ch.SctpNet.setPeerPrimAddrOption0");
}
}
/*
* Class: sun_nio_ch_SctpNet
* Method: getInitMsgOption0
* Signature: (I[I)V
*/
JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_getInitMsgOption0
(JNIEnv *env, jclass klass, jint fd, jintArray retVal) {
struct sctp_initmsg sctp_initmsg;
unsigned int sim_len = sizeof(sctp_initmsg);
int vals[2];
if (getsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &sctp_initmsg,
&sim_len) < 0) {
JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
"sun.nio.ch.SctpNet.getInitMsgOption0");
return;
}
vals[0] = sctp_initmsg.sinit_max_instreams;
vals[1] = sctp_initmsg.sinit_num_ostreams;
(*env)->SetIntArrayRegion(env, retVal, 0, 2, vals);
}
/*
* Class: sun_nio_ch_SctpNet
* Method: setInitMsgOption0
* Signature: (III)V
*/
JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_setInitMsgOption0
(JNIEnv *env, jclass klass, jint fd, jint inArg, jint outArg) {
struct sctp_initmsg sctp_initmsg;
sctp_initmsg.sinit_max_instreams = (unsigned int)inArg;
sctp_initmsg.sinit_num_ostreams = (unsigned int)outArg;
sctp_initmsg.sinit_max_attempts = 0; // default
sctp_initmsg.sinit_max_init_timeo = 0; // default
if (setsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &sctp_initmsg,
sizeof(sctp_initmsg)) < 0) {
JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
"sun.nio.ch.SctpNet.setInitMsgOption0");
}
}
/*
* Class: sun_nio_ch_SctpNet
* Method: shutdown0
* Signature: (II)V
*/
JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_shutdown0
(JNIEnv *env, jclass klass, jint fd, jint assocId) {
int rv;
struct msghdr msg[1];
struct iovec iov[1];
int cbuf_size = CMSG_SPACE(sizeof (struct sctp_sndrcvinfo));
char cbuf[CMSG_SPACE(sizeof (struct sctp_sndrcvinfo))];
struct cmsghdr* cmsg;
struct sctp_sndrcvinfo *sri;
/* SctpSocketChannel */
if (assocId < 0) {
shutdown(fd, SHUT_WR);
return;
}
memset(msg, 0, sizeof (*msg));
memset(cbuf, 0, cbuf_size);
msg->msg_name = NULL;
msg->msg_namelen = 0;
iov->iov_base = NULL;
iov->iov_len = 0;
msg->msg_iov = iov;
msg->msg_iovlen = 1;
msg->msg_control = cbuf;
msg->msg_controllen = cbuf_size;
msg->msg_flags = 0;
cmsg = CMSG_FIRSTHDR(msg);
cmsg->cmsg_level = IPPROTO_SCTP;
cmsg->cmsg_type = SCTP_SNDRCV;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
/* Initialize the payload: */
sri = (struct sctp_sndrcvinfo*) CMSG_DATA(cmsg);
memset(sri, 0, sizeof (*sri));
if (assocId > 0) {
sri->sinfo_assoc_id = assocId;
}
sri->sinfo_flags = sri->sinfo_flags | SCTP_EOF;
/* Sum of the length of all control messages in the buffer. */
msg->msg_controllen = cmsg->cmsg_len;
if ((rv = sendmsg(fd, msg, 0)) < 0) {
handleSocketError(env, errno);
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
#include "sun_nio_ch_SctpServerChannelImpl.h"
extern void Java_sun_nio_ch_ServerSocketChannelImpl_initIDs(JNIEnv* env,
jclass c);
extern jint Java_sun_nio_ch_ServerSocketChannelImpl_accept0(JNIEnv* env,
jobject this, jobject ssfdo, jobject newfdo, jobjectArray isaa);
/*
* Class: sun_nio_ch_SctpServerChannelImpl
* Method: initIDs
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_sun_nio_ch_SctpServerChannelImpl_initIDs
(JNIEnv* env, jclass c) {
Java_sun_nio_ch_ServerSocketChannelImpl_initIDs(env, c);
}
/*
* Class: sun_nio_ch_SctpServerChannelImpl
* Method: accept0
* Signature: (Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;[Ljava/net/InetSocketAddress;)I
*/
JNIEXPORT jint JNICALL Java_sun_nio_ch_SctpServerChannelImpl_accept0
(JNIEnv* env, jobject this, jobject ssfdo, jobject newfdo, jobjectArray isaa) {
return Java_sun_nio_ch_ServerSocketChannelImpl_accept0(env, this,
ssfdo, newfdo, isaa);
}

View File

@ -0,0 +1,150 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.nio.ch;
import java.net.SocketAddress;
import java.net.InetAddress;
import java.io.IOException;
import java.util.Set;
import java.nio.ByteBuffer;
import java.nio.channels.spi.SelectorProvider;
import com.sun.nio.sctp.Association;
import com.sun.nio.sctp.MessageInfo;
import com.sun.nio.sctp.NotificationHandler;
import com.sun.nio.sctp.SctpChannel;
import com.sun.nio.sctp.SctpSocketOption;
/**
* Unimplemented.
*/
public class SctpChannelImpl extends SctpChannel
{
private static final String message = "SCTP not supported on this platform";
public SctpChannelImpl(SelectorProvider provider) {
super(provider);
throw new UnsupportedOperationException(message);
}
@Override
public Association association() {
throw new UnsupportedOperationException(message);
}
@Override
public SctpChannel bind(SocketAddress local)
throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public SctpChannel bindAddress(InetAddress address)
throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public SctpChannel unbindAddress(InetAddress address)
throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public boolean connect(SocketAddress remote) throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public boolean connect(SocketAddress remote, int maxOutStreams,
int maxInStreams) throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public boolean isConnectionPending() {
throw new UnsupportedOperationException(message);
}
@Override
public boolean finishConnect() throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public Set<SocketAddress> getAllLocalAddresses()
throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public Set<SocketAddress> getRemoteAddresses()
throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public SctpChannel shutdown() throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public <T> T getOption(SctpSocketOption<T> name)
throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public <T> SctpChannel setOption(SctpSocketOption<T> name, T value)
throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public Set<SctpSocketOption<?>> supportedOptions() {
throw new UnsupportedOperationException(message);
}
@Override
public <T> MessageInfo receive(ByteBuffer dst, T attachment,
NotificationHandler<T> handler) throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public int send(ByteBuffer src, MessageInfo messageInfo)
throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
protected void implConfigureBlocking(boolean block) throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public void implCloseSelectableChannel() throws IOException {
throw new UnsupportedOperationException(message);
}
}

View File

@ -0,0 +1,137 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.nio.ch;
import java.net.SocketAddress;
import java.net.InetAddress;
import java.io.IOException;
import java.util.Set;
import java.nio.ByteBuffer;
import java.nio.channels.spi.SelectorProvider;
import com.sun.nio.sctp.Association;
import com.sun.nio.sctp.SctpChannel;
import com.sun.nio.sctp.MessageInfo;
import com.sun.nio.sctp.NotificationHandler;
import com.sun.nio.sctp.SctpMultiChannel;
import com.sun.nio.sctp.SctpSocketOption;
/**
* Unimplemented.
*/
public class SctpMultiChannelImpl extends SctpMultiChannel
{
private static final String message = "SCTP not supported on this platform";
public SctpMultiChannelImpl(SelectorProvider provider) {
super(provider);
throw new UnsupportedOperationException(message);
}
@Override
public Set<Association> associations() {
throw new UnsupportedOperationException(message);
}
@Override
public SctpMultiChannel bind(SocketAddress local,
int backlog) throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public SctpMultiChannel bindAddress(InetAddress address)
throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public SctpMultiChannel unbindAddress(InetAddress address)
throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public Set<SocketAddress> getAllLocalAddresses()
throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public Set<SocketAddress> getRemoteAddresses
(Association association) throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public SctpMultiChannel shutdown(Association association)
throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public <T> T getOption(SctpSocketOption<T> name,
Association association) throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public <T> SctpMultiChannel setOption(SctpSocketOption<T> name,
T value, Association association) throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public Set<SctpSocketOption<?>> supportedOptions() {
throw new UnsupportedOperationException(message);
}
@Override
public <T> MessageInfo receive(ByteBuffer buffer, T attachment,
NotificationHandler<T> handler) throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public int send(ByteBuffer buffer, MessageInfo messageInfo)
throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public SctpChannel branch(Association association)
throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
protected void implConfigureBlocking(boolean block) throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public void implCloseSelectableChannel() throws IOException {
throw new UnsupportedOperationException(message);
}
}

View File

@ -0,0 +1,102 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.nio.ch;
import java.net.SocketAddress;
import java.net.InetAddress;
import java.io.IOException;
import java.util.Set;
import java.nio.channels.spi.SelectorProvider;
import com.sun.nio.sctp.SctpChannel;
import com.sun.nio.sctp.SctpServerChannel;
import com.sun.nio.sctp.SctpSocketOption;
/**
* Unimplemented.
*/
public class SctpServerChannelImpl extends SctpServerChannel
{
private static final String message = "SCTP not supported on this platform";
public SctpServerChannelImpl(SelectorProvider provider) {
super(provider);
throw new UnsupportedOperationException(message);
}
@Override
public SctpChannel accept() throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public SctpServerChannel bind(SocketAddress local,
int backlog) throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public SctpServerChannel bindAddress(InetAddress address)
throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public SctpServerChannel unbindAddress(InetAddress address)
throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public Set<SocketAddress> getAllLocalAddresses()
throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public <T> T getOption(SctpSocketOption<T> name) throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public <T> SctpServerChannel setOption(SctpSocketOption<T> name,
T value) throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public Set<SctpSocketOption<?>> supportedOptions() {
throw new UnsupportedOperationException(message);
}
@Override
protected void implConfigureBlocking(boolean block) throws IOException {
throw new UnsupportedOperationException(message);
}
@Override
public void implCloseSelectableChannel() throws IOException {
throw new UnsupportedOperationException(message);
}
}

View File

@ -0,0 +1,148 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
/* @test
* @bug 4927640
* @summary Tests the SCTP protocol implementation
* @author chegar
*/
import java.net.SocketAddress;
import com.sun.nio.sctp.Association;
import com.sun.nio.sctp.MessageInfo;
public class MessageInfoTests {
static final int DEFAULT_STREAM_NUMBER = 14;
static final int TEST_STREAM_NUMBER = 15;
static final int TEST_PPID = 8;
static final long TEST_TTL = 10000L;
static final SocketAddress addr = new TestSocketAddress();
static final Association assoc = new TestAssociation(1, 1, 1);
void test(String[] args) {
/* TEST 1 : createOutGoing(SocketAddress,int) */
MessageInfo info = MessageInfo.createOutgoing(addr,
DEFAULT_STREAM_NUMBER);
checkDefaults(info);
checkGetterSetters(info);
/* TEST 2 : createOutGoing(Association,SocketAddress,int) */
info = MessageInfo.createOutgoing(assoc, addr, DEFAULT_STREAM_NUMBER);
checkDefaults(info);
check(info.association().equals(assoc), "incorrect association");
checkGetterSetters(info);
/* TEST 3: null values */
info = MessageInfo.createOutgoing(null, 0);
check(info.address() == null, "address should be null");
check(info.association() == null, "association should be null");
info = MessageInfo.createOutgoing(assoc, null, 0);
check(info.address() == null, "address should be null");
/* Test 4: IllegalArgumentException */
testIAE(new Runnable() {
public void run() { MessageInfo.createOutgoing(addr, -1); } });
testIAE(new Runnable() {
public void run() { MessageInfo.createOutgoing(addr, 65537); } });
testIAE(new Runnable() {
public void run() { MessageInfo.createOutgoing(null, addr, 0); } });
testIAE(new Runnable() {
public void run() { MessageInfo.createOutgoing(assoc, addr, -1); } });
testIAE(new Runnable() {
public void run() { MessageInfo.createOutgoing(assoc, addr, 65537);}});
final MessageInfo iaeInfo = MessageInfo.createOutgoing(assoc, addr, 0);
testIAE(new Runnable() {
public void run() { iaeInfo.streamNumber(-1); } });
testIAE(new Runnable() {
public void run() { iaeInfo.streamNumber(65537); } });
}
/* TEST : unordered = false, timeToLive = 0, complete = true,
* payloadProtocolID = 0. */
void checkDefaults(MessageInfo info) {
check(info.isUnordered() == false, "default unordered value not false");
check(info.timeToLive() == 0L, "timeToLive should be 0L");
check(info.isComplete() == true, "default complete value not true");
check(info.payloadProtocolID() == 0, "default PPID not 0");
check(info.bytes() == 0, "default bytes value not 0");
check(info.streamNumber() == DEFAULT_STREAM_NUMBER,
"incorrect default stream number");
check(info.address().equals(addr), "incorrect address");
}
void checkGetterSetters(MessageInfo info) {
check(info.streamNumber(TEST_STREAM_NUMBER).streamNumber() ==
TEST_STREAM_NUMBER, "stream number not being set correctly");
check(info.complete(false).isComplete() == false,
"complete not being set correctly");
check(info.unordered(true).isUnordered() == true,
"unordered not being set correctly");
check(info.payloadProtocolID(TEST_PPID).payloadProtocolID() ==
TEST_PPID, "PPID not being set correctly");
check(info.timeToLive(TEST_TTL).timeToLive() == TEST_TTL,
"TTL not being set correctly");
}
void testIAE(Runnable runnable) {
try {
runnable.run();
fail("IllegalArgumentException should have been thrown");
} catch(IllegalArgumentException iae) {
pass();
}
}
static class TestSocketAddress extends SocketAddress {}
static class TestAssociation extends Association {
TestAssociation(int assocID, int maxInStreams, int maxOutStreams) {
super(assocID, maxInStreams, maxOutStreams);
}
}
//--------------------- Infrastructure ---------------------------
boolean debug = true;
volatile int passed = 0, failed = 0;
void pass() {passed++;}
void fail() {failed++; Thread.dumpStack();}
void fail(String msg) {System.err.println(msg); fail();}
void unexpected(Throwable t) {failed++; t.printStackTrace();}
void check(boolean cond) {if (cond) pass(); else fail();}
void check(boolean cond, String failMessage) {if (cond) pass();
else fail(failMessage);}
void debug(String message) {if(debug) { System.out.println(message); } }
public static void main(String[] args) throws Throwable {
Class<?> k = new Object(){}.getClass().getEnclosingClass();
try {k.getMethod("instanceMain",String[].class)
.invoke( k.newInstance(), (Object) args);}
catch (Throwable e) {throw e.getCause();}}
public void instanceMain(String[] args) throws Throwable {
try {test(args);} catch (Throwable t) {unexpected(t);}
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View File

@ -0,0 +1,350 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
/* @test
* @bug 4927640
* @summary Tests the SCTP protocol implementation
* @author chegar
*/
import java.net.*;
import java.io.*;
import java.util.List;
import java.util.Set;
import java.util.Iterator;
import java.nio.ByteBuffer;
import java.nio.channels.AlreadyBoundException;
import java.nio.channels.AlreadyConnectedException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.UnsupportedAddressTypeException;
import com.sun.nio.sctp.AssociationChangeNotification;
import com.sun.nio.sctp.AbstractNotificationHandler;
import com.sun.nio.sctp.HandlerResult;
import com.sun.nio.sctp.IllegalUnbindException;
import com.sun.nio.sctp.MessageInfo;
import com.sun.nio.sctp.PeerAddressChangeNotification;
import com.sun.nio.sctp.SctpChannel;
import com.sun.nio.sctp.SctpServerChannel;
import com.sun.nio.sctp.ShutdownNotification;
import static java.lang.System.out;
/**
* Tests bind, bindAddress, unbindAddress, getLocalAddress, and
* getAllLocalAddresses.
*/
public class Bind {
void test(String[] args) {
if (!Util.isSCTPSupported()) {
out.println("SCTP protocol is not supported");
out.println("Test cannot be run");
return;
}
/* Simply bind tests */
testBind();
/* Test unconnected */
testBindUnbind(false);
/* Test connected */
/* Adding/Removing addresses from a connected association is optional.
* This test can be run on systems that support dynamic address
* reconfiguration */
//testBindUnbind(true);
}
void testBind() {
SctpChannel channel = null;
try {
channel = SctpChannel.open();
/* TEST 1: empty set if channel is not bound */
check(channel.getAllLocalAddresses().isEmpty(),
"getAllLocalAddresses returned non empty set for unbound channel");
/* TEST 2: null to bind the channel to an automatically assigned
* socket address */
channel.bind(null);
/* TEST 3: non empty set if the channel is bound */
check(!channel.getAllLocalAddresses().isEmpty(),
"getAllLocalAddresses returned empty set for bound channel");
debug("getAllLocalAddresses on channel bound to the wildcard:\n"
+ channel.getAllLocalAddresses());
/* TEST 4: AlreadyBoundException if this channel is already bound */
try { channel.bind(null); }
catch (AlreadyBoundException unused) { pass(); }
catch (IOException ioe) { unexpected(ioe); }
/* TEST 5: UnsupportedAddressTypeException */
try {
channel.close(); /* open a new unbound channel for test */
channel = SctpChannel.open();
channel.bind(new UnsupportedSocketAddress());
fail("UnsupportedSocketAddress expected");
} catch (UnsupportedAddressTypeException unused) { pass();
} catch (IOException ioe) { unexpected(ioe); }
/* TEST 6: AlreadyConnectedException */
try {
channel.close(); /* open a new unbound channel for test */
channel = SctpChannel.open();
connectChannel(channel);
channel.bind(null);
fail("AlreadyConnectedException expected");
} catch (AlreadyConnectedException unused) { pass();
} catch (IOException ioe) { unexpected(ioe); }
/* TEST 7: ClosedChannelException - If this channel is closed */
try {
channel.close(); /* open a new unbound channel for test */
channel = SctpChannel.open();
channel.close();
channel.bind(null);
fail("ClosedChannelException expected");
} catch (ClosedChannelException unused) { pass();
} catch (IOException ioe) { unexpected(ioe); }
/* TEST 8: ClosedChannelException if channel is closed */
try {
channel.getAllLocalAddresses();
fail("should have thrown ClosedChannelException");
} catch (ClosedChannelException cce) {
pass();
} catch (Exception ioe) {
unexpected(ioe);
}
} catch (IOException ioe) {
unexpected(ioe);
} finally {
try { channel.close(); }
catch (IOException ioe) { unexpected(ioe); }
}
}
void testBindUnbind(boolean connected) {
SctpChannel channel = null;
SctpChannel peerChannel = null;
debug("testBindUnbind, connected: " + connected);
try {
channel = SctpChannel.open();
List<InetAddress> addresses = Util.getAddresses(true, false);
Iterator iterator = addresses.iterator();
InetSocketAddress a = new InetSocketAddress((InetAddress)iterator.next(), 0);
debug("channel.bind( " + a + ")");
channel.bind(a);
while (iterator.hasNext()) {
InetAddress ia = (InetAddress)iterator.next();
debug("channel.bindAddress(" + ia + ")");
channel.bindAddress(ia);
}
if (debug) {Util.dumpAddresses(channel, out);}
if (connected) {
/* Test with connected channel */
peerChannel = connectChannel(channel);
}
/* TEST 1: bind/unbindAddresses on the system addresses */
debug("bind/unbindAddresses on the system addresses");
List<InetAddress> addrs = Util.getAddresses(true, false);
for (InetAddress addr : addrs) {
try {
debug("unbindAddress: " + addr);
check(boundAddress(channel, addr), "trying to remove address that is not bound");
channel.unbindAddress(addr);
if (debug) {Util.dumpAddresses(channel, out);}
check(!boundAddress(channel, addr), "address was not removed");
debug("bindAddress: " + addr);
channel.bindAddress(addr);
if (debug) {Util.dumpAddresses(channel, out);}
check(boundAddress(channel, addr), "address is not bound");
} catch (IOException ioe) {
unexpected(ioe);
}
}
/* TEST 2: bindAddress - already bound address. */
InetAddress againAddress = addrs.get(0);
try {
debug("bind already bound address " + againAddress);
channel.bindAddress(againAddress);
} catch (AlreadyBoundException unused) {
debug("Caught AlreadyBoundException - OK");
pass();
} catch (IOException ioe) {
unexpected(ioe);
}
/* TEST 3: bind non local address */
try {
InetAddress nla = InetAddress.getByName("123.123.123.123");
debug("bind non local address " + nla);
channel.bindAddress(nla);
} catch (IOException ioe) {
debug("Informative only " + ioe);
}
/* TEST 4: unbind address that is not bound */
try {
debug("unbind address that is not bound " + againAddress);
/* remove address first then again */
channel.unbindAddress(againAddress);
channel.unbindAddress(againAddress);
} catch (IllegalUnbindException unused) {
debug("Caught IllegalUnbindException - OK");
pass();
} catch (IOException ioe) {
unexpected(ioe);
}
/* TEST 5: unbind address that is not bound */
try {
InetAddress nla = InetAddress.getByName("123.123.123.123");
debug("unbind address that is not bound " + nla);
channel.unbindAddress(nla);
} catch (IllegalUnbindException unused) {
debug("Caught IllegalUnbindException - OK");
pass();
} catch (IOException ioe) {
unexpected(ioe);
}
if (connected) {
channel.shutdown();
BindNotificationHandler handler = new BindNotificationHandler();
ByteBuffer buffer = ByteBuffer.allocate(10);
MessageInfo info;
while((info = peerChannel.receive(buffer, null, handler)) != null) {
if (info != null) {
if (info.bytes() == -1) {
debug("peerChannel Reached EOF");
break;
}
}
}
while((info = channel.receive(buffer, null, handler)) != null) {
if (info != null) {
if (info.bytes() == -1) {
debug("channel Reached EOF");
break;
}
}
}
}
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
try { if (channel != null) channel.close(); }
catch (IOException ioe) { unexpected(ioe); }
}
}
boolean boundAddress(SctpChannel channel, InetAddress addr)
throws IOException {
for (SocketAddress boundAddr : channel.getAllLocalAddresses()) {
if (((InetSocketAddress) boundAddr).getAddress().equals(addr))
return true;
}
return false;
}
SctpChannel connectChannel(SctpChannel channel)
throws IOException {
debug("connecting channel...");
try {
SctpServerChannel ssc = SctpServerChannel.open();
ssc.bind(null);
Set<SocketAddress> addrs = ssc.getAllLocalAddresses();
Iterator<SocketAddress> iterator = addrs.iterator();
SocketAddress addr = iterator.next();
debug("using " + addr + "...");
channel.connect(addr);
SctpChannel peerChannel = ssc.accept();
ssc.close();
debug("connected");
return peerChannel;
} catch (IOException ioe) {
debug("Cannot connect channel");
unexpected(ioe);
throw ioe;
}
}
class BindNotificationHandler extends AbstractNotificationHandler<Object>
{
@Override
public HandlerResult handleNotification(
AssociationChangeNotification acn, Object unused)
{
debug("AssociationChangeNotification: " + acn);
return HandlerResult.CONTINUE;
}
@Override
public HandlerResult handleNotification(
PeerAddressChangeNotification pacn, Object unused)
{
debug("PeerAddressChangeNotification: " + pacn);
return HandlerResult.CONTINUE;
}
@Override
public HandlerResult handleNotification(
ShutdownNotification sn, Object unused)
{
debug("ShutdownNotification: " + sn);
return HandlerResult.CONTINUE;
}
}
class UnsupportedSocketAddress extends SocketAddress { }
//--------------------- Infrastructure ---------------------------
boolean debug = true;
volatile int passed = 0, failed = 0;
void pass() {passed++;}
void fail() {failed++; Thread.dumpStack();}
void fail(String msg) {System.err.println(msg); fail();}
void unexpected(Throwable t) {failed++; t.printStackTrace();}
void check(boolean cond) {if (cond) pass(); else fail();}
void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
void debug(String message) {if(debug) { System.out.println(message); } }
public static void main(String[] args) throws Throwable {
Class<?> k = new Object(){}.getClass().getEnclosingClass();
try {k.getMethod("instanceMain",String[].class)
.invoke( k.newInstance(), (Object) args);}
catch (Throwable e) {throw e.getCause();}}
public void instanceMain(String[] args) throws Throwable {
try {test(args);} catch (Throwable t) {unexpected(t);}
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View File

@ -0,0 +1,283 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
/* @test
* @bug 4927640
* @summary Tests the SCTP protocol implementation
* @author chegar
*/
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.nio.channels.AlreadyConnectedException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ConnectionPendingException;
import java.nio.channels.NoConnectionPendingException;
import java.nio.channels.UnresolvedAddressException;
import java.nio.channels.UnsupportedAddressTypeException;
import com.sun.nio.sctp.SctpChannel;
import com.sun.nio.sctp.SctpServerChannel;
import static java.lang.System.out;
import static java.lang.System.err;
/**
* Tests connect, finishConnect, isConnectionPending,
* getRemoteAddresses and association.
*/
public class Connect {
final CountDownLatch finishedLatch = new CountDownLatch(1);
void test(String[] args) {
SocketAddress address = null;
Server server = null;
if (!Util.isSCTPSupported()) {
out.println("SCTP protocol is not supported");
out.println("Test cannot be run");
return;
}
if (args.length == 2) {
/* requested to connect to a specific address */
try {
int port = Integer.valueOf(args[1]);
address = new InetSocketAddress(args[0], port);
} catch (NumberFormatException nfe) {
err.println(nfe);
}
} else {
/* start server on local machine, default */
try {
server = new Server();
server.start();
address = server.address();
debug("Server started and listening on " + address);
} catch (IOException ioe) {
ioe.printStackTrace();
return;
}
}
doTest(address);
}
void doTest(SocketAddress addr) {
SctpChannel channel = null;
final SocketAddress peerAddress = addr;
try {
channel = SctpChannel.open();
/* TEST 0.5 Verify default values for new/unconnected channel */
check(channel.getRemoteAddresses().isEmpty(),
"non empty set for unconnected channel");
check(channel.association() == null,
"non-null association for unconnected channel");
check(!channel.isConnectionPending(),
"should not have a connection pending");
/* TEST 1: non-blocking connect */
channel.configureBlocking(false);
if (channel.connect(peerAddress) != true) {
debug("non-blocking connect did not immediately succeed");
check(channel.isConnectionPending(),
"should return true for isConnectionPending");
try {
channel.connect(peerAddress);
fail("should have thrown ConnectionPendingException");
} catch (ConnectionPendingException cpe) {
pass();
} catch (IOException ioe) {
unexpected(ioe);
}
channel.configureBlocking(true);
check(channel.finishConnect(),
"finishConnect should have returned true");
}
/* TEST 1.5 Verify after connect */
check(!channel.getRemoteAddresses().isEmpty(),
"empty set for connected channel");
check(channel.association() != null,
"null association for connected channel");
check(!channel.isConnectionPending(),
"pending connection for connected channel");
/* TEST 2: Verify AlreadyConnectedException thrown */
try {
channel.connect(peerAddress);
fail("should have thrown AlreadyConnectedException");
} catch (AlreadyConnectedException unused) {
pass();
} catch (IOException ioe) {
unexpected(ioe);
}
/* TEST 3: UnresolvedAddressException */
channel.close();
channel = SctpChannel.open();
InetSocketAddress unresolved =
InetSocketAddress.createUnresolved("xxyyzzabc", 4567);
try {
channel.connect(unresolved);
fail("should have thrown UnresolvedAddressException");
} catch (UnresolvedAddressException unused) {
pass();
} catch (IOException ioe) {
unexpected(ioe);
}
/* TEST 4: UnsupportedAddressTypeException */
SocketAddress unsupported = new UnsupportedSocketAddress();
try {
channel.connect(unsupported);
fail("should have thrown UnsupportedAddressTypeException");
} catch (UnsupportedAddressTypeException unused) {
pass();
} catch (IOException ioe) {
unexpected(ioe);
}
/* TEST 5: ClosedChannelException */
channel.close();
final SctpChannel closedChannel = channel;
testCCE(new Callable<Void>() {
public Void call() throws IOException {
closedChannel.connect(peerAddress); return null; } });
/* TEST 5.5 getRemoteAddresses */
testCCE(new Callable<Void>() {
public Void call() throws IOException {
closedChannel.getRemoteAddresses(); return null; } });
testCCE(new Callable<Void>() {
public Void call() throws IOException {
closedChannel.association(); return null; } });
check(!channel.isConnectionPending(),
"pending connection for closed channel");
/* Run some more finishConnect tests */
/* TEST 6: NoConnectionPendingException */
channel = SctpChannel.open();
try {
channel.finishConnect();
fail("should have thrown NoConnectionPendingException");
} catch (NoConnectionPendingException unused) {
pass();
} catch (IOException ioe) {
unexpected(ioe);
}
/* TEST 7: ClosedChannelException */
channel.close();
final SctpChannel cceChannel = channel;
testCCE(new Callable<Void>() {
public Void call() throws IOException {
cceChannel.finishConnect(); return null; } });
} catch (IOException ioe) {
unexpected(ioe);
} finally {
finishedLatch.countDown();
try { if (channel != null) channel.close(); }
catch (IOException e) { unexpected(e);}
}
}
class UnsupportedSocketAddress extends SocketAddress { }
void testCCE(Callable callable) {
try {
callable.call();
fail("should have thrown ClosedChannelException");
} catch (ClosedChannelException cce) {
pass();
} catch (Exception ioe) {
unexpected(ioe);
}
}
class Server implements Runnable
{
final InetSocketAddress serverAddr;
private SctpServerChannel ssc;
public Server() throws IOException {
ssc = SctpServerChannel.open().bind(null);
java.util.Set<SocketAddress> addrs = ssc.getAllLocalAddresses();
if (addrs.isEmpty())
debug("addrs should not be empty");
serverAddr = (InetSocketAddress) addrs.iterator().next();
}
public void start() {
(new Thread(this, "Server-" + serverAddr.getPort())).start();
}
public InetSocketAddress address() {
return serverAddr;
}
@Override
public void run() {
SctpChannel sc = null;
try {
sc = ssc.accept();
finishedLatch.await();
} catch (IOException ioe) {
unexpected(ioe);
} catch (InterruptedException ie) {
unexpected(ie);
} finally {
try { if (ssc != null) ssc.close(); }
catch (IOException ioe) { unexpected(ioe); }
try { if (sc != null) sc.close(); }
catch (IOException ioe) { unexpected(ioe); }
}
}
}
//--------------------- Infrastructure ---------------------------
boolean debug = true;
volatile int passed = 0, failed = 0;
void pass() {passed++;}
void fail() {failed++; Thread.dumpStack();}
void fail(String msg) {System.err.println(msg); fail();}
void unexpected(Throwable t) {failed++; t.printStackTrace();}
void check(boolean cond) {if (cond) pass(); else fail();}
void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
void debug(String message) {if(debug) { System.out.println(message); } }
public static void main(String[] args) throws Throwable {
Class<?> k = new Object(){}.getClass().getEnclosingClass();
try {k.getMethod("instanceMain",String[].class)
.invoke( k.newInstance(), (Object) args);}
catch (Throwable e) {throw e.getCause();}}
public void instanceMain(String[] args) throws Throwable {
try {test(args);} catch (Throwable t) {unexpected(t);}
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View File

@ -0,0 +1,361 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
/* @test
* @bug 4927640
* @summary Tests the SCTP protocol implementation
* @author chegar
*/
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.nio.ByteBuffer;
import java.nio.channels.NotYetConnectedException;
import java.nio.channels.ClosedChannelException;
import com.sun.nio.sctp.AbstractNotificationHandler;
import com.sun.nio.sctp.AssociationChangeNotification;
import com.sun.nio.sctp.AssociationChangeNotification.AssocChangeEvent;
import com.sun.nio.sctp.HandlerResult;
import com.sun.nio.sctp.IllegalReceiveException;
import com.sun.nio.sctp.MessageInfo;
import com.sun.nio.sctp.Notification;
import com.sun.nio.sctp.SctpChannel;
import com.sun.nio.sctp.SctpServerChannel;
import com.sun.nio.sctp.ShutdownNotification;
import static java.lang.System.out;
import static java.lang.System.err;
public class Receive {
/* Latches used to synchronize between the client and server so that
* connections without any IO may not be closed without being accepted */
final CountDownLatch clientFinishedLatch = new CountDownLatch(1);
final CountDownLatch serverFinishedLatch = new CountDownLatch(1);
/* Used to verify that the ppid is being sent and received correctly */
static final int PPID = 5;
void test(String[] args) {
SocketAddress address = null;
Server server;
if (!Util.isSCTPSupported()) {
out.println("SCTP protocol is not supported");
out.println("Test cannot be run");
return;
}
if (args.length == 2) {
/* requested to connecct to a specific address */
try {
int port = Integer.valueOf(args[1]);
address = new InetSocketAddress(args[0], port);
} catch (NumberFormatException nfe) {
err.println(nfe);
}
} else {
/* start server on local machine, default */
try {
server = new Server();
server.start();
address = server.address();
debug("Server started and listening on " + address);
} catch (IOException ioe) {
ioe.printStackTrace();
return;
}
}
doTest(address);
}
void doTest(SocketAddress peerAddress) {
SctpChannel channel = null;
ByteBuffer buffer = ByteBuffer.allocate(Util.LARGE_BUFFER);
MessageInfo info;
try {
channel = SctpChannel.open();
ReceiveNotificationHandler handler =
new ReceiveNotificationHandler(channel);
/* TEST 1: Verify NotYetConnectedException thrown */
try {
channel.receive(buffer, null, handler);
fail("should have thrown NotYetConnectedException");
} catch (NotYetConnectedException unused) {
pass();
} catch (IOException ioe) {
unexpected(ioe);
}
channel.connect(peerAddress);
/* TEST 2: receive small message */
do {
debug("Test 2: invoking receive");
info = channel.receive(buffer, null, handler);
if (info == null) {
fail("unexpected null from receive");
return;
}
} while (!info.isComplete());
buffer.flip();
check(handler.receivedCommUp(), "SCTP_COMM_UP not received");
check(info != null, "info is null");
check(info.address() != null, "address is null");
check(info.association() != null, "association is null");
check(info.isComplete(), "message is not complete");
check(info.isUnordered() != true,
"message should not be unordered");
check(info.streamNumber() >= 0, "invalid stream number");
check(info.payloadProtocolID() == PPID, "PPID incorrect");
check(info.bytes() == Util.SMALL_MESSAGE.getBytes("ISO-8859-1").
length, "bytes received not equal to message length");
check(info.bytes() == buffer.remaining(), "bytes != remaining");
check(Util.compare(buffer, Util.SMALL_MESSAGE),
"received message not the same as sent message");
buffer.clear();
/* TEST 3: receive large message */
do {
debug("Test 3: invoking receive");
info = channel.receive(buffer, null, handler);
if (info == null) {
fail("unexpected null from receive");
return;
}
} while (!info.isComplete());
buffer.flip();
check(info != null, "info is null");
check(info.address() != null, "address is null");
check(info.association() != null, "association is null");
check(info.isComplete(), "message is not complete");
check(info.isUnordered() != true,
"message should not be unordered");
check(info.streamNumber() >= 0, "invalid stream number");
check(info.bytes() == Util.LARGE_MESSAGE.getBytes("ISO-8859-1").
length, "bytes received not equal to message length");
check(info.bytes() == buffer.remaining(), "bytes != remaining");
check(Util.compare(buffer, Util.LARGE_MESSAGE),
"received message not the same as sent message");
buffer.clear();
/* TEST 4: EOF */
buffer.clear(); // buffer position 0
info = channel.receive(buffer,null, handler);
check(info != null, "info is null");
check(info.bytes() == -1, "should have received EOF");
check(buffer.position() == 0, "buffer position should be unchanged");
/* TEST 5: ClosedChannelException */
channel.close();
try {
channel.receive(buffer, null, null);
fail("should have thrown ClosedChannelException");
} catch (ClosedChannelException cce) {
pass();
} catch (IOException ioe) {
unexpected(ioe);
}
handler = null;
/* TEST 6: handler returns RETURN after handling a notification */
ReceiveNotificationHandler handler2 =
new ReceiveNotificationHandler(null); /* HandlerResult.RETURN */
channel = SctpChannel.open(peerAddress, 0, 0);
info = channel.receive(buffer, null, handler2);
check(info == null, "channel should return null");
check(handler2.receivedCommUp(), "SCTP_COMM_UP not received");
check(buffer.position() == 0, "buffer position should be unchanged");
/* TEST 7: Non blocking channel return null if no data */
channel.configureBlocking(false);
info = channel.receive(buffer, null, null);
check(info == null, "non-blocking channel should return null");
check(buffer.position() == 0, "buffer position should be unchanged");
} catch (IOException ioe) {
unexpected(ioe);
} finally {
clientFinishedLatch.countDown();
try { serverFinishedLatch.await(10L, TimeUnit.SECONDS); }
catch (InterruptedException ie) { unexpected(ie); }
if (channel != null) {
try { channel.close(); }
catch (IOException e) { unexpected (e);}
}
}
}
class Server implements Runnable
{
final InetSocketAddress serverAddr;
private SctpServerChannel ssc;
public Server() throws IOException {
ssc = SctpServerChannel.open().bind(null);
java.util.Set<SocketAddress> addrs = ssc.getAllLocalAddresses();
if (addrs.isEmpty())
debug("addrs should not be empty");
serverAddr = (InetSocketAddress) addrs.iterator().next();
}
public void start() {
(new Thread(this, "Server-" + serverAddr.getPort())).start();
}
public InetSocketAddress address() {
return serverAddr;
}
@Override
public void run() {
try {
SctpChannel sc = ssc.accept();
/* send a small message */
MessageInfo info = MessageInfo.createOutgoing(null, 0)
.payloadProtocolID(PPID);
ByteBuffer buf = ByteBuffer.allocateDirect(Util.SMALL_BUFFER);
buf.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1"));
buf.flip();
debug("sending small message: " + buf);
sc.send(buf, info);
/* send a large message */
buf = ByteBuffer.allocateDirect(Util.LARGE_BUFFER);
buf.put(Util.LARGE_MESSAGE.getBytes("ISO-8859-1"));
buf.flip();
debug("sending large message: " + buf);
sc.send(buf, info);
sc.shutdown();
debug("shutdown");
ReceiveNotificationHandler handler =
new ReceiveNotificationHandler(sc);
sc.receive(buf, null, handler);
sc.close();
/* accept another socket for the TEST 6 */
sc = ssc.accept();
ssc.close();
clientFinishedLatch.await(10L, TimeUnit.SECONDS);
serverFinishedLatch.countDown();
sc.close();
} catch (IOException ioe) {
unexpected(ioe);
} catch (InterruptedException ie) {
unexpected(ie);
}
}
}
class ReceiveNotificationHandler extends AbstractNotificationHandler<Object>
{
SctpChannel channel;
boolean receivedCommUp; // false
public ReceiveNotificationHandler(SctpChannel channel) {
this.channel = channel;
}
public boolean receivedCommUp() {
return receivedCommUp;
}
@Override
public HandlerResult handleNotification(
Notification notification, Object attachment) {
fail("Unknown notification type");
return HandlerResult.CONTINUE;
}
@Override
public HandlerResult handleNotification(
AssociationChangeNotification notification, Object attachment) {
AssocChangeEvent event = notification.event();
debug("AssociationChangeNotification");
debug(" Association: " + notification.association());
debug(" Event: " + event);
if (event.equals(AssocChangeEvent.COMM_UP))
receivedCommUp = true;
if (channel == null)
return HandlerResult.RETURN;
/* TEST 4: IllegalReceiveException - If the given handler invokes
* the receive method of this channel*/
ByteBuffer buffer = ByteBuffer.allocate(10);
try {
channel.receive(buffer, null, this);
fail("IllegalReceiveException expected");
} catch (IllegalReceiveException unused) {
pass();
} catch (IOException ioe) {
unexpected(ioe);
}
return HandlerResult.CONTINUE;
}
@Override
public HandlerResult handleNotification(
ShutdownNotification notification, Object attachment) {
debug("ShutdownNotification");
debug(" Association: " + notification.association());
return HandlerResult.CONTINUE;
}
}
//--------------------- Infrastructure ---------------------------
boolean debug = true;
volatile int passed = 0, failed = 0;
void pass() {passed++;}
void fail() {failed++; Thread.dumpStack();}
void fail(String msg) {System.err.println(msg); fail();}
void unexpected(Throwable t) {failed++; t.printStackTrace();}
void check(boolean cond) {if (cond) pass(); else fail();}
void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
void debug(String message) {if(debug) {
System.out.println(Thread.currentThread() + " " + message); } }
public static void main(String[] args) throws Throwable {
Class<?> k = new Object(){}.getClass().getEnclosingClass();
try {k.getMethod("instanceMain",String[].class)
.invoke( k.newInstance(), (Object) args);}
catch (Throwable e) {throw e.getCause();}}
public void instanceMain(String[] args) throws Throwable {
try {test(args);} catch (Throwable t) {unexpected(t);}
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View File

@ -0,0 +1,419 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
/* @test
* @bug 4927640
* @summary Tests the SCTP protocol implementation
* @author chegar
*/
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.nio.ByteBuffer;
import java.nio.channels.NotYetConnectedException;
import java.nio.channels.ClosedChannelException;
import com.sun.nio.sctp.AbstractNotificationHandler;
import com.sun.nio.sctp.Association;
import com.sun.nio.sctp.AssociationChangeNotification;
import com.sun.nio.sctp.AssociationChangeNotification.AssocChangeEvent;
import com.sun.nio.sctp.HandlerResult;
import com.sun.nio.sctp.InvalidStreamException;
import com.sun.nio.sctp.MessageInfo;
import com.sun.nio.sctp.Notification;
import com.sun.nio.sctp.SctpChannel;
import com.sun.nio.sctp.SctpServerChannel;
import static java.lang.System.out;
import static java.lang.System.err;
public class Send {
/* Latches used to synchronize between the client and server so that
* connections without any IO may not be closed without being accepted */
final CountDownLatch clientFinishedLatch = new CountDownLatch(1);
final CountDownLatch serverFinishedLatch = new CountDownLatch(1);
SendNotificationHandler handler = new SendNotificationHandler();
void test(String[] args) {
SocketAddress address = null;
Server server = null;
if (!Util.isSCTPSupported()) {
out.println("SCTP protocol is not supported");
out.println("Test cannot be run");
return;
}
if (args.length == 2) {
/* requested to connecct to a specific address */
try {
int port = Integer.valueOf(args[1]);
address = new InetSocketAddress(args[0], port);
} catch (NumberFormatException nfe) {
err.println(nfe);
}
} else {
/* start server on local machine, default */
try {
server = new Server();
server.start();
address = server.address();
debug("Server started and listening on " + address);
} catch (IOException ioe) {
ioe.printStackTrace();
return;
}
}
doTest(address);
}
void doTest(SocketAddress peerAddress) {
SctpChannel channel = null;
ByteBuffer buffer = ByteBuffer.allocate(Util.LARGE_BUFFER);
MessageInfo info = MessageInfo.createOutgoing(null, 0);
try {
channel = SctpChannel.open();
/* TEST 1: Verify NotYetConnectedException thrown */
try {
channel.send(buffer, info);
fail("should have thrown NotYetConnectedException");
} catch (NotYetConnectedException unused) {
pass();
} catch (IOException ioe) {
unexpected(ioe);
}
channel.connect(peerAddress);
/* Receive CommUp */
channel.receive(buffer, null, handler);
/* save for TEST 8 */
Association association = channel.association();
/* TEST 2: send small message */
int streamNumber = 0;
debug("sending on stream number: " + streamNumber);
info = MessageInfo.createOutgoing(null, streamNumber);
buffer.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1"));
buffer.flip();
int position = buffer.position();
int remaining = buffer.remaining();
debug("sending small message: " + buffer);
int sent = channel.send(buffer, info);
check(sent == remaining, "sent should be equal to remaining");
check(buffer.position() == (position + sent),
"buffers position should have been incremented by sent");
buffer.clear();
/* TEST 3: send large message */
streamNumber = handler.maxOutStreams() - 1;
debug("sending on stream number: " + streamNumber);
info = MessageInfo.createOutgoing(null, streamNumber);
buffer.put(Util.LARGE_MESSAGE.getBytes("ISO-8859-1"));
buffer.flip();
position = buffer.position();
remaining = buffer.remaining();
debug("sending large message: " + buffer);
sent = channel.send(buffer, info);
check(sent == remaining, "sent should be equal to remaining");
check(buffer.position() == (position + sent),
"buffers position should have been incremented by sent");
/* TEST 4: InvalidStreamExcepton */
streamNumber = handler.maxInStreams;
info = MessageInfo.createOutgoing(null, streamNumber);
buffer.clear();
buffer.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1"));
buffer.flip();
position = buffer.position();
remaining = buffer.remaining();
debug("sending on stream number: " + streamNumber);
debug("sending small message: " + buffer);
try {
sent = channel.send(buffer, info);
fail("should have thrown InvalidStreamExcepton");
} catch (InvalidStreamException ise){
pass();
} catch (IOException ioe) {
unexpected(ioe);
}
check(buffer.remaining() == remaining,
"remaining should not be changed");
check(buffer.position() == position,
"buffers position should not be changed");
/* TEST 5: Non blocking send should return zero if there is
insufficient room in the underlying output buffer */
buffer.clear();
channel.configureBlocking(false);
info = MessageInfo.createOutgoing(null, 1);
buffer.put(Util.LARGE_MESSAGE.getBytes("ISO-8859-1"));
buffer.flip();
int count = 0; // do not loop forever
do {
position = buffer.position();
remaining = buffer.remaining();
debug("sending large message: " + buffer);
sent = channel.send(buffer, info);
if (sent == 0) {
check(buffer.remaining() == remaining,
"remaining should not be changed");
check(buffer.position() == position,
"buffers position should not be changed");
}
buffer.rewind();
} while (sent != 0 && count++ < 100);
/* TEST 6: ClosedChannelException */
channel.close();
try {
channel.send(buffer, info);
fail("should have thrown ClosedChannelException");
} catch (ClosedChannelException cce) {
pass();
} catch (IOException ioe) {
unexpected(ioe);
}
/* TEST 7: send without previous receive.
* Verify that send can still throw InvalidStreamExcepton */
debug("Opening new channel.");
channel = SctpChannel.open(peerAddress, 0, 0);
streamNumber = Short.MAX_VALUE - 1;
info = MessageInfo.createOutgoing(null, streamNumber);
buffer.clear();
buffer.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1"));
buffer.flip();
position = buffer.position();
remaining = buffer.remaining();
debug("sending on stream number: " + streamNumber);
debug("sending small message: " + buffer);
try {
sent = channel.send(buffer, info);
fail("should have thrown InvalidStreamExcepton");
} catch (InvalidStreamException ise){
pass();
} catch (IOException ioe) {
unexpected(ioe);
}
check(buffer.remaining() == remaining,
"remaining should not be changed");
check(buffer.position() == position,
"buffers position should not be changed");
/* Receive CommUp */
channel.receive(buffer, null, handler);
check(handler.receivedCommUp(), "should have received COMM_UP");
/* TEST 8: Send to an invalid preferred SocketAddress */
SocketAddress addr = new InetSocketAddress("123.123.123.123", 3456);
info = MessageInfo.createOutgoing(addr, 0);
debug("sending to " + addr);
debug("sending small message: " + buffer);
try {
sent = channel.send(buffer, info);
fail("Invalid address should have thrown an Exception.");
} catch (Exception e){
pass();
debug("OK, caught " + e);
}
} catch (IOException ioe) {
unexpected(ioe);
} finally {
clientFinishedLatch.countDown();
try { serverFinishedLatch.await(10L, TimeUnit.SECONDS); }
catch (InterruptedException ie) { unexpected(ie); }
if (channel != null) {
try { channel.close(); }
catch (IOException e) { unexpected (e);}
}
}
}
class Server implements Runnable
{
final InetSocketAddress serverAddr;
private SctpServerChannel ssc;
public Server() throws IOException {
ssc = SctpServerChannel.open().bind(null);
java.util.Set<SocketAddress> addrs = ssc.getAllLocalAddresses();
if (addrs.isEmpty())
debug("addrs should not be empty");
serverAddr = (InetSocketAddress) addrs.iterator().next();
}
public void start() {
(new Thread(this, "Server-" + serverAddr.getPort())).start();
}
public InetSocketAddress address() {
return serverAddr;
}
@Override
public void run() {
ByteBuffer buffer = ByteBuffer.allocateDirect(Util.LARGE_BUFFER);
SctpChannel sc1 = null, sc2 = null;
try {
sc1 = ssc.accept();
/* receive a small message */
MessageInfo info;
do {
info = sc1.receive(buffer, null, null);
if (info == null) {
fail("Server: unexpected null from receive");
return;
}
} while (!info.isComplete());
buffer.flip();
check(info != null, "info is null");
check(info.streamNumber() == 0,
"message not sent on the correct stream");
check(info.bytes() == Util.SMALL_MESSAGE.getBytes("ISO-8859-1").
length, "bytes received not equal to message length");
check(info.bytes() == buffer.remaining(), "bytes != remaining");
check(Util.compare(buffer, Util.SMALL_MESSAGE),
"received message not the same as sent message");
/* receive a large message */
buffer.clear();
do {
info = sc1.receive(buffer, null, null);
if (info == null) {
fail("Server: unexpected null from receive");
return;
}
} while (!info.isComplete());
buffer.flip();
check(info != null, "info is null");
check(info.streamNumber() == handler.maxOutStreams() - 1,
"message not sent on the correct stream");
check(info.bytes() == Util.LARGE_MESSAGE.getBytes("ISO-8859-1").
length, "bytes received not equal to message length");
check(info.bytes() == buffer.remaining(), "bytes != remaining");
check(Util.compare(buffer, Util.LARGE_MESSAGE),
"received message not the same as sent message");
/* TEST 7 ++ */
sc2 = ssc.accept();
clientFinishedLatch.await(10L, TimeUnit.SECONDS);
serverFinishedLatch.countDown();
} catch (IOException ioe) {
unexpected(ioe);
} catch (InterruptedException ie) {
unexpected(ie);
} finally {
try { if (ssc != null) ssc.close(); }
catch (IOException unused) {}
try { if (sc1 != null) sc1.close(); }
catch (IOException unused) {}
try { if (sc2 != null) sc2.close(); }
catch (IOException unused) {}
}
}
}
class SendNotificationHandler extends AbstractNotificationHandler<Void>
{
boolean receivedCommUp; // false
int maxInStreams;
int maxOutStreams;
public boolean receivedCommUp() {
return receivedCommUp;
}
public int maxInStreams() {
return maxInStreams;
}
public int maxOutStreams(){
return maxOutStreams;
}
@Override
public HandlerResult handleNotification(
Notification notification, Void attachment) {
fail("Unknown notification type");
return HandlerResult.CONTINUE;
}
@Override
public HandlerResult handleNotification(
AssociationChangeNotification notification, Void attachment) {
AssocChangeEvent event = notification.event();
Association association = notification.association();
debug("AssociationChangeNotification");
debug(" Association: " + notification.association());
debug(" Event: " + event);
if (event.equals(AssocChangeEvent.COMM_UP))
receivedCommUp = true;
this.maxInStreams = association.maxInboundStreams();
this.maxOutStreams = association.maxOutboundStreams();
return HandlerResult.RETURN;
}
}
//--------------------- Infrastructure ---------------------------
boolean debug = true;
volatile int passed = 0, failed = 0;
void pass() {passed++;}
void fail() {failed++; Thread.dumpStack();}
void fail(String msg) {System.err.println(msg); fail();}
void unexpected(Throwable t) {failed++; t.printStackTrace();}
void check(boolean cond) {if (cond) pass(); else fail();}
void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
void debug(String message) {if(debug) { System.out.println(message); } }
public static void main(String[] args) throws Throwable {
Class<?> k = new Object(){}.getClass().getEnclosingClass();
try {k.getMethod("instanceMain",String[].class)
.invoke( k.newInstance(), (Object) args);}
catch (Throwable e) {throw e.getCause();}}
public void instanceMain(String[] args) throws Throwable {
try {test(args);} catch (Throwable t) {unexpected(t);}
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View File

@ -0,0 +1,274 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
/* @test
* @bug 4927640
* @summary Tests the SCTP protocol implementation
* @author chegar
*/
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.NotYetConnectedException;
import com.sun.nio.sctp.AbstractNotificationHandler;
import com.sun.nio.sctp.HandlerResult;
import com.sun.nio.sctp.MessageInfo;
import com.sun.nio.sctp.SctpChannel;
import com.sun.nio.sctp.SctpServerChannel;
import com.sun.nio.sctp.ShutdownNotification;
import static java.lang.System.out;
import static java.lang.System.err;
public class Shutdown {
static CountDownLatch finishedLatch = new CountDownLatch(1);
static CountDownLatch sentLatch = new CountDownLatch(1);
void test(String[] args) {
SocketAddress address = null;
ShutdownServer server = null;
if (!Util.isSCTPSupported()) {
out.println("SCTP protocol is not supported");
out.println("Test cannot be run");
return;
}
if (args.length == 2) {
/* requested to connecct to a specific address */
try {
int port = Integer.valueOf(args[1]);
address = new InetSocketAddress(args[0], port);
} catch (NumberFormatException nfe) {
err.println(nfe);
}
} else {
/* start server on local machine, default */
try {
server = new ShutdownServer();
server.start();
address = server.address();
debug("Server started and listening on " + address);
} catch (IOException ioe) {
ioe.printStackTrace();
return;
}
}
doTest(address);
}
void doTest(SocketAddress peerAddress) {
SctpChannel channel = null;
ByteBuffer buffer = ByteBuffer.allocate(Util.SMALL_BUFFER);
MessageInfo info;
try {
channel = SctpChannel.open();
/* TEST 1: Verify NotYetConnectedException thrown */
debug("Test 1: NotYetConnectedException");
try {
channel.shutdown();
fail("shutdown not throwing expected NotYetConnectedException");
} catch (NotYetConnectedException unused) {
pass();
} catch (IOException ioe) {
unexpected(ioe);
}
channel.connect(peerAddress);
sentLatch.await();
channel.shutdown();
/* TEST 2: receive data sent before shutdown */
do {
debug("Test 2: invoking receive");
info = channel.receive(buffer, null, null);
if (info == null) {
fail("unexpected null from receive");
return;
}
} while (!info.isComplete());
buffer.flip();
check(info != null, "info is null");
check(info.bytes() == Util.SMALL_MESSAGE.getBytes("ISO-8859-1").
length, "bytes received not equal to message length");
check(info.bytes() == buffer.remaining(), "bytes != remaining");
check(Util.compare(buffer, Util.SMALL_MESSAGE),
"received message not the same as sent message");
buffer.clear();
/* TEST 3: receive notifications on the SCTP stack */
debug("Test 3: receive notifications");
while ((info = channel.receive(buffer, null, null )) != null &&
info.bytes() != -1 );
/* TEST 4: If the channel is already shutdown then invoking this
* method has no effect. */
debug("Test 4: no-op");
try {
channel.shutdown();
pass();
} catch (IOException ioe) {
unexpected(ioe);
}
/* TEST 5: Further sends will throw ClosedChannelException */
debug("Test 5: ClosedChannelException");
info = MessageInfo.createOutgoing(null, 1);
try {
channel.send(buffer, info);
fail("shutdown not throwing expected ClosedChannelException");
} catch (ClosedChannelException unused) {
pass();
} catch (IOException ioe) {
unexpected(ioe);
}
} catch (IOException ioe) {
unexpected(ioe);
} catch (InterruptedException ie) {
unexpected(ie);
}finally {
finishedLatch.countDown();
try { if (channel != null) channel.close(); }
catch (IOException e) { unexpected(e);}
}
}
class ShutdownServer implements Runnable
{
final InetSocketAddress serverAddr;
private SctpServerChannel ssc;
public ShutdownServer() throws IOException {
ssc = SctpServerChannel.open().bind(null);
//serverAddr = (InetSocketAddress)(ssc.getAllLocalAddresses().iterator().next());
java.util.Set<SocketAddress> addrs = ssc.getAllLocalAddresses();
if (addrs.isEmpty())
debug("addrs should not be empty");
serverAddr = (InetSocketAddress) addrs.iterator().next();
}
public void start() {
(new Thread(this, "ShutdownServer-" + serverAddr.getPort())).start();
}
public InetSocketAddress address() {
return serverAddr;
}
@Override
public void run() {
SctpChannel sc = null;
try {
sc = ssc.accept();
/* send a message */
MessageInfo info = MessageInfo.createOutgoing(null, 1);
ByteBuffer buf = ByteBuffer.allocateDirect(Util.SMALL_BUFFER);
buf.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1"));
buf.flip();
sc.send(buf, info);
/* notify client that the data has been sent */
sentLatch.countDown();
/* wait until after the client has finished its tests */
finishedLatch.await();
buf.clear();
ShutdownNotificationHandler handler =
new ShutdownNotificationHandler();
BooleanWrapper bool = new BooleanWrapper();
sc.configureBlocking(false);
sc.receive(buf, bool, handler);
check(bool.booleanValue(), "SHUTDOWN not received on Server");
} catch (IOException ioe) {
ioe.printStackTrace();
} catch (InterruptedException ie) {
ie.printStackTrace();
} finally {
try { if (ssc != null) ssc.close(); }
catch (IOException ioe) { unexpected(ioe); }
try { if (sc != null) sc.close(); }
catch (IOException ioe) { unexpected(ioe); }
}
}
}
class BooleanWrapper {
boolean bool;
boolean booleanValue() {
return bool;
}
void booleanValue(boolean value) {
bool = value;
}
}
class ShutdownNotificationHandler extends AbstractNotificationHandler<BooleanWrapper>
{
@Override
public HandlerResult handleNotification(
ShutdownNotification sn, BooleanWrapper bool)
{
bool.booleanValue(true);
debug(sn.toString());
return HandlerResult.RETURN;
}
}
//--------------------- Infrastructure ---------------------------
boolean debug = true;
volatile int passed = 0, failed = 0;
void pass() {passed++;}
void fail() {failed++; Thread.dumpStack();}
void fail(String msg) {System.err.println(msg); fail();}
void unexpected(Throwable t) {failed++; t.printStackTrace();}
void check(boolean cond) {if (cond) pass(); else fail();}
void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
void debug(String message) {if(debug) { System.out.println(message); } }
public static void main(String[] args) throws Throwable {
Class<?> k = new Object(){}.getClass().getEnclosingClass();
try {k.getMethod("instanceMain",String[].class)
.invoke( k.newInstance(), (Object) args);}
catch (Throwable e) {throw e.getCause();}}
public void instanceMain(String[] args) throws Throwable {
try {test(args);} catch (Throwable t) {unexpected(t);}
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View File

@ -0,0 +1,157 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
/* @test
* @bug 4927640
* @summary Tests the SCTP protocol implementation
* @author chegar
*/
import java.io.IOException;
import java.util.Set;
import java.util.List;
import java.util.Arrays;
import java.nio.channels.ClosedChannelException;
import com.sun.nio.sctp.SctpChannel;
import com.sun.nio.sctp.SctpSocketOption;
import static com.sun.nio.sctp.SctpStandardSocketOption.*;
import static java.lang.System.out;
public class SocketOptionTests {
<T> void checkOption(SctpChannel sc, SctpSocketOption<T> name,
T expectedValue) throws IOException {
T value = sc.getOption(name);
check(value.equals(expectedValue), name + ": value (" + value +
") not as expected (" + expectedValue + ")");
}
<T> void optionalSupport(SctpChannel sc, SctpSocketOption<T> name,
T value) {
try {
sc.setOption(name, value);
checkOption(sc, name, value);
} catch (IOException e) {
/* Informational only, not all options have native support */
out.println(name + " not supported. " + e);
}
}
void test(String[] args) {
if (!Util.isSCTPSupported()) {
out.println("SCTP protocol is not supported");
out.println("Test cannot be run");
return;
}
try {
SctpChannel sc = SctpChannel.open();
/* check supported options */
Set<SctpSocketOption<?>> options = sc.supportedOptions();
List<? extends SctpSocketOption<?>> expected = Arrays.<SctpSocketOption<?>>asList(
SCTP_DISABLE_FRAGMENTS, SCTP_EXPLICIT_COMPLETE,
SCTP_FRAGMENT_INTERLEAVE, SCTP_INIT_MAXSTREAMS,
SCTP_NODELAY, SCTP_PRIMARY_ADDR, SCTP_SET_PEER_PRIMARY_ADDR,
SO_SNDBUF, SO_RCVBUF, SO_LINGER);
for (SctpSocketOption opt: expected) {
if (!options.contains(opt))
fail(opt.name() + " should be supported");
}
InitMaxStreams streams = InitMaxStreams.create(1024, 1024);
sc.setOption(SCTP_INIT_MAXSTREAMS, streams);
checkOption(sc, SCTP_INIT_MAXSTREAMS, streams);
streams = sc.getOption(SCTP_INIT_MAXSTREAMS);
check(streams.maxInStreams() == 1024, "Max in streams: value: "
+ streams.maxInStreams() + ", expected 1024 ");
check(streams.maxOutStreams() == 1024, "Max out streams: value: "
+ streams.maxOutStreams() + ", expected 1024 ");
optionalSupport(sc, SCTP_DISABLE_FRAGMENTS, true);
optionalSupport(sc, SCTP_EXPLICIT_COMPLETE, true);
optionalSupport(sc, SCTP_FRAGMENT_INTERLEAVE, 1);
//TODO: SCTP_PRIMARY_ADDR
//sc.bind(null);
//connect
//InetSocketAddress addr = new InetSocketAddress(0);
//sc.setOption(SCTP_PRIMARY_ADDR, addr);
sc.setOption(SCTP_NODELAY, true);
checkOption(sc, SCTP_NODELAY, true);
sc.setOption(SO_SNDBUF, 16*1024);
sc.setOption(SO_RCVBUF, 16*1024);
checkOption(sc, SO_LINGER, -1); /* default should be negative */
sc.setOption(SO_LINGER, 2000);
checkOption(sc, SO_LINGER, 2000);
/* NullPointerException */
try {
sc.setOption(null, "value");
fail("NullPointerException not thrown for setOption");
} catch (NullPointerException unused) {
pass();
}
try {
sc.getOption(null);
fail("NullPointerException not thrown for getOption");
} catch (NullPointerException unused) {
pass();
}
/* ClosedChannelException */
sc.close();
try {
sc.setOption(SCTP_INIT_MAXSTREAMS, streams);
fail("ClosedChannelException not thrown");
} catch (ClosedChannelException unused) {
pass();
}
} catch (IOException ioe) {
unexpected(ioe);
}
}
//--------------------- Infrastructure ---------------------------
boolean debug = true;
volatile int passed = 0, failed = 0;
void pass() {passed++;}
void fail() {failed++; Thread.dumpStack();}
void fail(String msg) {System.err.println(msg); fail();}
void unexpected(Throwable t) {failed++; t.printStackTrace();}
void check(boolean cond) {if (cond) pass(); else fail();}
void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
void debug(String message) {if(debug) { System.out.println(message); } }
public static void main(String[] args) throws Throwable {
Class<?> k = new Object(){}.getClass().getEnclosingClass();
try {k.getMethod("instanceMain",String[].class)
.invoke( k.newInstance(), (Object) args);}
catch (Throwable e) {throw e.getCause();}}
public void instanceMain(String[] args) throws Throwable {
try {test(args);} catch (Throwable t) {unexpected(t);}
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View File

@ -0,0 +1,138 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
import java.net.NetworkInterface;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.InetAddress;
import java.net.Inet4Address;
import java.net.SocketException;
import java.io.IOException;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.nio.ByteBuffer;
import com.sun.nio.sctp.SctpChannel;
import static java.lang.System.out;
public class Util {
static final int SMALL_BUFFER = 128;
static final String SMALL_MESSAGE =
"Under the bridge and over the dam, looking for berries, berries for jam";
static final int LARGE_BUFFER = 32768;
static final String LARGE_MESSAGE;
static {
StringBuffer sb = new StringBuffer(LARGE_BUFFER);
for (int i=0; i<460; i++)
sb.append(SMALL_MESSAGE);
LARGE_MESSAGE = sb.toString();
}
static boolean isSCTPSupported() {
try {
SctpChannel c = SctpChannel.open();
c.close();
return true;
} catch (IOException ioe) {
ioe.printStackTrace();
} catch (UnsupportedOperationException e) {
out.println(e);
}
return false;
}
/**
* Returns a list of all the addresses on the system.
* @param inclLoopback
* if {@code true}, include the loopback addresses
* @param ipv4Only
* it {@code true}, only IPv4 addresses will be included
*/
static List<InetAddress> getAddresses(boolean inclLoopback,
boolean ipv4Only)
throws SocketException {
ArrayList<InetAddress> list = new ArrayList<InetAddress>();
Enumeration<NetworkInterface> nets =
NetworkInterface.getNetworkInterfaces();
for (NetworkInterface netInf : Collections.list(nets)) {
Enumeration<InetAddress> addrs = netInf.getInetAddresses();
for (InetAddress addr : Collections.list(addrs)) {
if (!list.contains(addr) &&
(inclLoopback ? true : !addr.isLoopbackAddress()) &&
(ipv4Only ? (addr instanceof Inet4Address) : true)) {
list.add(addr);
}
}
}
return list;
}
static void dumpAddresses(SctpChannel channel,
PrintStream printStream)
throws IOException {
Set<SocketAddress> addrs = channel.getAllLocalAddresses();
printStream.println("Local Addresses: ");
for (Iterator<SocketAddress> it = addrs.iterator(); it.hasNext(); ) {
InetSocketAddress addr = (InetSocketAddress)it.next();
printStream.println("\t" + addr);
}
}
/**
* Compare the contents of the given ByteBuffer with the contens of the
* given byte array. true if, and only if, the contents are the same.
*/
static boolean compare(ByteBuffer bb, byte[] message) {
if (message.length != bb.remaining()) {
out.println("Compare failed, byte array length != to buffer remaining");
return false;
}
for (int i=0; i<message.length; i++) {
byte b = bb.get();
if (message[i] != b) {
out.println("Position " + i + ": " + message[i] + " != " + b);
return false;
}
}
return true;
}
static boolean compare(ByteBuffer bb, String str) {
try{
return Util.compare(bb, str.getBytes("ISO-8859-1"));
} catch (UnsupportedEncodingException unsupported) {
throw new AssertionError(unsupported);
}
}
}

View File

@ -0,0 +1,320 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
/* @test
* @bug 4927640
* @summary Tests the SCTP protocol implementation
* @author chegar
*/
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.io.IOException;
import java.util.Set;
import java.util.Iterator;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.nio.ByteBuffer;
import com.sun.nio.sctp.Association;
import com.sun.nio.sctp.InvalidStreamException;
import com.sun.nio.sctp.MessageInfo;
import com.sun.nio.sctp.SctpMultiChannel;
import static java.lang.System.out;
import static java.lang.System.err;
public class Send {
/* Latches used to synchronize between the client and server so that
* connections without any IO may not be closed without being accepted */
final CountDownLatch clientFinishedLatch = new CountDownLatch(1);
final CountDownLatch serverFinishedLatch = new CountDownLatch(1);
void test(String[] args) {
SocketAddress address = null;
Server server = null;
if (!Util.isSCTPSupported()) {
out.println("SCTP protocol is not supported");
out.println("Test cannot be run");
return;
}
if (args.length == 2) {
/* requested to connecct to a specific address */
try {
int port = Integer.valueOf(args[1]);
address = new InetSocketAddress(args[0], port);
} catch (NumberFormatException nfe) {
err.println(nfe);
}
} else {
/* start server on local machine, default */
try {
server = new Server();
server.start();
address = server.address();
debug("Server started and listening on " + address);
} catch (IOException ioe) {
ioe.printStackTrace();
return;
}
}
doTest(address);
}
void doTest(SocketAddress peerAddress) {
SctpMultiChannel channel = null;
ByteBuffer buffer = ByteBuffer.allocate(Util.LARGE_BUFFER);
MessageInfo info = MessageInfo.createOutgoing(null, 0);
try {
channel = SctpMultiChannel.open();
/* TEST 1: send small message */
int streamNumber = 0;
debug("sending to " + peerAddress + " on stream number: " + streamNumber);
info = MessageInfo.createOutgoing(peerAddress, streamNumber);
buffer.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1"));
buffer.flip();
int position = buffer.position();
int remaining = buffer.remaining();
debug("sending small message: " + buffer);
int sent = channel.send(buffer, info);
check(sent == remaining, "sent should be equal to remaining");
check(buffer.position() == (position + sent),
"buffers position should have been incremented by sent");
/* TEST 2: receive the echoed message */
buffer.clear();
info = channel.receive(buffer, null, null);
buffer.flip();
check(info != null, "info is null");
check(info.streamNumber() == streamNumber,
"message not sent on the correct stream");
check(info.bytes() == Util.SMALL_MESSAGE.getBytes("ISO-8859-1").
length, "bytes received not equal to message length");
check(info.bytes() == buffer.remaining(), "bytes != remaining");
check(Util.compare(buffer, Util.SMALL_MESSAGE),
"received message not the same as sent message");
/* TEST 3: send large message */
Set<Association> assocs = channel.associations();
check(assocs.size() == 1, "there should be only one association");
Iterator<Association> it = assocs.iterator();
check(it.hasNext());
Association assoc = it.next();
streamNumber = assoc.maxOutboundStreams() - 1;
debug("sending on stream number: " + streamNumber);
info = MessageInfo.createOutgoing(assoc, null, streamNumber);
buffer.clear();
buffer.put(Util.LARGE_MESSAGE.getBytes("ISO-8859-1"));
buffer.flip();
position = buffer.position();
remaining = buffer.remaining();
debug("sending large message: " + buffer);
sent = channel.send(buffer, info);
check(sent == remaining, "sent should be equal to remaining");
check(buffer.position() == (position + sent),
"buffers position should have been incremented by sent");
/* TEST 4: receive the echoed message */
buffer.clear();
info = channel.receive(buffer, null, null);
buffer.flip();
check(info != null, "info is null");
check(info.streamNumber() == streamNumber,
"message not sent on the correct stream");
check(info.bytes() == Util.LARGE_MESSAGE.getBytes("ISO-8859-1").
length, "bytes received not equal to message length");
check(info.bytes() == buffer.remaining(), "bytes != remaining");
check(Util.compare(buffer, Util.LARGE_MESSAGE),
"received message not the same as sent message");
/* TEST 5: InvalidStreamExcepton */
streamNumber = assoc.maxOutboundStreams() + 1;
info = MessageInfo.createOutgoing(assoc, null, streamNumber);
buffer.clear();
buffer.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1"));
buffer.flip();
position = buffer.position();
remaining = buffer.remaining();
debug("sending on stream number: " + streamNumber);
debug("sending small message: " + buffer);
try {
sent = channel.send(buffer, info);
fail("should have thrown InvalidStreamExcepton");
} catch (InvalidStreamException ise){
pass();
} catch (IOException ioe) {
unexpected(ioe);
}
check(buffer.remaining() == remaining,
"remaining should not be changed");
check(buffer.position() == position,
"buffers position should not be changed");
/* TEST 5: getRemoteAddresses(Association) */
channel.getRemoteAddresses(assoc);
} catch (IOException ioe) {
unexpected(ioe);
} finally {
clientFinishedLatch.countDown();
try { serverFinishedLatch.await(10L, TimeUnit.SECONDS); }
catch (InterruptedException ie) { unexpected(ie); }
if (channel != null) {
try { channel.close(); }
catch (IOException e) { unexpected (e);}
}
}
}
class Server implements Runnable
{
final InetSocketAddress serverAddr;
private SctpMultiChannel serverChannel;
public Server() throws IOException {
serverChannel = SctpMultiChannel.open().bind(null);
java.util.Set<SocketAddress> addrs = serverChannel.getAllLocalAddresses();
if (addrs.isEmpty())
debug("addrs should not be empty");
serverAddr = (InetSocketAddress) addrs.iterator().next();
}
public void start() {
(new Thread(this, "Server-" + serverAddr.getPort())).start();
}
public InetSocketAddress address() {
return serverAddr;
}
@Override
public void run() {
ByteBuffer buffer = ByteBuffer.allocateDirect(Util.LARGE_BUFFER);
try {
MessageInfo info;
/* receive a small message */
do {
info = serverChannel.receive(buffer, null, null);
if (info == null) {
fail("Server: unexpected null from receive");
return;
}
} while (!info.isComplete());
buffer.flip();
check(info != null, "info is null");
check(info.streamNumber() == 0,
"message not sent on the correct stream");
check(info.bytes() == Util.SMALL_MESSAGE.getBytes("ISO-8859-1").
length, "bytes received not equal to message length");
check(info.bytes() == buffer.remaining(), "bytes != remaining");
check(Util.compare(buffer, Util.SMALL_MESSAGE),
"received message not the same as sent message");
check(info != null, "info is null");
Set<Association> assocs = serverChannel.associations();
check(assocs.size() == 1, "there should be only one association");
Iterator<Association> it = assocs.iterator();
check(it.hasNext());
Association assoc = it.next();
/* echo the message */
debug("Server: echoing first message");
buffer.flip();
int bytes = serverChannel.send(buffer, info);
debug("Server: sent " + bytes + "bytes");
/* receive a large message */
buffer.clear();
do {
info = serverChannel.receive(buffer, null, null);
if (info == null) {
fail("Server: unexpected null from receive");
return;
}
} while (!info.isComplete());
buffer.flip();
check(info.streamNumber() == assoc.maxInboundStreams() - 1,
"message not sent on the correct stream");
check(info.bytes() == Util.LARGE_MESSAGE.getBytes("ISO-8859-1").
length, "bytes received not equal to message length");
check(info.bytes() == buffer.remaining(), "bytes != remaining");
check(Util.compare(buffer, Util.LARGE_MESSAGE),
"received message not the same as sent message");
/* echo the message */
debug("Server: echoing second message");
buffer.flip();
bytes = serverChannel.send(buffer, info);
debug("Server: sent " + bytes + "bytes");
clientFinishedLatch.await(10L, TimeUnit.SECONDS);
serverFinishedLatch.countDown();
} catch (IOException ioe) {
unexpected(ioe);
} catch (InterruptedException ie) {
unexpected(ie);
} finally {
try { if (serverChannel != null) serverChannel.close(); }
catch (IOException unused) {}
}
}
}
//--------------------- Infrastructure ---------------------------
boolean debug = true;
volatile int passed = 0, failed = 0;
void pass() {passed++;}
void fail() {failed++; Thread.dumpStack();}
void fail(String msg) {System.err.println(msg); fail();}
void unexpected(Throwable t) {failed++; t.printStackTrace();}
void check(boolean cond) {if (cond) pass(); else fail();}
void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
void debug(String message) {if(debug) { System.out.println(message); } }
public static void main(String[] args) throws Throwable {
Class<?> k = new Object(){}.getClass().getEnclosingClass();
try {k.getMethod("instanceMain",String[].class)
.invoke( k.newInstance(), (Object) args);}
catch (Throwable e) {throw e.getCause();}}
public void instanceMain(String[] args) throws Throwable {
try {test(args);} catch (Throwable t) {unexpected(t);}
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View File

@ -0,0 +1,138 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
import java.net.NetworkInterface;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.InetAddress;
import java.net.Inet4Address;
import java.net.SocketException;
import java.io.IOException;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.nio.ByteBuffer;
import com.sun.nio.sctp.SctpChannel;
import static java.lang.System.out;
public class Util {
static final int SMALL_BUFFER = 128;
static final String SMALL_MESSAGE =
"Under the bridge and over the dam, looking for berries, berries for jam";
static final int LARGE_BUFFER = 32768;
static final String LARGE_MESSAGE;
static {
StringBuffer sb = new StringBuffer(LARGE_BUFFER);
for (int i=0; i<460; i++)
sb.append(SMALL_MESSAGE);
LARGE_MESSAGE = sb.toString();
}
static boolean isSCTPSupported() {
try {
SctpChannel c = SctpChannel.open();
c.close();
return true;
} catch (IOException ioe) {
ioe.printStackTrace();
} catch (UnsupportedOperationException e) {
out.println(e);
}
return false;
}
/**
* Returns a list of all the addresses on the system.
* @param inclLoopback
* if {@code true}, include the loopback addresses
* @param ipv4Only
* it {@code true}, only IPv4 addresses will be included
*/
static List<InetAddress> getAddresses(boolean inclLoopback,
boolean ipv4Only)
throws SocketException {
ArrayList<InetAddress> list = new ArrayList<InetAddress>();
Enumeration<NetworkInterface> nets =
NetworkInterface.getNetworkInterfaces();
for (NetworkInterface netInf : Collections.list(nets)) {
Enumeration<InetAddress> addrs = netInf.getInetAddresses();
for (InetAddress addr : Collections.list(addrs)) {
if (!list.contains(addr) &&
(inclLoopback ? true : !addr.isLoopbackAddress()) &&
(ipv4Only ? (addr instanceof Inet4Address) : true)) {
list.add(addr);
}
}
}
return list;
}
static void dumpAddresses(SctpChannel channel,
PrintStream printStream)
throws IOException {
Set<SocketAddress> addrs = channel.getAllLocalAddresses();
printStream.println("Local Addresses: ");
for (Iterator<SocketAddress> it = addrs.iterator(); it.hasNext(); ) {
InetSocketAddress addr = (InetSocketAddress)it.next();
printStream.println("\t" + addr);
}
}
/**
* Compare the contents of the given ByteBuffer with the contens of the
* given byte array. true if, and only if, the contents are the same.
*/
static boolean compare(ByteBuffer bb, byte[] message) {
if (message.length != bb.remaining()) {
out.println("Compare failed, byte array length != to buffer remaining");
return false;
}
for (int i=0; i<message.length; i++) {
byte b = bb.get();
if (message[i] != b) {
out.println("Position " + i + ": " + message[i] + " != " + b);
return false;
}
}
return true;
}
static boolean compare(ByteBuffer bb, String str) {
try{
return Util.compare(bb, str.getBytes("ISO-8859-1"));
} catch (UnsupportedEncodingException unsupported) {
throw new AssertionError(unsupported);
}
}
}

View File

@ -0,0 +1,274 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
/* @test
* @bug 4927640
* @summary Tests the SCTP protocol implementation
* @author chegar
*/
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.nio.channels.AlreadyConnectedException;
import java.nio.channels.AsynchronousCloseException;
import java.nio.channels.NotYetBoundException;
import java.nio.channels.ClosedByInterruptException;
import java.nio.channels.ClosedChannelException;
import com.sun.nio.sctp.SctpChannel;
import com.sun.nio.sctp.SctpServerChannel;
import static java.lang.System.out;
import static java.lang.System.err;
public class Accept {
static CountDownLatch acceptLatch = new CountDownLatch(1);
static CountDownLatch closeByIntLatch = new CountDownLatch(1);
static CountDownLatch asyncCloseLatch = new CountDownLatch(1);
AcceptServer server = null;
void test(String[] args) {
SocketAddress address = null;
if (!Util.isSCTPSupported()) {
out.println("SCTP protocol is not supported");
out.println("Test cannot be run");
return;
}
if (args.length == 2) {
/* requested to connecct to a specific address */
try {
int port = Integer.valueOf(args[1]);
address = new InetSocketAddress(args[0], port);
} catch (NumberFormatException nfe) {
err.println(nfe);
}
} else {
/* start server on local machine, default */
try {
server = new AcceptServer();
server.start();
address = server.address();
debug("Server started and listening on " + address);
} catch (IOException ioe) {
ioe.printStackTrace();
return;
}
}
doClient(address);
}
void doClient(SocketAddress peerAddress) {
SctpChannel channel = null;
try {
channel = SctpChannel.open(peerAddress, 0, 0);
acceptLatch.await();
/* for test 4 */
closeByIntLatch.await();
sleep(500);
server.thread().interrupt();
/* for test 5 */
asyncCloseLatch.await();
sleep(500);
server.channel().close();
/* wait for the server thread to finish */
join(server.thread(), 10000);
} catch (IOException ioe) {
unexpected(ioe);
} catch (InterruptedException ie) {
unexpected(ie);
} finally {
try { if (channel != null) channel.close(); }
catch (IOException e) { unexpected(e);}
}
}
class AcceptServer implements Runnable
{
final InetSocketAddress serverAddr;
private SctpServerChannel ssc;
private Thread serverThread;
public AcceptServer() throws IOException {
ssc = SctpServerChannel.open();
/* TEST 1: NotYetBoundException */
debug("TEST 1: NotYetBoundException");
try {
ssc.accept();
fail();
} catch (NotYetBoundException nybe) {
debug(" caught NotYetBoundException");
pass();
} catch (IOException ioe) {
unexpected(ioe);
}
ssc.bind(null);
java.util.Set<SocketAddress> addrs = ssc.getAllLocalAddresses();
if (addrs.isEmpty())
debug("addrs should not be empty");
serverAddr = (InetSocketAddress) addrs.iterator().next();
/* TEST 2: null if this channel is in non-blocking mode and no
* association is available to be accepted */
ssc.configureBlocking(false);
debug("TEST 2: non-blocking mode null");
try {
SctpChannel sc = ssc.accept();
check(sc == null, "non-blocking mode should return null");
} catch (IOException ioe) {
unexpected(ioe);
} finally {
ssc.configureBlocking(true);
}
}
void start() {
serverThread = new Thread(this, "AcceptServer-" +
serverAddr.getPort());
serverThread.start();
}
InetSocketAddress address() {
return serverAddr;
}
SctpServerChannel channel() {
return ssc;
}
Thread thread() {
return serverThread;
}
@Override
public void run() {
SctpChannel sc = null;
try {
/* TEST 3: accepted channel */
debug("TEST 3: accepted channel");
sc = ssc.accept();
checkAcceptedChannel(sc);
acceptLatch.countDown();
/* TEST 4: ClosedByInterruptException */
debug("TEST 4: ClosedByInterruptException");
try {
closeByIntLatch.countDown();
ssc.accept();
fail();
} catch (ClosedByInterruptException unused) {
debug(" caught ClosedByInterruptException");
pass();
}
/* TEST 5: AsynchronousCloseException */
debug("TEST 5: AsynchronousCloseException");
/* reset thread interrupt status */
Thread.currentThread().interrupted();
ssc = SctpServerChannel.open().bind(null);
try {
asyncCloseLatch.countDown();
ssc.accept();
fail();
} catch (AsynchronousCloseException unused) {
debug(" caught AsynchronousCloseException");
pass();
}
/* TEST 6: ClosedChannelException */
debug("TEST 6: ClosedChannelException");
try {
ssc.accept();
fail();
} catch (ClosedChannelException unused) {
debug(" caught ClosedChannelException");
pass();
}
ssc = null;
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
try { if (ssc != null) ssc.close(); }
catch (IOException ioe) { unexpected(ioe); }
try { if (sc != null) sc.close(); }
catch (IOException ioe) { unexpected(ioe); }
}
}
}
void checkAcceptedChannel(SctpChannel sc) {
try {
debug("Checking accepted SctpChannel");
check(sc.association() != null,
"accepted channel should have an association");
check(!(sc.getRemoteAddresses().isEmpty()),
"accepted channel should be connected");
check(!(sc.isConnectionPending()),
"accepted channel should not have a connection pending");
check(sc.isBlocking(),
"accepted channel should be blocking");
try { sc.connect(new TestSocketAddress()); fail(); }
catch (AlreadyConnectedException unused) { pass(); }
try { sc.bind(new TestSocketAddress()); fail(); }
catch (AlreadyConnectedException unused) { pass(); }
} catch (IOException unused) { fail(); }
}
static class TestSocketAddress extends SocketAddress {}
//--------------------- Infrastructure ---------------------------
boolean debug = true;
volatile int passed = 0, failed = 0;
void pass() {passed++;}
void fail() {failed++; Thread.dumpStack();}
void fail(String msg) {err.println(msg); fail();}
void unexpected(Throwable t) {failed++; t.printStackTrace();}
void check(boolean cond) {if (cond) pass(); else fail();}
void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
void debug(String message) {if(debug) { out.println(message); } }
void sleep(long millis) { try { Thread.currentThread().sleep(millis); }
catch(InterruptedException ie) { unexpected(ie); }}
void join(Thread thread, long millis) { try { thread.join(millis); }
catch(InterruptedException ie) { unexpected(ie); }}
public static void main(String[] args) throws Throwable {
Class<?> k = new Object(){}.getClass().getEnclosingClass();
try {k.getMethod("instanceMain",String[].class)
.invoke( k.newInstance(), (Object) args);}
catch (Throwable e) {throw e.getCause();}}
public void instanceMain(String[] args) throws Throwable {
try {test(args);} catch (Throwable t) {unexpected(t);}
out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View File

@ -0,0 +1,230 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
/* @test
* @bug 4927640
* @summary Tests the SCTP protocol implementation
* @author chegar
*/
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.io.IOException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.nio.channels.AlreadyConnectedException;
import java.nio.channels.Selector;
import java.nio.channels.SelectionKey;
import com.sun.nio.sctp.SctpChannel;
import com.sun.nio.sctp.SctpServerChannel;
import static java.lang.System.out;
import static java.lang.System.err;
public class NonBlockingAccept {
static CountDownLatch acceptLatch = new CountDownLatch(1);
static final int SEL_TIMEOUT = 10000;
static final int NUM_TEST_CONNECTIONS = 10;
void test(String[] args) {
SocketAddress address = null;
NonblockingServer server;
if (!Util.isSCTPSupported()) {
out.println("SCTP protocol is not supported");
out.println("Test cannot be run");
return;
}
if (args.length == 2) {
/* requested to connecct to a specific address */
try {
int port = Integer.valueOf(args[1]);
address = new InetSocketAddress(args[0], port);
} catch (NumberFormatException nfe) {
err.println(nfe);
}
} else {
/* start server on local machine, default */
try {
server = new NonblockingServer();
server.start();
address = server.address();
debug("Server started and listening on " + address);
} catch (IOException ioe) {
ioe.printStackTrace();
return;
}
}
doClient(address);
}
void doClient(SocketAddress peerAddress) {
Set<SctpChannel> channels = new HashSet<SctpChannel>(NUM_TEST_CONNECTIONS);
try {
for (int i=0; i<NUM_TEST_CONNECTIONS;) {
debug("connecting " + ++i);
channels.add(SctpChannel.open(peerAddress, 0, 0));
sleep(100);
}
/* don't close the channels until they have been accepted */
acceptLatch.await();
for(SctpChannel sc: channels)
sc.close();
} catch (IOException ioe) {
unexpected(ioe);
} catch (InterruptedException ie) {
unexpected(ie);
}
}
class NonblockingServer implements Runnable
{
final InetSocketAddress serverAddr;
private SctpServerChannel ssc;
private Thread serverThread;
public NonblockingServer() throws IOException {
ssc = SctpServerChannel.open().bind(null);
java.util.Set<SocketAddress> addrs = ssc.getAllLocalAddresses();
if (addrs.isEmpty())
debug("addrs should not be empty");
serverAddr = (InetSocketAddress) addrs.iterator().next();
}
void start() {
serverThread = new Thread(this, "NonblockingServer-" +
serverAddr.getPort());
serverThread.start();
}
InetSocketAddress address () {
return serverAddr;
}
@Override
public void run() {
Selector acceptSelector = null;
SelectionKey acceptKey = null;
try {
acceptSelector = Selector.open();
ssc.configureBlocking(false);
check(ssc.isBlocking() == false, "Should be in non-blocking mode");
acceptKey = ssc.register(acceptSelector, SelectionKey.OP_ACCEPT);
int connectionsAccepted = 0;
while (connectionsAccepted < NUM_TEST_CONNECTIONS) {
int keysAdded = acceptSelector.select(SEL_TIMEOUT);
if (keysAdded > 0) {
Set<SelectionKey> keys = acceptSelector.selectedKeys();
Iterator<SelectionKey> i = keys.iterator();
while(i.hasNext()) {
SelectionKey sk = i.next();
i.remove();
SctpServerChannel nextReady =
(SctpServerChannel)sk.channel();
check(nextReady.equals(ssc),
"channels should be equal");
check(sk.isAcceptable(),
"key should be acceptable");
check(!sk.isReadable(),
"key should not be readable");
check(!sk.isWritable(),
"key should not be writable");
check(!sk.isConnectable(),
"key should not be connectable");
SctpChannel acceptsc = nextReady.accept();
connectionsAccepted++;
debug("Accepted " + connectionsAccepted + " connections");
check(acceptsc != null,
"Accepted channel should not be null");
if (acceptsc != null) {
checkAcceptedChannel(acceptsc);
acceptsc.close();
}
} /* while */
} /* if */
} /* while */
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
acceptLatch.countDown();
if (acceptKey != null) acceptKey.cancel();
try { if (acceptSelector != null) acceptSelector.close(); }
catch (IOException ioe) { unexpected(ioe); }
try { if (ssc != null) ssc.close(); }
catch (IOException ioe) { unexpected(ioe); }
}
}
}
void checkAcceptedChannel(SctpChannel sc) {
try {
debug("Checking accepted SctpChannel");
check(sc.association() != null,
"accepted channel should have an association");
check(!(sc.getRemoteAddresses().isEmpty()),
"accepted channel should be connected");
check(!(sc.isConnectionPending()),
"accepted channel should not have a connection pending");
check(sc.isBlocking(),
"accepted channel should be blocking");
try { sc.connect(new TestSocketAddress()); fail(); }
catch (AlreadyConnectedException unused) { pass(); }
try { sc.bind(new TestSocketAddress()); fail(); }
catch (AlreadyConnectedException unused) { pass(); }
} catch (IOException unused) { fail(); }
}
static class TestSocketAddress extends SocketAddress {}
//--------------------- Infrastructure ---------------------------
boolean debug = true;
volatile int passed = 0, failed = 0;
void pass() {passed++;}
void fail() {failed++; Thread.dumpStack();}
void fail(String msg) {err.println(msg); fail();}
void unexpected(Throwable t) {failed++; t.printStackTrace();}
void check(boolean cond) {if (cond) pass(); else fail();}
void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
void debug(String message) {if(debug) { out.println(message); } }
void sleep(long millis) { try { Thread.currentThread().sleep(millis); }
catch(InterruptedException ie) { unexpected(ie); }}
public static void main(String[] args) throws Throwable {
Class<?> k = new Object(){}.getClass().getEnclosingClass();
try {k.getMethod("instanceMain",String[].class)
.invoke( k.newInstance(), (Object) args);}
catch (Throwable e) {throw e.getCause();}}
public void instanceMain(String[] args) throws Throwable {
try {test(args);} catch (Throwable t) {unexpected(t);}
out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View File

@ -0,0 +1,138 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
import java.net.NetworkInterface;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.InetAddress;
import java.net.Inet4Address;
import java.net.SocketException;
import java.io.IOException;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.nio.ByteBuffer;
import com.sun.nio.sctp.SctpChannel;
import static java.lang.System.out;
public class Util {
static final int SMALL_BUFFER = 128;
static final String SMALL_MESSAGE =
"Under the bridge and over the dam, looking for berries, berries for jam";
static final int LARGE_BUFFER = 32768;
static final String LARGE_MESSAGE;
static {
StringBuffer sb = new StringBuffer(LARGE_BUFFER);
for (int i=0; i<460; i++)
sb.append(SMALL_MESSAGE);
LARGE_MESSAGE = sb.toString();
}
static boolean isSCTPSupported() {
try {
SctpChannel c = SctpChannel.open();
c.close();
return true;
} catch (IOException ioe) {
ioe.printStackTrace();
} catch (UnsupportedOperationException e) {
out.println(e);
}
return false;
}
/**
* Returns a list of all the addresses on the system.
* @param inclLoopback
* if {@code true}, include the loopback addresses
* @param ipv4Only
* it {@code true}, only IPv4 addresses will be included
*/
static List<InetAddress> getAddresses(boolean inclLoopback,
boolean ipv4Only)
throws SocketException {
ArrayList<InetAddress> list = new ArrayList<InetAddress>();
Enumeration<NetworkInterface> nets =
NetworkInterface.getNetworkInterfaces();
for (NetworkInterface netInf : Collections.list(nets)) {
Enumeration<InetAddress> addrs = netInf.getInetAddresses();
for (InetAddress addr : Collections.list(addrs)) {
if (!list.contains(addr) &&
(inclLoopback ? true : !addr.isLoopbackAddress()) &&
(ipv4Only ? (addr instanceof Inet4Address) : true)) {
list.add(addr);
}
}
}
return list;
}
static void dumpAddresses(SctpChannel channel,
PrintStream printStream)
throws IOException {
Set<SocketAddress> addrs = channel.getAllLocalAddresses();
printStream.println("Local Addresses: ");
for (Iterator<SocketAddress> it = addrs.iterator(); it.hasNext(); ) {
InetSocketAddress addr = (InetSocketAddress)it.next();
printStream.println("\t" + addr);
}
}
/**
* Compare the contents of the given ByteBuffer with the contens of the
* given byte array. true if, and only if, the contents are the same.
*/
static boolean compare(ByteBuffer bb, byte[] message) {
if (message.length != bb.remaining()) {
out.println("Compare failed, byte array length != to buffer remaining");
return false;
}
for (int i=0; i<message.length; i++) {
byte b = bb.get();
if (message[i] != b) {
out.println("Position " + i + ": " + message[i] + " != " + b);
return false;
}
}
return true;
}
static boolean compare(ByteBuffer bb, String str) {
try{
return Util.compare(bb, str.getBytes("ISO-8859-1"));
} catch (UnsupportedEncodingException unsupported) {
throw new AssertionError(unsupported);
}
}
}