PageRenderTime 95ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/transport-sctp/src/main/java/io/netty/channel/sctp/nio/NioSctpChannel.java

https://gitlab.com/taichu/netty
Java | 403 lines | 321 code | 45 blank | 37 comment | 31 complexity | 06a4abc1b410a926f0e799dd25683cea MD5 | raw file
  1. /*
  2. * Copyright 2011 The Netty Project
  3. *
  4. * The Netty Project licenses this file to you under the Apache License,
  5. * version 2.0 (the "License"); you may not use this file except in compliance
  6. * with the License. You may obtain a copy of the License at:
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations
  14. * under the License.
  15. */
  16. package io.netty.channel.sctp.nio;
  17. import com.sun.nio.sctp.Association;
  18. import com.sun.nio.sctp.MessageInfo;
  19. import com.sun.nio.sctp.NotificationHandler;
  20. import com.sun.nio.sctp.SctpChannel;
  21. import io.netty.buffer.ByteBuf;
  22. import io.netty.buffer.ByteBufAllocator;
  23. import io.netty.channel.Channel;
  24. import io.netty.channel.ChannelException;
  25. import io.netty.channel.ChannelFuture;
  26. import io.netty.channel.ChannelMetadata;
  27. import io.netty.channel.ChannelOutboundBuffer;
  28. import io.netty.channel.ChannelPromise;
  29. import io.netty.channel.RecvByteBufAllocator;
  30. import io.netty.channel.nio.AbstractNioMessageChannel;
  31. import io.netty.channel.sctp.DefaultSctpChannelConfig;
  32. import io.netty.channel.sctp.SctpChannelConfig;
  33. import io.netty.channel.sctp.SctpMessage;
  34. import io.netty.channel.sctp.SctpNotificationHandler;
  35. import io.netty.channel.sctp.SctpServerChannel;
  36. import io.netty.util.internal.PlatformDependent;
  37. import io.netty.util.internal.StringUtil;
  38. import io.netty.util.internal.logging.InternalLogger;
  39. import io.netty.util.internal.logging.InternalLoggerFactory;
  40. import java.io.IOException;
  41. import java.net.InetAddress;
  42. import java.net.InetSocketAddress;
  43. import java.net.SocketAddress;
  44. import java.nio.ByteBuffer;
  45. import java.nio.channels.SelectionKey;
  46. import java.util.Collections;
  47. import java.util.HashSet;
  48. import java.util.Iterator;
  49. import java.util.LinkedHashSet;
  50. import java.util.List;
  51. import java.util.Set;
  52. /**
  53. * {@link io.netty.channel.sctp.SctpChannel} implementation which use non-blocking mode and allows to read /
  54. * write {@link SctpMessage}s to the underlying {@link SctpChannel}.
  55. *
  56. * Be aware that not all operations systems support SCTP. Please refer to the documentation of your operation system,
  57. * to understand what you need to do to use it. Also this feature is only supported on Java 7+.
  58. */
  59. public class NioSctpChannel extends AbstractNioMessageChannel implements io.netty.channel.sctp.SctpChannel {
  60. private static final ChannelMetadata METADATA = new ChannelMetadata(false);
  61. private static final InternalLogger logger = InternalLoggerFactory.getInstance(NioSctpChannel.class);
  62. private final SctpChannelConfig config;
  63. private final NotificationHandler<?> notificationHandler;
  64. private static SctpChannel newSctpChannel() {
  65. try {
  66. return SctpChannel.open();
  67. } catch (IOException e) {
  68. throw new ChannelException("Failed to open a sctp channel.", e);
  69. }
  70. }
  71. /**
  72. * Create a new instance
  73. */
  74. public NioSctpChannel() {
  75. this(newSctpChannel());
  76. }
  77. /**
  78. * Create a new instance using {@link SctpChannel}
  79. */
  80. public NioSctpChannel(SctpChannel sctpChannel) {
  81. this(null, sctpChannel);
  82. }
  83. /**
  84. * Create a new instance
  85. *
  86. * @param parent the {@link Channel} which is the parent of this {@link NioSctpChannel}
  87. * or {@code null}.
  88. * @param sctpChannel the underlying {@link SctpChannel}
  89. */
  90. public NioSctpChannel(Channel parent, SctpChannel sctpChannel) {
  91. super(parent, sctpChannel, SelectionKey.OP_READ);
  92. try {
  93. sctpChannel.configureBlocking(false);
  94. config = new NioSctpChannelConfig(this, sctpChannel);
  95. notificationHandler = new SctpNotificationHandler(this);
  96. } catch (IOException e) {
  97. try {
  98. sctpChannel.close();
  99. } catch (IOException e2) {
  100. if (logger.isWarnEnabled()) {
  101. logger.warn(
  102. "Failed to close a partially initialized sctp channel.", e2);
  103. }
  104. }
  105. throw new ChannelException("Failed to enter non-blocking mode.", e);
  106. }
  107. }
  108. @Override
  109. public InetSocketAddress localAddress() {
  110. return (InetSocketAddress) super.localAddress();
  111. }
  112. @Override
  113. public InetSocketAddress remoteAddress() {
  114. return (InetSocketAddress) super.remoteAddress();
  115. }
  116. @Override
  117. public SctpServerChannel parent() {
  118. return (SctpServerChannel) super.parent();
  119. }
  120. @Override
  121. public ChannelMetadata metadata() {
  122. return METADATA;
  123. }
  124. @Override
  125. public Association association() {
  126. try {
  127. return javaChannel().association();
  128. } catch (IOException ignored) {
  129. return null;
  130. }
  131. }
  132. @Override
  133. public Set<InetSocketAddress> allLocalAddresses() {
  134. try {
  135. final Set<SocketAddress> allLocalAddresses = javaChannel().getAllLocalAddresses();
  136. final Set<InetSocketAddress> addresses = new LinkedHashSet<InetSocketAddress>(allLocalAddresses.size());
  137. for (SocketAddress socketAddress : allLocalAddresses) {
  138. addresses.add((InetSocketAddress) socketAddress);
  139. }
  140. return addresses;
  141. } catch (Throwable ignored) {
  142. return Collections.emptySet();
  143. }
  144. }
  145. @Override
  146. public SctpChannelConfig config() {
  147. return config;
  148. }
  149. @Override
  150. public Set<InetSocketAddress> allRemoteAddresses() {
  151. try {
  152. final Set<SocketAddress> allLocalAddresses = javaChannel().getRemoteAddresses();
  153. final Set<InetSocketAddress> addresses = new HashSet<InetSocketAddress>(allLocalAddresses.size());
  154. for (SocketAddress socketAddress : allLocalAddresses) {
  155. addresses.add((InetSocketAddress) socketAddress);
  156. }
  157. return addresses;
  158. } catch (Throwable ignored) {
  159. return Collections.emptySet();
  160. }
  161. }
  162. @Override
  163. protected SctpChannel javaChannel() {
  164. return (SctpChannel) super.javaChannel();
  165. }
  166. @Override
  167. public boolean isActive() {
  168. SctpChannel ch = javaChannel();
  169. return ch.isOpen() && association() != null;
  170. }
  171. @Override
  172. protected SocketAddress localAddress0() {
  173. try {
  174. Iterator<SocketAddress> i = javaChannel().getAllLocalAddresses().iterator();
  175. if (i.hasNext()) {
  176. return i.next();
  177. }
  178. } catch (IOException e) {
  179. // ignore
  180. }
  181. return null;
  182. }
  183. @Override
  184. protected SocketAddress remoteAddress0() {
  185. try {
  186. Iterator<SocketAddress> i = javaChannel().getRemoteAddresses().iterator();
  187. if (i.hasNext()) {
  188. return i.next();
  189. }
  190. } catch (IOException e) {
  191. // ignore
  192. }
  193. return null;
  194. }
  195. @Override
  196. protected void doBind(SocketAddress localAddress) throws Exception {
  197. javaChannel().bind(localAddress);
  198. }
  199. @Override
  200. protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
  201. if (localAddress != null) {
  202. javaChannel().bind(localAddress);
  203. }
  204. boolean success = false;
  205. try {
  206. boolean connected = javaChannel().connect(remoteAddress);
  207. if (!connected) {
  208. selectionKey().interestOps(SelectionKey.OP_CONNECT);
  209. }
  210. success = true;
  211. return connected;
  212. } finally {
  213. if (!success) {
  214. doClose();
  215. }
  216. }
  217. }
  218. @Override
  219. protected void doFinishConnect() throws Exception {
  220. if (!javaChannel().finishConnect()) {
  221. throw new Error();
  222. }
  223. }
  224. @Override
  225. protected void doDisconnect() throws Exception {
  226. doClose();
  227. }
  228. @Override
  229. protected void doClose() throws Exception {
  230. javaChannel().close();
  231. }
  232. @Override
  233. protected int doReadMessages(List<Object> buf) throws Exception {
  234. SctpChannel ch = javaChannel();
  235. RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
  236. ByteBuf buffer = allocHandle.allocate(config().getAllocator());
  237. boolean free = true;
  238. try {
  239. ByteBuffer data = buffer.internalNioBuffer(buffer.writerIndex(), buffer.writableBytes());
  240. int pos = data.position();
  241. MessageInfo messageInfo = ch.receive(data, null, notificationHandler);
  242. if (messageInfo == null) {
  243. return 0;
  244. }
  245. allocHandle.lastBytesRead(data.position() - pos);
  246. buf.add(new SctpMessage(messageInfo,
  247. buffer.writerIndex(buffer.writerIndex() + allocHandle.lastBytesRead())));
  248. free = false;
  249. return 1;
  250. } catch (Throwable cause) {
  251. PlatformDependent.throwException(cause);
  252. return -1;
  253. } finally {
  254. if (free) {
  255. buffer.release();
  256. }
  257. }
  258. }
  259. @Override
  260. protected boolean doWriteMessage(Object msg, ChannelOutboundBuffer in) throws Exception {
  261. SctpMessage packet = (SctpMessage) msg;
  262. ByteBuf data = packet.content();
  263. int dataLen = data.readableBytes();
  264. if (dataLen == 0) {
  265. return true;
  266. }
  267. ByteBufAllocator alloc = alloc();
  268. boolean needsCopy = data.nioBufferCount() != 1;
  269. if (!needsCopy) {
  270. if (!data.isDirect() && alloc.isDirectBufferPooled()) {
  271. needsCopy = true;
  272. }
  273. }
  274. ByteBuffer nioData;
  275. if (!needsCopy) {
  276. nioData = data.nioBuffer();
  277. } else {
  278. data = alloc.directBuffer(dataLen).writeBytes(data);
  279. nioData = data.nioBuffer();
  280. }
  281. final MessageInfo mi = MessageInfo.createOutgoing(association(), null, packet.streamIdentifier());
  282. mi.payloadProtocolID(packet.protocolIdentifier());
  283. mi.streamNumber(packet.streamIdentifier());
  284. mi.unordered(packet.isUnordered());
  285. final int writtenBytes = javaChannel().send(nioData, mi);
  286. return writtenBytes > 0;
  287. }
  288. @Override
  289. protected final Object filterOutboundMessage(Object msg) throws Exception {
  290. if (msg instanceof SctpMessage) {
  291. SctpMessage m = (SctpMessage) msg;
  292. ByteBuf buf = m.content();
  293. if (buf.isDirect() && buf.nioBufferCount() == 1) {
  294. return m;
  295. }
  296. return new SctpMessage(m.protocolIdentifier(), m.streamIdentifier(), m.isUnordered(),
  297. newDirectBuffer(m, buf));
  298. }
  299. throw new UnsupportedOperationException(
  300. "unsupported message type: " + StringUtil.simpleClassName(msg) +
  301. " (expected: " + StringUtil.simpleClassName(SctpMessage.class));
  302. }
  303. @Override
  304. public ChannelFuture bindAddress(InetAddress localAddress) {
  305. return bindAddress(localAddress, newPromise());
  306. }
  307. @Override
  308. public ChannelFuture bindAddress(final InetAddress localAddress, final ChannelPromise promise) {
  309. if (eventLoop().inEventLoop()) {
  310. try {
  311. javaChannel().bindAddress(localAddress);
  312. promise.setSuccess();
  313. } catch (Throwable t) {
  314. promise.setFailure(t);
  315. }
  316. } else {
  317. eventLoop().execute(new Runnable() {
  318. @Override
  319. public void run() {
  320. bindAddress(localAddress, promise);
  321. }
  322. });
  323. }
  324. return promise;
  325. }
  326. @Override
  327. public ChannelFuture unbindAddress(InetAddress localAddress) {
  328. return unbindAddress(localAddress, newPromise());
  329. }
  330. @Override
  331. public ChannelFuture unbindAddress(final InetAddress localAddress, final ChannelPromise promise) {
  332. if (eventLoop().inEventLoop()) {
  333. try {
  334. javaChannel().unbindAddress(localAddress);
  335. promise.setSuccess();
  336. } catch (Throwable t) {
  337. promise.setFailure(t);
  338. }
  339. } else {
  340. eventLoop().execute(new Runnable() {
  341. @Override
  342. public void run() {
  343. unbindAddress(localAddress, promise);
  344. }
  345. });
  346. }
  347. return promise;
  348. }
  349. private final class NioSctpChannelConfig extends DefaultSctpChannelConfig {
  350. private NioSctpChannelConfig(NioSctpChannel channel, SctpChannel javaChannel) {
  351. super(channel, javaChannel);
  352. }
  353. @Override
  354. protected void autoReadCleared() {
  355. clearReadPending();
  356. }
  357. }
  358. }