Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/solaris/classes/sun/nio/ch/sctp/SctpMultiChannelImpl.java
32300 views
/*1* Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24package sun.nio.ch.sctp;2526import java.net.InetAddress;27import java.net.SocketAddress;28import java.net.SocketException;29import java.net.InetSocketAddress;30import java.io.FileDescriptor;31import java.io.IOException;32import java.util.Collections;33import java.util.Map.Entry;34import java.util.Iterator;35import java.util.Set;36import java.util.HashSet;37import java.util.HashMap;38import java.nio.ByteBuffer;39import java.nio.channels.SelectionKey;40import java.nio.channels.ClosedChannelException;41import java.nio.channels.NotYetBoundException;42import java.nio.channels.spi.SelectorProvider;43import com.sun.nio.sctp.AbstractNotificationHandler;44import com.sun.nio.sctp.Association;45import com.sun.nio.sctp.AssociationChangeNotification;46import com.sun.nio.sctp.HandlerResult;47import com.sun.nio.sctp.IllegalReceiveException;48import com.sun.nio.sctp.InvalidStreamException;49import com.sun.nio.sctp.IllegalUnbindException;50import com.sun.nio.sctp.NotificationHandler;51import com.sun.nio.sctp.MessageInfo;52import com.sun.nio.sctp.SctpChannel;53import com.sun.nio.sctp.SctpMultiChannel;54import com.sun.nio.sctp.SctpSocketOption;55import sun.nio.ch.DirectBuffer;56import sun.nio.ch.NativeThread;57import sun.nio.ch.IOStatus;58import sun.nio.ch.IOUtil;59import sun.nio.ch.Net;60import sun.nio.ch.PollArrayWrapper;61import sun.nio.ch.SelChImpl;62import sun.nio.ch.SelectionKeyImpl;63import sun.nio.ch.Util;64import static com.sun.nio.sctp.SctpStandardSocketOptions.*;65import static sun.nio.ch.sctp.ResultContainer.*;6667/**68* An implementation of SctpMultiChannel69*/70public class SctpMultiChannelImpl extends SctpMultiChannel71implements SelChImpl72{73private final FileDescriptor fd;7475private final int fdVal;7677/* IDs of native threads doing send and receives, for signalling */78private volatile long receiverThread = 0;79private volatile long senderThread = 0;8081/* Lock held by current receiving thread */82private final Object receiveLock = new Object();8384/* Lock held by current sending thread */85private final Object sendLock = new Object();8687/* Lock held by any thread that modifies the state fields declared below88* DO NOT invoke a blocking I/O operation while holding this lock! */89private final Object stateLock = new Object();9091private enum ChannelState {92UNINITIALIZED,93KILLPENDING,94KILLED,95}9697/* -- The following fields are protected by stateLock -- */98private ChannelState state = ChannelState.UNINITIALIZED;99100/* Binding: Once bound the port will remain constant. */101int port = -1;102private HashSet<InetSocketAddress> localAddresses = new HashSet<InetSocketAddress>();103/* Has the channel been bound to the wildcard address */104private boolean wildcard; /* false */105106/* Keeps a map of addresses to association, and visa versa */107private HashMap<SocketAddress, Association> addressMap =108new HashMap<SocketAddress, Association>();109private HashMap<Association, Set<SocketAddress>> associationMap =110new HashMap<Association, Set<SocketAddress>>();111112/* -- End of fields protected by stateLock -- */113114/* If an association has been shutdown mark it for removal after115* the user handler has been invoked */116private final ThreadLocal<Association> associationToRemove =117new ThreadLocal<Association>() {118@Override protected Association initialValue() {119return null;120}121};122123/* A notification handler cannot invoke receive */124private final ThreadLocal<Boolean> receiveInvoked =125new ThreadLocal<Boolean>() {126@Override protected Boolean initialValue() {127return Boolean.FALSE;128}129};130131public SctpMultiChannelImpl(SelectorProvider provider)132throws IOException {133//TODO: update provider, remove public modifier134super(provider);135this.fd = SctpNet.socket(false /*one-to-many*/);136this.fdVal = IOUtil.fdVal(fd);137}138139@Override140public SctpMultiChannel bind(SocketAddress local, int backlog)141throws IOException {142synchronized (receiveLock) {143synchronized (sendLock) {144synchronized (stateLock) {145ensureOpen();146if (isBound())147SctpNet.throwAlreadyBoundException();148InetSocketAddress isa = (local == null) ?149new InetSocketAddress(0) : Net.checkAddress(local);150151SecurityManager sm = System.getSecurityManager();152if (sm != null)153sm.checkListen(isa.getPort());154Net.bind(fd, isa.getAddress(), isa.getPort());155156InetSocketAddress boundIsa = Net.localAddress(fd);157port = boundIsa.getPort();158localAddresses.add(isa);159if (isa.getAddress().isAnyLocalAddress())160wildcard = true;161162SctpNet.listen(fdVal, backlog < 1 ? 50 : backlog);163}164}165}166return this;167}168169@Override170public SctpMultiChannel bindAddress(InetAddress address)171throws IOException {172return bindUnbindAddress(address, true);173}174175@Override176public SctpMultiChannel unbindAddress(InetAddress address)177throws IOException {178return bindUnbindAddress(address, false);179}180181private SctpMultiChannel bindUnbindAddress(InetAddress address,182boolean add)183throws IOException {184if (address == null)185throw new IllegalArgumentException();186187synchronized (receiveLock) {188synchronized (sendLock) {189synchronized (stateLock) {190if (!isOpen())191throw new ClosedChannelException();192if (!isBound())193throw new NotYetBoundException();194if (wildcard)195throw new IllegalStateException(196"Cannot add or remove addresses from a channel that is bound to the wildcard address");197if (address.isAnyLocalAddress())198throw new IllegalArgumentException(199"Cannot add or remove the wildcard address");200if (add) {201for (InetSocketAddress addr : localAddresses) {202if (addr.getAddress().equals(address)) {203SctpNet.throwAlreadyBoundException();204}205}206} else { /*removing */207/* Verify that there is more than one address208* and that address is already bound */209if (localAddresses.size() <= 1)210throw new IllegalUnbindException("Cannot remove address from a channel with only one address bound");211boolean foundAddress = false;212for (InetSocketAddress addr : localAddresses) {213if (addr.getAddress().equals(address)) {214foundAddress = true;215break;216}217}218if (!foundAddress )219throw new IllegalUnbindException("Cannot remove address from a channel that is not bound to that address");220}221222SctpNet.bindx(fdVal, new InetAddress[]{address}, port, add);223224/* Update our internal Set to reflect the addition/removal */225if (add)226localAddresses.add(new InetSocketAddress(address, port));227else {228for (InetSocketAddress addr : localAddresses) {229if (addr.getAddress().equals(address)) {230localAddresses.remove(addr);231break;232}233}234}235}236}237}238return this;239}240241@Override242public Set<Association> associations()243throws ClosedChannelException, NotYetBoundException {244synchronized (stateLock) {245if (!isOpen())246throw new ClosedChannelException();247if (!isBound())248throw new NotYetBoundException();249250return Collections.unmodifiableSet(associationMap.keySet());251}252}253254private boolean isBound() {255synchronized (stateLock) {256return port == -1 ? false : true;257}258}259260private void ensureOpen() throws IOException {261synchronized (stateLock) {262if (!isOpen())263throw new ClosedChannelException();264}265}266267private void receiverCleanup() throws IOException {268synchronized (stateLock) {269receiverThread = 0;270if (state == ChannelState.KILLPENDING)271kill();272}273}274275private void senderCleanup() throws IOException {276synchronized (stateLock) {277senderThread = 0;278if (state == ChannelState.KILLPENDING)279kill();280}281}282283@Override284protected void implConfigureBlocking(boolean block) throws IOException {285IOUtil.configureBlocking(fd, block);286}287288@Override289public void implCloseSelectableChannel() throws IOException {290synchronized (stateLock) {291SctpNet.preClose(fdVal);292293if (receiverThread != 0)294NativeThread.signal(receiverThread);295296if (senderThread != 0)297NativeThread.signal(senderThread);298299if (!isRegistered())300kill();301}302}303304@Override305public FileDescriptor getFD() {306return fd;307}308309@Override310public int getFDVal() {311return fdVal;312}313314/**315* Translates native poll revent ops into a ready operation ops316*/317private boolean translateReadyOps(int ops, int initialOps,318SelectionKeyImpl sk) {319int intOps = sk.nioInterestOps();320int oldOps = sk.nioReadyOps();321int newOps = initialOps;322323if ((ops & Net.POLLNVAL) != 0) {324/* This should only happen if this channel is pre-closed while a325* selection operation is in progress326* ## Throw an error if this channel has not been pre-closed */327return false;328}329330if ((ops & (Net.POLLERR | Net.POLLHUP)) != 0) {331newOps = intOps;332sk.nioReadyOps(newOps);333return (newOps & ~oldOps) != 0;334}335336if (((ops & Net.POLLIN) != 0) &&337((intOps & SelectionKey.OP_READ) != 0))338newOps |= SelectionKey.OP_READ;339340if (((ops & Net.POLLOUT) != 0) &&341((intOps & SelectionKey.OP_WRITE) != 0))342newOps |= SelectionKey.OP_WRITE;343344sk.nioReadyOps(newOps);345return (newOps & ~oldOps) != 0;346}347348@Override349public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) {350return translateReadyOps(ops, sk.nioReadyOps(), sk);351}352353@Override354public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) {355return translateReadyOps(ops, 0, sk);356}357358@Override359public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) {360int newOps = 0;361if ((ops & SelectionKey.OP_READ) != 0)362newOps |= Net.POLLIN;363if ((ops & SelectionKey.OP_WRITE) != 0)364newOps |= Net.POLLOUT;365sk.selector.putEventOps(sk, newOps);366}367368@Override369public void kill() throws IOException {370synchronized (stateLock) {371if (state == ChannelState.KILLED)372return;373if (state == ChannelState.UNINITIALIZED) {374state = ChannelState.KILLED;375return;376}377assert !isOpen() && !isRegistered();378379/* Postpone the kill if there is a thread sending or receiving. */380if (receiverThread == 0 && senderThread == 0) {381SctpNet.close(fdVal);382state = ChannelState.KILLED;383} else {384state = ChannelState.KILLPENDING;385}386}387}388389@Override390public <T> SctpMultiChannel setOption(SctpSocketOption<T> name,391T value,392Association association)393throws IOException {394if (name == null)395throw new NullPointerException();396if (!(supportedOptions().contains(name)))397throw new UnsupportedOperationException("'" + name + "' not supported");398399synchronized (stateLock) {400if (association != null && (name.equals(SCTP_PRIMARY_ADDR) ||401name.equals(SCTP_SET_PEER_PRIMARY_ADDR))) {402checkAssociation(association);403}404if (!isOpen())405throw new ClosedChannelException();406407int assocId = association == null ? 0 : association.associationID();408SctpNet.setSocketOption(fdVal, name, value, assocId);409}410return this;411}412413@Override414@SuppressWarnings("unchecked")415public <T> T getOption(SctpSocketOption<T> name, Association association)416throws IOException {417if (name == null)418throw new NullPointerException();419if (!supportedOptions().contains(name))420throw new UnsupportedOperationException("'" + name + "' not supported");421422synchronized (stateLock) {423if (association != null && (name.equals(SCTP_PRIMARY_ADDR) ||424name.equals(SCTP_SET_PEER_PRIMARY_ADDR))) {425checkAssociation(association);426}427if (!isOpen())428throw new ClosedChannelException();429430int assocId = association == null ? 0 : association.associationID();431return (T)SctpNet.getSocketOption(fdVal, name, assocId);432}433}434435private static class DefaultOptionsHolder {436static final Set<SctpSocketOption<?>> defaultOptions = defaultOptions();437438private static Set<SctpSocketOption<?>> defaultOptions() {439HashSet<SctpSocketOption<?>> set = new HashSet<SctpSocketOption<?>>(10);440set.add(SCTP_DISABLE_FRAGMENTS);441set.add(SCTP_EXPLICIT_COMPLETE);442set.add(SCTP_FRAGMENT_INTERLEAVE);443set.add(SCTP_INIT_MAXSTREAMS);444set.add(SCTP_NODELAY);445set.add(SCTP_PRIMARY_ADDR);446set.add(SCTP_SET_PEER_PRIMARY_ADDR);447set.add(SO_SNDBUF);448set.add(SO_RCVBUF);449set.add(SO_LINGER);450return Collections.unmodifiableSet(set);451}452}453454@Override455public final Set<SctpSocketOption<?>> supportedOptions() {456return DefaultOptionsHolder.defaultOptions;457}458459@Override460public <T> MessageInfo receive(ByteBuffer buffer,461T attachment,462NotificationHandler<T> handler)463throws IOException {464if (buffer == null)465throw new IllegalArgumentException("buffer cannot be null");466467if (buffer.isReadOnly())468throw new IllegalArgumentException("Read-only buffer");469470if (receiveInvoked.get())471throw new IllegalReceiveException(472"cannot invoke receive from handler");473receiveInvoked.set(Boolean.TRUE);474475try {476ResultContainer resultContainer = new ResultContainer();477do {478resultContainer.clear();479synchronized (receiveLock) {480ensureOpen();481if (!isBound())482throw new NotYetBoundException();483484int n = 0;485try {486begin();487488synchronized (stateLock) {489if(!isOpen())490return null;491receiverThread = NativeThread.current();492}493494do {495n = receive(fdVal, buffer, resultContainer);496} while ((n == IOStatus.INTERRUPTED) && isOpen());497498} finally {499receiverCleanup();500end((n > 0) || (n == IOStatus.UNAVAILABLE));501assert IOStatus.check(n);502}503504if (!resultContainer.isNotification()) {505/* message or nothing */506if (resultContainer.hasSomething()) {507/* Set the association before returning */508MessageInfoImpl info =509resultContainer.getMessageInfo();510info.setAssociation(lookupAssociation(info.511associationID()));512SecurityManager sm = System.getSecurityManager();513if (sm != null) {514InetSocketAddress isa = (InetSocketAddress)info.address();515if (!addressMap.containsKey(isa)) {516/* must be a new association */517try {518sm.checkAccept(isa.getAddress().getHostAddress(),519isa.getPort());520} catch (SecurityException se) {521buffer.clear();522throw se;523}524}525}526527assert info.association() != null;528return info;529} else {530/* Non-blocking may return null if nothing available*/531return null;532}533} else { /* notification */534synchronized (stateLock) {535handleNotificationInternal(536resultContainer);537}538}539} /* receiveLock */540} while (handler == null ? true :541(invokeNotificationHandler(resultContainer, handler, attachment)542== HandlerResult.CONTINUE));543} finally {544receiveInvoked.set(Boolean.FALSE);545}546547return null;548}549550private int receive(int fd,551ByteBuffer dst,552ResultContainer resultContainer)553throws IOException {554int pos = dst.position();555int lim = dst.limit();556assert (pos <= lim);557int rem = (pos <= lim ? lim - pos : 0);558if (dst instanceof DirectBuffer && rem > 0)559return receiveIntoNativeBuffer(fd, resultContainer, dst, rem, pos);560561/* Substitute a native buffer. */562int newSize = Math.max(rem, 1);563ByteBuffer bb = Util.getTemporaryDirectBuffer(newSize);564try {565int n = receiveIntoNativeBuffer(fd, resultContainer, bb, newSize, 0);566bb.flip();567if (n > 0 && rem > 0)568dst.put(bb);569return n;570} finally {571Util.releaseTemporaryDirectBuffer(bb);572}573}574575private int receiveIntoNativeBuffer(int fd,576ResultContainer resultContainer,577ByteBuffer bb,578int rem,579int pos)580throws IOException {581int n = receive0(fd, resultContainer, ((DirectBuffer)bb).address() + pos, rem);582if (n > 0)583bb.position(pos + n);584return n;585}586587private InternalNotificationHandler internalNotificationHandler =588new InternalNotificationHandler();589590private void handleNotificationInternal(ResultContainer resultContainer)591{592invokeNotificationHandler(resultContainer,593internalNotificationHandler, null);594}595596private class InternalNotificationHandler597extends AbstractNotificationHandler<Object>598{599@Override600public HandlerResult handleNotification(601AssociationChangeNotification not, Object unused) {602AssociationChange sac = (AssociationChange) not;603604/* Update map to reflect change in association */605switch (not.event()) {606case COMM_UP :607Association newAssociation = new AssociationImpl608(sac.assocId(), sac.maxInStreams(), sac.maxOutStreams());609addAssociation(newAssociation);610break;611case SHUTDOWN :612case COMM_LOST :613//case RESTART: ???614/* mark association for removal after user handler invoked*/615associationToRemove.set(lookupAssociation(sac.assocId()));616}617return HandlerResult.CONTINUE;618}619}620621private <T> HandlerResult invokeNotificationHandler(622ResultContainer resultContainer,623NotificationHandler<T> handler,624T attachment) {625HandlerResult result;626SctpNotification notification = resultContainer.notification();627notification.setAssociation(lookupAssociation(notification.assocId()));628629if (!(handler instanceof AbstractNotificationHandler)) {630result = handler.handleNotification(notification, attachment);631} else { /* AbstractNotificationHandler */632AbstractNotificationHandler<T> absHandler =633(AbstractNotificationHandler<T>)handler;634switch(resultContainer.type()) {635case ASSOCIATION_CHANGED :636result = absHandler.handleNotification(637resultContainer.getAssociationChanged(), attachment);638break;639case PEER_ADDRESS_CHANGED :640result = absHandler.handleNotification(641resultContainer.getPeerAddressChanged(), attachment);642break;643case SEND_FAILED :644result = absHandler.handleNotification(645resultContainer.getSendFailed(), attachment);646break;647case SHUTDOWN :648result = absHandler.handleNotification(649resultContainer.getShutdown(), attachment);650break;651default :652/* implementation specific handlers */653result = absHandler.handleNotification(654resultContainer.notification(), attachment);655}656}657658if (!(handler instanceof InternalNotificationHandler)) {659/* Only remove associations after user handler660* has finished with them */661Association assoc = associationToRemove.get();662if (assoc != null) {663removeAssociation(assoc);664associationToRemove.set(null);665}666667}668669return result;670}671672private Association lookupAssociation(int assocId) {673/* Lookup the association in our internal map */674synchronized (stateLock) {675Set<Association> assocs = associationMap.keySet();676for (Association a : assocs) {677if (a.associationID() == assocId) {678return a;679}680}681}682return null;683}684685private void addAssociation(Association association) {686synchronized (stateLock) {687int assocId = association.associationID();688Set<SocketAddress> addresses = null;689690try {691addresses = SctpNet.getRemoteAddresses(fdVal, assocId);692} catch (IOException unused) {693/* OK, determining connected addresses may not be possible694* shutdown, connection lost, etc */695}696697associationMap.put(association, addresses);698if (addresses != null) {699for (SocketAddress addr : addresses)700addressMap.put(addr, association);701}702}703}704705private void removeAssociation(Association association) {706synchronized (stateLock) {707int assocId = association.associationID();708Set<SocketAddress> addresses = null;709710try {711addresses = SctpNet.getRemoteAddresses(fdVal, assocId);712} catch (IOException unused) {713/* OK, determining connected addresses may not be possible714* shutdown, connection lost, etc */715}716717Set<Association> assocs = associationMap.keySet();718for (Association a : assocs) {719if (a.associationID() == assocId) {720associationMap.remove(a);721break;722}723}724if (addresses != null) {725for (SocketAddress addr : addresses)726addressMap.remove(addr);727} else {728/* We cannot determine the connected addresses */729Set<java.util.Map.Entry<SocketAddress, Association>> addrAssocs =730addressMap.entrySet();731Iterator<Entry<SocketAddress, Association>> iterator = addrAssocs.iterator();732while (iterator.hasNext()) {733Entry<SocketAddress, Association> entry = iterator.next();734if (entry.getValue().equals(association)) {735iterator.remove();736}737}738}739}740}741742/**743* @throws IllegalArgumentException744* If the given association is not controlled by this channel745*746* @return {@code true} if, and only if, the given association is one747* of the current associations controlled by this channel748*/749private boolean checkAssociation(Association messageAssoc) {750synchronized (stateLock) {751for (Association association : associationMap.keySet()) {752if (messageAssoc.equals(association)) {753return true;754}755}756}757throw new IllegalArgumentException(758"Given Association is not controlled by this channel");759}760761private void checkStreamNumber(Association assoc, int streamNumber) {762synchronized (stateLock) {763if (streamNumber < 0 || streamNumber >= assoc.maxOutboundStreams())764throw new InvalidStreamException();765}766}767768/* TODO: Add support for ttl and isComplete to both 121 12M769* SCTP_EOR not yet supported on reference platforms770* TTL support limited...771*/772@Override773public int send(ByteBuffer buffer, MessageInfo messageInfo)774throws IOException {775if (buffer == null)776throw new IllegalArgumentException("buffer cannot be null");777778if (messageInfo == null)779throw new IllegalArgumentException("messageInfo cannot be null");780781synchronized (sendLock) {782ensureOpen();783784if (!isBound())785bind(null, 0);786787int n = 0;788try {789int assocId = -1;790SocketAddress address = null;791begin();792793synchronized (stateLock) {794if(!isOpen())795return 0;796senderThread = NativeThread.current();797798/* Determine what address or association to send to */799Association assoc = messageInfo.association();800InetSocketAddress addr = (InetSocketAddress)messageInfo.address();801if (assoc != null) {802checkAssociation(assoc);803checkStreamNumber(assoc, messageInfo.streamNumber());804assocId = assoc.associationID();805/* have we also got a preferred address */806if (addr != null) {807if (!assoc.equals(addressMap.get(addr)))808throw new IllegalArgumentException("given preferred address is not part of this association");809address = addr;810}811} else if (addr != null) {812address = addr;813Association association = addressMap.get(addr);814if (association != null) {815checkStreamNumber(association, messageInfo.streamNumber());816assocId = association.associationID();817818} else { /* must be new association */819SecurityManager sm = System.getSecurityManager();820if (sm != null)821sm.checkConnect(addr.getAddress().getHostAddress(),822addr.getPort());823}824} else {825throw new AssertionError(826"Both association and address cannot be null");827}828}829830do {831n = send(fdVal, buffer, assocId, address, messageInfo);832} while ((n == IOStatus.INTERRUPTED) && isOpen());833834return IOStatus.normalize(n);835} finally {836senderCleanup();837end((n > 0) || (n == IOStatus.UNAVAILABLE));838assert IOStatus.check(n);839}840}841}842843private int send(int fd,844ByteBuffer src,845int assocId,846SocketAddress target,847MessageInfo messageInfo)848throws IOException {849int streamNumber = messageInfo.streamNumber();850boolean unordered = messageInfo.isUnordered();851int ppid = messageInfo.payloadProtocolID();852853if (src instanceof DirectBuffer)854return sendFromNativeBuffer(fd, src, target, assocId,855streamNumber, unordered, ppid);856857/* Substitute a native buffer */858int pos = src.position();859int lim = src.limit();860assert (pos <= lim && streamNumber >= 0);861862int rem = (pos <= lim ? lim - pos : 0);863ByteBuffer bb = Util.getTemporaryDirectBuffer(rem);864try {865bb.put(src);866bb.flip();867/* Do not update src until we see how many bytes were written */868src.position(pos);869870int n = sendFromNativeBuffer(fd, bb, target, assocId,871streamNumber, unordered, ppid);872if (n > 0) {873/* now update src */874src.position(pos + n);875}876return n;877} finally {878Util.releaseTemporaryDirectBuffer(bb);879}880}881882private int sendFromNativeBuffer(int fd,883ByteBuffer bb,884SocketAddress target,885int assocId,886int streamNumber,887boolean unordered,888int ppid)889throws IOException {890InetAddress addr = null; // no preferred address891int port = 0;892if (target != null) {893InetSocketAddress isa = Net.checkAddress(target);894addr = isa.getAddress();895port = isa.getPort();896}897int pos = bb.position();898int lim = bb.limit();899assert (pos <= lim);900int rem = (pos <= lim ? lim - pos : 0);901902int written = send0(fd, ((DirectBuffer)bb).address() + pos, rem, addr,903port, assocId, streamNumber, unordered, ppid);904if (written > 0)905bb.position(pos + written);906return written;907}908909@Override910public SctpMultiChannel shutdown(Association association)911throws IOException {912synchronized (stateLock) {913checkAssociation(association);914if (!isOpen())915throw new ClosedChannelException();916917SctpNet.shutdown(fdVal, association.associationID());918}919return this;920}921922@Override923public Set<SocketAddress> getAllLocalAddresses()924throws IOException {925synchronized (stateLock) {926if (!isOpen())927throw new ClosedChannelException();928if (!isBound())929return Collections.emptySet();930931return SctpNet.getLocalAddresses(fdVal);932}933}934935@Override936public Set<SocketAddress> getRemoteAddresses(Association association)937throws IOException {938synchronized (stateLock) {939checkAssociation(association);940if (!isOpen())941throw new ClosedChannelException();942943try {944return SctpNet.getRemoteAddresses(fdVal, association.associationID());945} catch (SocketException se) {946/* a valid association should always have remote addresses */947Set<SocketAddress> addrs = associationMap.get(association);948return addrs != null ? addrs : Collections.<SocketAddress>emptySet();949}950}951}952953@Override954public SctpChannel branch(Association association)955throws IOException {956synchronized (stateLock) {957checkAssociation(association);958if (!isOpen())959throw new ClosedChannelException();960961FileDescriptor bFd = SctpNet.branch(fdVal,962association.associationID());963/* successfully branched, we can now remove it from assoc list */964removeAssociation(association);965966return new SctpChannelImpl(provider(), bFd, association);967}968}969970/* Use common native implementation shared between971* one-to-one and one-to-many */972private static int receive0(int fd,973ResultContainer resultContainer,974long address,975int length)976throws IOException{977return SctpChannelImpl.receive0(fd, resultContainer, address,978length, false /*peek */);979}980981private static int send0(int fd,982long address,983int length,984InetAddress addr,985int port,986int assocId,987int streamNumber,988boolean unordered,989int ppid)990throws IOException {991return SctpChannelImpl.send0(fd, address, length, addr, port, assocId,992streamNumber, unordered, ppid);993}994995static {996IOUtil.load(); /* loads nio & net native libraries */997java.security.AccessController.doPrivileged(998new java.security.PrivilegedAction<Void>() {999public Void run() {1000System.loadLibrary("sctp");1001return null;1002}1003});1004}1005}100610071008