View Javadoc
1   /*
2    * Copyright 2012 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    *   https://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.netty5.channel.socket.nio;
17  
18  import io.netty5.channel.Channel;
19  import io.netty5.channel.ChannelException;
20  import io.netty5.channel.ChannelMetadata;
21  import io.netty5.channel.ChannelOption;
22  import io.netty5.channel.ChannelOutboundBuffer;
23  import io.netty5.channel.ChannelShutdownDirection;
24  import io.netty5.channel.EventLoop;
25  import io.netty5.channel.EventLoopGroup;
26  import io.netty5.channel.ServerChannelRecvBufferAllocator;
27  import io.netty5.channel.nio.AbstractNioMessageChannel;
28  import io.netty5.channel.socket.SocketProtocolFamily;
29  import io.netty5.util.NetUtil;
30  import io.netty5.util.internal.SocketUtils;
31  import io.netty5.util.internal.logging.InternalLogger;
32  import io.netty5.util.internal.logging.InternalLoggerFactory;
33  
34  import java.io.IOException;
35  import java.lang.reflect.Method;
36  import java.net.ProtocolFamily;
37  import java.net.SocketAddress;
38  import java.net.SocketOption;
39  import java.nio.channels.SelectionKey;
40  import java.nio.channels.ServerSocketChannel;
41  import java.nio.channels.SocketChannel;
42  import java.nio.channels.spi.SelectorProvider;
43  import java.util.List;
44  
45  import static io.netty5.channel.ChannelOption.SO_BACKLOG;
46  import static io.netty5.channel.socket.nio.NioChannelUtil.isDomainSocket;
47  import static io.netty5.channel.socket.nio.NioChannelUtil.toDomainSocketAddress;
48  import static io.netty5.channel.socket.nio.NioChannelUtil.toJdkFamily;
49  import static io.netty5.channel.socket.nio.NioChannelUtil.toUnixDomainSocketAddress;
50  import static io.netty5.util.internal.ObjectUtil.checkPositiveOrZero;
51  
52  /**
53   * A {@link io.netty5.channel.socket.ServerSocketChannel} implementation which uses
54   * NIO selector based implementation to accept new connections.
55   *
56   * <h3>Available options</h3>
57   *
58   * In addition to the options provided by {@link io.netty5.channel.socket.SocketChannel},
59   * {@link NioSocketChannel} allows the following options in the option map:
60   *
61   * <table border="1" cellspacing="0" cellpadding="6">
62   * <tr>
63   * <th>{@link ChannelOption}</th>
64   * <th>{@code INET}</th>
65   * <th>{@code INET6}</th>
66   * <th>{@code UNIX</th>
67   * </tr><tr>
68   * <td>{@link NioChannelOption}</td><td>X</td><td>X</td><td>X</td>
69   * </tr>
70   * </table>
71   */
72  public class NioServerSocketChannel extends AbstractNioMessageChannel<Channel, SocketAddress, SocketAddress>
73                               implements io.netty5.channel.socket.ServerSocketChannel {
74  
75      private static final ChannelMetadata METADATA = new ChannelMetadata(false, 16);
76      private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
77  
78      private static final InternalLogger logger = InternalLoggerFactory.getInstance(NioServerSocketChannel.class);
79  
80      private static final Method OPEN_SERVER_SOCKET_CHANNEL_WITH_FAMILY =
81              NioChannelUtil.findOpenMethod("openServerSocketChannel");
82  
83      private static ServerSocketChannel newChannel(SelectorProvider provider, ProtocolFamily family) {
84          try {
85              ServerSocketChannel channel =
86                      NioChannelUtil.newChannel(OPEN_SERVER_SOCKET_CHANNEL_WITH_FAMILY, provider, family);
87              return channel == null ? provider.openServerSocketChannel() : channel;
88          } catch (IOException e) {
89              throw new ChannelException("Failed to open a socket.", e);
90          }
91      }
92  
93      private final ProtocolFamily family;
94  
95      private final EventLoopGroup childEventLoopGroup;
96  
97      private volatile int backlog = NetUtil.SOMAXCONN;
98  
99      private volatile boolean bound;
100 
101     /**
102      * Create a new instance
103      */
104     public NioServerSocketChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup) {
105         this(eventLoop, childEventLoopGroup, DEFAULT_SELECTOR_PROVIDER);
106     }
107 
108     /**
109      * Create a new instance using the given {@link SelectorProvider}.
110      */
111     public NioServerSocketChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup, SelectorProvider provider) {
112         this(eventLoop, childEventLoopGroup, provider, null);
113     }
114 
115     /**
116      * Create a new instance using the given {@link SelectorProvider} and protocol family (supported only since JDK 15).
117      */
118     public NioServerSocketChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup,
119                                   SelectorProvider provider, ProtocolFamily protocolFamily) {
120         this(eventLoop, childEventLoopGroup, newChannel(provider, toJdkFamily(protocolFamily)), protocolFamily);
121     }
122 
123     /**
124      * Create a new instance using the given {@link ServerSocketChannel}.
125      */
126     public NioServerSocketChannel(
127             EventLoop eventLoop, EventLoopGroup childEventLoopGroup, ServerSocketChannel channel) {
128         this(eventLoop, childEventLoopGroup, channel, null);
129     }
130 
131     /**
132      * Create a new instance using the given {@link ServerSocketChannel}.
133      */
134     public NioServerSocketChannel(
135             EventLoop eventLoop, EventLoopGroup childEventLoopGroup,
136             ServerSocketChannel channel, ProtocolFamily family) {
137         super(null, eventLoop, METADATA, new ServerChannelRecvBufferAllocator(),
138                 channel, SelectionKey.OP_ACCEPT);
139         this.family = toJdkFamily(family);
140         this.childEventLoopGroup = validateEventLoopGroup(
141                 childEventLoopGroup, "childEventLoopGroup", NioSocketChannel.class);
142     }
143 
144     @Override
145     public EventLoopGroup childEventLoopGroup() {
146         return childEventLoopGroup;
147     }
148 
149     @Override
150     public boolean isActive() {
151         // As java.nio.ServerSocketChannel.isBound() will continue to return true even after the channel was closed
152         // we will also need to check if it is open.
153         return isOpen() && bound;
154     }
155 
156     @SuppressWarnings("unchecked")
157     @Override
158     protected <T> T getExtendedOption(ChannelOption<T> option) {
159         if (option == SO_BACKLOG) {
160             return (T) Integer.valueOf(getBacklog());
161         }
162         SocketOption<T> socketOption = NioChannelOption.toSocketOption(option);
163         if (socketOption != null) {
164             return NioChannelOption.getOption(javaChannel(), socketOption);
165         }
166         return super.getExtendedOption(option);
167     }
168 
169     @Override
170     protected <T> void setExtendedOption(ChannelOption<T> option, T value) {
171         if (option == SO_BACKLOG) {
172             setBacklog((Integer) value);
173         } else {
174             SocketOption<T> socketOption = NioChannelOption.toSocketOption(option);
175             if (socketOption != null) {
176                 NioChannelOption.setOption(javaChannel(), socketOption, value);
177             } else {
178                 super.setExtendedOption(option, value);
179             }
180         }
181     }
182 
183     @Override
184     protected boolean isExtendedOptionSupported(ChannelOption<?> option) {
185         if (option == SO_BACKLOG) {
186             return true;
187         }
188         SocketOption<?> socketOption = NioChannelOption.toSocketOption(option);
189         if (socketOption != null) {
190             return NioChannelOption.isOptionSupported(javaChannel(), socketOption);
191         }
192         return super.isExtendedOptionSupported(option);
193     }
194 
195     private int getBacklog() {
196         return backlog;
197     }
198 
199     private void setBacklog(int backlog) {
200         checkPositiveOrZero(backlog, "backlog");
201         this.backlog = backlog;
202     }
203 
204     @Override
205     protected ServerSocketChannel javaChannel() {
206         return (ServerSocketChannel) super.javaChannel();
207     }
208 
209     @Override
210     protected SocketAddress localAddress0() {
211         try {
212             SocketAddress address = javaChannel().getLocalAddress();
213             if (isDomainSocket(family)) {
214                 address = toDomainSocketAddress(address);
215             }
216             return address;
217         } catch (IOException e) {
218             // Just return null
219             return null;
220         }
221     }
222 
223     @Override
224     protected void doShutdown(ChannelShutdownDirection direction) throws Exception {
225         throw new UnsupportedOperationException();
226     }
227 
228     @Override
229     public boolean isShutdown(ChannelShutdownDirection direction) {
230         return !isActive();
231     }
232 
233     @Override
234     protected void doBind(SocketAddress localAddress) throws Exception {
235         if (isDomainSocket(family)) {
236             localAddress = toUnixDomainSocketAddress(localAddress);
237         }
238         javaChannel().bind(localAddress, getBacklog());
239         bound = true;
240     }
241 
242     @Override
243     protected int doReadMessages(List<Object> buf) throws Exception {
244         SocketChannel ch = SocketUtils.accept(javaChannel());
245 
246         try {
247             if (ch != null) {
248                 buf.add(new NioSocketChannel(this, childEventLoopGroup().next(), ch, family));
249                 return 1;
250             }
251         } catch (Throwable t) {
252             logger.warn("Failed to create a new channel from an accepted socket.", t);
253 
254             try {
255                 ch.close();
256             } catch (Throwable t2) {
257                 logger.warn("Failed to close a socket.", t2);
258             }
259         }
260 
261         return 0;
262     }
263 
264     // Unnecessary stuff
265     @Override
266     protected boolean doConnect(
267             SocketAddress remoteAddress, SocketAddress localAddress) {
268         throw new UnsupportedOperationException();
269     }
270 
271     @Override
272     protected boolean doFinishConnect(SocketAddress requestedRemoteAddress) {
273         throw new UnsupportedOperationException();
274     }
275 
276     @Override
277     protected SocketAddress remoteAddress0() {
278         return null;
279     }
280 
281     @Override
282     protected void doDisconnect() {
283         throw new UnsupportedOperationException();
284     }
285 
286     @Override
287     protected boolean doWriteMessage(Object msg, ChannelOutboundBuffer in) {
288         throw new UnsupportedOperationException();
289     }
290 
291     @Override
292     protected final Object filterOutboundMessage(Object msg) {
293         throw new UnsupportedOperationException();
294     }
295 
296     @Override
297     protected void autoReadCleared() {
298         clearReadPending();
299     }
300 
301     // Override just to to be able to call directly via unit tests.
302     @Override
303     protected boolean closeOnReadError(Throwable cause) {
304         return super.closeOnReadError(cause);
305     }
306 }