Added a more general ability for datagram sessions -- it's based on the

bootstrap implementation instead of a custom "Server" channel.
This commit is contained in:
nathan 2018-02-22 00:48:10 +01:00
parent b0799c6846
commit d021340878
10 changed files with 711 additions and 807 deletions

View File

@ -29,13 +29,28 @@ import dorkbox.util.OS;
import dorkbox.util.Property;
import dorkbox.util.exceptions.SecurityException;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.bootstrap.SessionBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.DefaultEventLoopGroup;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.FixedRecvByteBufAllocator;
import io.netty.channel.WriteBufferWaterMark;
import io.netty.channel.epoll.EpollDatagramChannel;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.kqueue.KQueueDatagramChannel;
import io.netty.channel.kqueue.KQueueEventLoopGroup;
import io.netty.channel.kqueue.KQueueServerSocketChannel;
import io.netty.channel.local.LocalAddress;
import io.netty.channel.local.LocalServerChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerDatagramChannel;
import io.netty.channel.oio.OioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.oio.OioDatagramChannel;
import io.netty.channel.socket.oio.OioServerSocketChannel;
/**
* The server can only be accessed in an ASYNC manner. This means that the server can only be used in RESPONSE to events. If you access the
@ -64,7 +79,7 @@ class Server<C extends Connection> extends EndPointServer {
private final ServerBootstrap localBootstrap;
private final ServerBootstrap tcpBootstrap;
private final ServerBootstrap udpBootstrap;
private final SessionBootstrap udpBootstrap;
private final int tcpPort;
private final int udpPort;
@ -125,7 +140,7 @@ class Server<C extends Connection> extends EndPointServer {
}
if (udpPort > 0) {
udpBootstrap = new ServerBootstrap();
udpBootstrap = new SessionBootstrap();
}
else {
udpBootstrap = null;
@ -138,25 +153,25 @@ class Server<C extends Connection> extends EndPointServer {
final EventLoopGroup boss;
final EventLoopGroup worker;
// if (OS.isAndroid()) {
// // android ONLY supports OIO (not NIO)
// boss = new OioEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-boss", threadGroup));
// worker = new OioEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName, threadGroup));
// }
// else if (OS.isLinux() && NativeLibrary.isAvailable()) {
// // JNI network stack is MUCH faster (but only on linux)
// boss = new EpollEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-boss", threadGroup));
// worker = new EpollEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName, threadGroup));
// }
// else if (OS.isMacOsX() && NativeLibrary.isAvailable()) {
// // KQueue network stack is MUCH faster (but only on macosx)
// boss = new KQueueEventLoopGroup(EndPoint.DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-boss", threadGroup));
// worker = new KQueueEventLoopGroup(EndPoint.DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName, threadGroup));
// }
// else {
if (OS.isAndroid()) {
// android ONLY supports OIO (not NIO)
boss = new OioEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-boss", threadGroup));
worker = new OioEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName, threadGroup));
}
else if (OS.isLinux() && NativeLibrary.isAvailable()) {
// JNI network stack is MUCH faster (but only on linux)
boss = new EpollEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-boss", threadGroup));
worker = new EpollEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName, threadGroup));
}
else if (OS.isMacOsX() && NativeLibrary.isAvailable()) {
// KQueue network stack is MUCH faster (but only on macosx)
boss = new KQueueEventLoopGroup(EndPoint.DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-boss", threadGroup));
worker = new KQueueEventLoopGroup(EndPoint.DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName, threadGroup));
}
else {
boss = new NioEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-boss", threadGroup));
worker = new NioEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName, threadGroup));
// }
}
manageForShutdown(boss);
manageForShutdown(worker);
@ -185,21 +200,21 @@ class Server<C extends Connection> extends EndPointServer {
}
if (tcpBootstrap != null) {
// if (OS.isAndroid()) {
// // android ONLY supports OIO (not NIO)
// tcpBootstrap.channel(OioServerSocketChannel.class);
// }
// else if (OS.isLinux() && NativeLibrary.isAvailable()) {
// // JNI network stack is MUCH faster (but only on linux)
// tcpBootstrap.channel(EpollServerSocketChannel.class);
// }
// else if (OS.isMacOsX() && NativeLibrary.isAvailable()) {
// // KQueue network stack is MUCH faster (but only on macosx)
// tcpBootstrap.channel(KQueueServerSocketChannel.class);
// }
// else {
if (OS.isAndroid()) {
// android ONLY supports OIO (not NIO)
tcpBootstrap.channel(OioServerSocketChannel.class);
}
else if (OS.isLinux() && NativeLibrary.isAvailable()) {
// JNI network stack is MUCH faster (but only on linux)
tcpBootstrap.channel(EpollServerSocketChannel.class);
}
else if (OS.isMacOsX() && NativeLibrary.isAvailable()) {
// KQueue network stack is MUCH faster (but only on macosx)
tcpBootstrap.channel(KQueueServerSocketChannel.class);
}
else {
tcpBootstrap.channel(NioServerSocketChannel.class);
// }
}
// TODO: If we use netty for an HTTP server,
// Beside the usual ChannelOptions the Native Transport allows to enable TCP_CORK which may come in handy if you implement a HTTP Server.
@ -230,23 +245,22 @@ class Server<C extends Connection> extends EndPointServer {
if (udpBootstrap != null) {
// if (OS.isAndroid()) {
// // android ONLY supports OIO (not NIO)
// udpBootstrap.channel(OioDatagramChannel.class);
// }
// else if (OS.isLinux() && NativeLibrary.isAvailable()) {
// // JNI network stack is MUCH faster (but only on linux)
// udpBootstrap.channel(EpollDatagramChannel.class);
// }
// else if (OS.isMacOsX() && NativeLibrary.isAvailable()) {
// // KQueue network stack is MUCH faster (but only on macosx)
// udpBootstrap.channel(KQueueDatagramChannel.class);
// }
// else {
if (OS.isAndroid()) {
// android ONLY supports OIO (not NIO)
udpBootstrap.channel(OioDatagramChannel.class);
}
else if (OS.isLinux() && NativeLibrary.isAvailable()) {
// JNI network stack is MUCH faster (but only on linux)
udpBootstrap.channel(EpollDatagramChannel.class);
}
else if (OS.isMacOsX() && NativeLibrary.isAvailable()) {
// KQueue network stack is MUCH faster (but only on macosx)
udpBootstrap.channel(KQueueDatagramChannel.class);
}
else {
// windows and linux/mac that are incompatible with the native implementations
// udpBootstrap.channel(NioDatagramChannel.class);
// }
udpBootstrap.channel(NioServerDatagramChannel.class);
udpBootstrap.channel(NioDatagramChannel.class);
}
// Netty4 has a default of 2048 bytes as upper limit for datagram packets, we want this to be whatever we specify
@ -260,11 +274,7 @@ class Server<C extends Connection> extends EndPointServer {
// TODO: move broadcast to it's own handler, and have UDP server be able to be bound to a specific IP
// OF NOTE: At the end in my case I decided to bind to .255 broadcast address on Linux systems. (to receive broadcast packets)
.localAddress(udpPort) // if you bind to a specific interface, Linux will be unable to receive broadcast packets! see: http://developerweb.net/viewtopic.php?id=5722
.childHandler(new RegistrationRemoteHandlerServerUDP(threadName,
registrationWrapper));
.childHandler(new RegistrationRemoteHandlerServerUDP(threadName, registrationWrapper));
// // have to check options.host for null. we don't bind to 0.0.0.0, we bind to "null" to get the "any" address!
// if (hostName.equals("0.0.0.0")) {

View File

@ -44,7 +44,7 @@ class KryoDecoderUdp extends MessageToMessageDecoder<Object> {
@Override
public
boolean acceptInboundMessage(final Object msg) throws Exception {
return msg instanceof ByteBuf || msg instanceof AddressedEnvelope;
return msg instanceof AddressedEnvelope;
}
/**
@ -74,15 +74,7 @@ class KryoDecoderUdp extends MessageToMessageDecoder<Object> {
@Override
protected
void decode(ChannelHandlerContext context, Object message, List<Object> out) throws Exception {
ByteBuf data;
if (message instanceof AddressedEnvelope) {
// this is on the client
data = (ByteBuf) ((AddressedEnvelope) message).content();
} else {
// this is on the server
data = (ByteBuf) message;
}
ByteBuf data = (ByteBuf) ((AddressedEnvelope) message).content();
if (data != null) {
try {

View File

@ -13,15 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.netty.channel.socket.nio;
package io.netty.bootstrap;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.AbstractNioChannel.NioUnsafe;
import io.netty.channel.nio.NioEventLoop;
import io.netty.channel.AbstractChannel;
import io.netty.channel.Channel;
import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelMetadata;
import io.netty.channel.ChannelOutboundBuffer;
import io.netty.channel.ChannelPromise;
import io.netty.channel.EventLoop;
import io.netty.channel.socket.DatagramPacket;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.internal.RecyclableArrayList;
@ -41,22 +44,27 @@ class DatagramSessionChannel extends AbstractChannel implements Channel {
private static final ChannelMetadata METADATA = new ChannelMetadata(false, 16);
protected final DatagramSessionChannelConfig config;
private final DatagramSessionChannelConfig config;
protected final NioServerDatagramChannel serverChannel;
protected final InetSocketAddress remote;
private SessionManager sessionManager;
private InetSocketAddress localAddress;
private InetSocketAddress remoteAddress;
private volatile boolean isOpen = true;
private ByteBuf buffer;
protected
DatagramSessionChannel(NioServerDatagramChannel serverChannel, InetSocketAddress remote) {
super(serverChannel);
this.serverChannel = serverChannel;
this.remote = remote;
DatagramSessionChannel(final Channel parentChannel,
final SessionManager sessionManager,
final DatagramSessionChannelConfig sessionConfig,
final InetSocketAddress localAddress,
final InetSocketAddress remoteAddress) {
super(parentChannel);
config = new DatagramSessionChannelConfig(this, serverChannel);
this.sessionManager = sessionManager;
this.config = sessionConfig;
this.localAddress = localAddress;
this.remoteAddress = remoteAddress;
}
@Override
@ -68,12 +76,6 @@ class DatagramSessionChannel extends AbstractChannel implements Channel {
@Override
protected
void doBeginRead() throws Exception {
// a single packet is 100% of our data, so we cannot have multiple reads (there is no "session" for UDP)
ChannelPipeline pipeline = pipeline();
pipeline.fireChannelRead(buffer);
pipeline.fireChannelReadComplete();
buffer = null;
}
@Override
@ -86,7 +88,7 @@ class DatagramSessionChannel extends AbstractChannel implements Channel {
protected
void doClose() throws Exception {
isOpen = false;
serverChannel.doCloseChannel(this);
sessionManager.doCloseChannel(this);
}
@Override
@ -119,7 +121,7 @@ class DatagramSessionChannel extends AbstractChannel implements Channel {
}
//schedule a task that will write those entries
NioEventLoop eventLoop = serverChannel.eventLoop();
EventLoop eventLoop = parent().eventLoop();
if (eventLoop.inEventLoop()) {
write0(list);
}
@ -143,7 +145,8 @@ class DatagramSessionChannel extends AbstractChannel implements Channel {
@Override
protected
boolean isCompatible(EventLoop eventloop) {
return eventloop instanceof NioEventLoop;
// compatible with all Datagram event loops where we are explicitly used
return true;
}
@Override
@ -161,7 +164,7 @@ class DatagramSessionChannel extends AbstractChannel implements Channel {
@Override
protected
SocketAddress localAddress0() {
return serverChannel.localAddress0();
return localAddress;
}
@Override
@ -180,24 +183,19 @@ class DatagramSessionChannel extends AbstractChannel implements Channel {
@Override
public
InetSocketAddress remoteAddress() {
return remote;
return remoteAddress;
}
@Override
protected
InetSocketAddress remoteAddress0() {
return remote;
}
public
void setBuffer(final ByteBuf buffer) {
this.buffer = buffer;
return remoteAddress;
}
private
void write0(final RecyclableArrayList list) {
try {
NioUnsafe unsafe = serverChannel.unsafe();
Unsafe unsafe = super.parent().unsafe();
for (Object buf : list) {
unsafe.write(buf, voidPromise());

View File

@ -13,13 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.netty.channel.socket.nio;
package io.netty.bootstrap;
import java.util.Map;
import dorkbox.network.connection.EndPoint;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.Channel;
import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelOption;
import io.netty.channel.DefaultMessageSizeEstimator;
import io.netty.channel.MessageSizeEstimator;
import io.netty.channel.RecvByteBufAllocator;
import io.netty.channel.WriteBufferWaterMark;
import io.netty.channel.socket.DatagramChannelConfig;
/**
@ -31,14 +37,14 @@ public class DatagramSessionChannelConfig implements ChannelConfig {
private volatile MessageSizeEstimator msgSizeEstimator = DEFAULT_MSG_SIZE_ESTIMATOR;
private final NioServerDatagramChannel serverDatagramSessionChannel;
private final Channel channel;
/**
* Creates a new instance.
*/
public
DatagramSessionChannelConfig(DatagramSessionChannel channel, final NioServerDatagramChannel serverDatagramSessionChannel) {
this.serverDatagramSessionChannel = serverDatagramSessionChannel;
DatagramSessionChannelConfig(Channel channel) {
this.channel = channel;
}
@Override
@ -56,7 +62,7 @@ public class DatagramSessionChannelConfig implements ChannelConfig {
@Override
public
<T> T getOption(final ChannelOption<T> option) {
return serverDatagramSessionChannel.config().getOption(option);
return channel.config().getOption(option);
}
@Override
@ -104,8 +110,7 @@ public class DatagramSessionChannelConfig implements ChannelConfig {
@Override
public
ByteBufAllocator getAllocator() {
return serverDatagramSessionChannel.config()
.getAllocator();
return channel.config().getAllocator();
}
@Override
@ -117,8 +122,7 @@ public class DatagramSessionChannelConfig implements ChannelConfig {
@Override
public
<T extends RecvByteBufAllocator> T getRecvByteBufAllocator() {
return serverDatagramSessionChannel.config()
.getRecvByteBufAllocator();
return channel.config().getRecvByteBufAllocator();
}
@Override

View File

@ -0,0 +1,289 @@
/*
* Copyright 2018 dorkbox, llc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.netty.bootstrap;
import java.net.SocketAddress;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.resolver.AddressResolverGroup;
import io.netty.resolver.DefaultAddressResolverGroup;
import io.netty.util.AttributeKey;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
/**
* VERY similar to the ServerBootstrap class, with the change to having a "SessionManager" added to the pipeline on init (instead of
* the ServerBootstrapAcceptor getting added)
*/
public
class SessionBootstrap extends AbstractBootstrap<SessionBootstrap, Channel> {
private static final AddressResolverGroup<?> DEFAULT_RESOLVER = DefaultAddressResolverGroup.INSTANCE;
private static final InternalLogger logger = InternalLoggerFactory.getInstance(SessionBootstrap.class);
@SuppressWarnings("unchecked")
private static
Entry<AttributeKey<?>, Object>[] newAttrArray(int size) {
return new Entry[size];
}
@SuppressWarnings("unchecked")
private static
Map.Entry<ChannelOption<?>, Object>[] newOptionArray(int size) {
return new Map.Entry[size];
}
private final Map<ChannelOption<?>, Object> childOptions = new LinkedHashMap<ChannelOption<?>, Object>();
private final Map<AttributeKey<?>, Object> childAttrs = new LinkedHashMap<AttributeKey<?>, Object>();
private final SessionBootstrapConfig config = new SessionBootstrapConfig(this);
private volatile EventLoopGroup childGroup;
private volatile ChannelHandler childHandler;
@SuppressWarnings("unchecked")
private volatile AddressResolverGroup<SocketAddress> resolver = (AddressResolverGroup<SocketAddress>) DEFAULT_RESOLVER;
public
SessionBootstrap() { }
private
SessionBootstrap(SessionBootstrap bootstrap) {
super(bootstrap);
resolver = bootstrap.resolver;
childGroup = bootstrap.childGroup;
childHandler = bootstrap.childHandler;
synchronized (bootstrap.childOptions) {
childOptions.putAll(bootstrap.childOptions);
}
synchronized (bootstrap.childAttrs) {
childAttrs.putAll(bootstrap.childAttrs);
}
}
/**
* Set the specific {@link AttributeKey} with the given value on every child {@link Channel}. If the value is
* {@code null} the {@link AttributeKey} is removed
*/
public
<T> SessionBootstrap childAttr(AttributeKey<T> childKey, T value) {
if (childKey == null) {
throw new NullPointerException("childKey");
}
if (value == null) {
childAttrs.remove(childKey);
}
else {
childAttrs.put(childKey, value);
}
return this;
}
final
Map<AttributeKey<?>, Object> childAttrs() {
return copiedMap(childAttrs);
}
/**
* Return the configured {@link EventLoopGroup} which will be used for the child channels or {@code null}
* if non is configured yet.
*
* @deprecated Use {@link #config()} instead.
*/
@Deprecated
public
EventLoopGroup childGroup() {
return childGroup;
}
/**
* Set the {@link ChannelHandler} which is used to serve the request for the {@link Channel}'s.
*/
public
SessionBootstrap childHandler(ChannelHandler childHandler) {
if (childHandler == null) {
throw new NullPointerException("childHandler");
}
this.childHandler = childHandler;
return this;
}
final
ChannelHandler childHandler() {
return childHandler;
}
/**
* Allow to specify a {@link ChannelOption} which is used for the {@link Channel} instances once they get created
* (after the acceptor accepted the {@link Channel}). Use a value of {@code null} to remove a previous set
* {@link ChannelOption}.
*/
public
<T> SessionBootstrap childOption(ChannelOption<T> childOption, T value) {
if (childOption == null) {
throw new NullPointerException("childOption");
}
if (value == null) {
synchronized (childOptions) {
childOptions.remove(childOption);
}
}
else {
synchronized (childOptions) {
childOptions.put(childOption, value);
}
}
return this;
}
final
Map<ChannelOption<?>, Object> childOptions() {
return copiedMap(childOptions);
}
@Override
@SuppressWarnings("CloneDoesntCallSuperClone")
public
SessionBootstrap clone() {
return new SessionBootstrap(this);
}
@Override
public final
SessionBootstrapConfig config() {
return config;
}
/**
* Specify the {@link EventLoopGroup} which is used for the parent (acceptor) and the child (client).
*/
@Override
public
SessionBootstrap group(EventLoopGroup group) {
return group(group, group);
}
/**
* Set the {@link EventLoopGroup} for the parent (acceptor) and the child (client). These
* {@link EventLoopGroup}'s are used to handle all the events and IO for {@link DatagramSessionChannel} and
* {@link Channel}'s.
*/
public
SessionBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
super.group(parentGroup);
if (childGroup == null) {
throw new NullPointerException("childGroup");
}
if (this.childGroup != null) {
throw new IllegalStateException("childGroup set already");
}
this.childGroup = childGroup;
return this;
}
@Override
void init(Channel channel) throws Exception {
final Map<ChannelOption<?>, Object> options = options0();
synchronized (options) {
setChannelOptions(channel, options, logger);
}
final Map<AttributeKey<?>, Object> attrs = attrs0();
synchronized (attrs) {
for (Entry<AttributeKey<?>, Object> e : attrs.entrySet()) {
@SuppressWarnings("unchecked")
AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
channel.attr(key)
.set(e.getValue());
}
}
ChannelPipeline p = channel.pipeline();
final EventLoopGroup currentChildGroup = childGroup;
final ChannelHandler currentChildHandler = childHandler;
final Entry<ChannelOption<?>, Object>[] currentChildOptions;
final Entry<AttributeKey<?>, Object>[] currentChildAttrs;
synchronized (childOptions) {
currentChildOptions = childOptions.entrySet()
.toArray(newOptionArray(childOptions.size()));
}
synchronized (childAttrs) {
currentChildAttrs = childAttrs.entrySet()
.toArray(newAttrArray(childAttrs.size()));
}
p.addLast(new ChannelInitializer<Channel>() {
@Override
public
void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
ch.eventLoop()
.execute(new Runnable() {
@Override
public
void run() {
pipeline.addLast(new SessionManager(ch,
currentChildGroup,
currentChildHandler,
currentChildOptions,
currentChildAttrs));
}
});
}
});
}
public
SocketAddress remoteAddress() {
// the nature of this is that WE do not have a remote address, but our child channels DO have a remote address
return null;
}
public
AddressResolverGroup<?> resolver() {
return resolver;
}
@Override
public
SessionBootstrap validate() {
super.validate();
if (childHandler == null) {
throw new IllegalStateException("childHandler not set");
}
if (childGroup == null) {
logger.warn("childGroup is not set. Using parentGroup instead.");
childGroup = config.group();
}
return this;
}
}

View File

@ -0,0 +1,64 @@
/*
* Copyright 2018 dorkbox, llc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.netty.bootstrap;
import java.net.SocketAddress;
import io.netty.channel.Channel;
import io.netty.resolver.AddressResolverGroup;
/**
* Exposes the configuration of a {@link Bootstrap}.
*/
public final
class SessionBootstrapConfig extends AbstractBootstrapConfig<SessionBootstrap, Channel> {
SessionBootstrapConfig(SessionBootstrap bootstrap) {
super(bootstrap);
}
/**
* Returns the configured remote address or {@code null} if non is configured yet.
*/
public
SocketAddress remoteAddress() {
return bootstrap.remoteAddress();
}
/**
* Returns the configured {@link AddressResolverGroup} or the default if non is configured yet.
*/
public
AddressResolverGroup<?> resolver() {
return bootstrap.resolver();
}
@Override
public
String toString() {
StringBuilder buf = new StringBuilder(super.toString());
buf.setLength(buf.length() - 1);
buf.append(", resolver: ")
.append(resolver());
SocketAddress remoteAddress = remoteAddress();
if (remoteAddress != null) {
buf.append(", remoteAddress: ")
.append(remoteAddress);
}
return buf.append(')')
.toString();
}
}

View File

@ -0,0 +1,245 @@
/*
* Copyright 2018 dorkbox, llc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.netty.bootstrap;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
import dorkbox.network.pipeline.discovery.BroadcastServer;
import dorkbox.util.bytes.BigEndian.Long_;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.util.AttributeKey;
import io.netty.util.collection.LongObjectHashMap;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
/**
*
*/
public
class SessionManager extends ChannelInboundHandlerAdapter {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(SessionBootstrap.class);
private static
void forceClose(DatagramSessionChannel child, Throwable t) throws Exception {
child.unsafe()
.closeForcibly();
child.doClose();
logger.warn("Failed to register an accepted channel: {}", child, t);
}
private static
long getChannelId(final InetSocketAddress remoteAddress) {
int address = remoteAddress.getAddress()
.hashCode(); // we want it as an int
int port = remoteAddress.getPort(); // this is really just 2 bytes
byte[] combined = new byte[8];
combined[0] = (byte) ((port >>> 24) & 0xFF);
combined[1] = (byte) ((port >>> 16) & 0xFF);
combined[2] = (byte) ((port >>> 8) & 0xFF);
combined[3] = (byte) ((port) & 0xFF);
combined[4] = (byte) ((address >>> 24) & 0xFF);
combined[5] = (byte) ((address >>> 16) & 0xFF);
combined[6] = (byte) ((address >>> 8) & 0xFF);
combined[7] = (byte) ((address) & 0xFF);
return Long_.from(combined);
}
private final BroadcastServer broadcastServer = new BroadcastServer();
// Does not need to be thread safe, because access only happens in the event loop
private final LongObjectHashMap<DatagramSessionChannel> datagramChannels = new LongObjectHashMap<DatagramSessionChannel>();
private final EventLoopGroup childGroup;
private final ChannelHandler childHandler;
private final Entry<ChannelOption<?>, Object>[] childOptions;
private final Entry<AttributeKey<?>, Object>[] childAttrs;
private final Runnable enableAutoReadTask;
private final DatagramSessionChannelConfig sessionConfig;
SessionManager(final Channel channel,
EventLoopGroup childGroup,
ChannelHandler childHandler,
Entry<ChannelOption<?>, Object>[] childOptions,
Entry<AttributeKey<?>, Object>[] childAttrs) {
this.sessionConfig = new DatagramSessionChannelConfig(channel);
this.childGroup = childGroup;
this.childHandler = childHandler;
this.childOptions = childOptions;
this.childAttrs = childAttrs;
// Task which is scheduled to re-enable auto-read.
// It's important to create this Runnable before we try to submit it as otherwise the URLClassLoader may
// not be able to load the class because of the file limit it already reached.
//
// See https://github.com/netty/netty/issues/1328
enableAutoReadTask = new Runnable() {
@Override
public
void run() {
channel.config()
.setAutoRead(true);
}
};
}
@Override
public
void channelInactive(final ChannelHandlerContext ctx) throws Exception {
super.channelInactive(ctx);
// have to close all of the "fake" DatagramChannels as well. Each one will remove itself from the channel map.
// We make a copy of this b/c of concurrent modification, in the event this is closed BEFORE the child-channels are closed
ArrayList<DatagramSessionChannel> channels = new ArrayList<DatagramSessionChannel>(datagramChannels.values());
for (DatagramSessionChannel datagramSessionChannel : channels) {
datagramSessionChannel.close();
}
}
@Override
@SuppressWarnings("unchecked")
public
void channelRead(ChannelHandlerContext context, Object msg) {
Channel channel = context.channel();
DatagramPacket packet = ((DatagramPacket) msg);
ByteBuf content = packet.content();
InetSocketAddress localAddress = packet.recipient();
InetSocketAddress remoteAddress = packet.sender();
// check to see if it's a broadcast packet or not
if (broadcastServer.isBroadcast(channel, content, localAddress, remoteAddress)) {
// don't bother creating channels if this is a broadcast event. Just respond and be finished
return;
}
long channelId = getChannelId(remoteAddress);
// create a new channel or reuse existing one
DatagramSessionChannel sessionChannel = datagramChannels.get(channelId);
ChannelPipeline sessionPipeline;
if (sessionChannel == null) {
try {
sessionChannel = new DatagramSessionChannel(context.channel(), this, sessionConfig, localAddress, remoteAddress);
datagramChannels.put(channelId, sessionChannel);
sessionPipeline = sessionChannel.pipeline();
// add the child handler to the fake channel
sessionPipeline.addLast(childHandler);
// setup the channel options
AbstractBootstrap.setChannelOptions(sessionChannel, childOptions, logger);
for (Entry<AttributeKey<?>, Object> e : childAttrs) {
sessionChannel.attr((AttributeKey<Object>) e.getKey())
.set(e.getValue());
}
try {
final DatagramSessionChannel finalSessionChannel = sessionChannel;
childGroup.register(sessionChannel)
.addListener(new ChannelFutureListener() {
@Override
public
void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
forceClose(finalSessionChannel, future.cause());
}
}
});
} catch (Throwable t) {
forceClose(sessionChannel, t);
}
} catch (Throwable t) {
logger.warn("Failed to create a new datagram channel from a read operation.", t);
try {
if (sessionChannel != null) {
sessionChannel.close();
}
} catch (Throwable t2) {
logger.warn("Failed to close the datagram channel.", t2);
}
return;
}
} else {
sessionPipeline = sessionChannel.pipeline();
}
// immediately trigger a read in the session pipeline
sessionPipeline.fireChannelRead(packet);
// will flush the pipeline if necessary
sessionPipeline.fireChannelReadComplete();
}
/**
* ADDED to support closing a DatagramSessionChannel. Always called from the EventLoop
*/
public
void doCloseChannel(final DatagramSessionChannel datagramSessionChannel) {
InetSocketAddress remoteAddress = datagramSessionChannel.remoteAddress();
long channelId = getChannelId(remoteAddress);
datagramChannels.remove(channelId);
}
@Override
public
void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
final ChannelConfig config = ctx.channel()
.config();
if (config.isAutoRead()) {
// stop accept new connections for 1 second to allow the channel to recover
// See https://github.com/netty/netty/issues/1328
config.setAutoRead(false);
ctx.channel()
.eventLoop()
.schedule(enableAutoReadTask, 1, TimeUnit.SECONDS);
}
// still let the exceptionCaught event flow through the pipeline to give the user
// a chance to do something with it
ctx.fireExceptionCaught(cause);
}
}

View File

@ -1,492 +0,0 @@
/*
* Copyright 2018 dorkbox, llc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.netty.channel.socket.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.spi.SelectorProvider;
import java.util.ArrayList;
import java.util.List;
import dorkbox.network.pipeline.discovery.BroadcastServer;
import dorkbox.util.bytes.BigEndian.Long_;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.AbstractNioMessageChannel;
import io.netty.channel.socket.*;
import io.netty.util.collection.LongObjectHashMap;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.SocketUtils;
import io.netty.util.internal.StringUtil;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
/**
* An NIO datagram {@link Channel} that sends and receives an
* {@link AddressedEnvelope AddressedEnvelope<ByteBuf, SocketAddress>}.
*
* @see AddressedEnvelope
* @see DatagramPacket
*/
public final
class NioServerDatagramChannel extends AbstractNioMessageChannel implements ServerSocketChannel {
private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
private static final ChannelMetadata METADATA = new ChannelMetadata(true, 16);
private static final String EXPECTED_TYPES = " (expected: " +
StringUtil.simpleClassName(DatagramPacket.class) + ", " +
StringUtil.simpleClassName(AddressedEnvelope.class) + '<' +
StringUtil.simpleClassName(ByteBuf.class) + ", " +
StringUtil.simpleClassName(SocketAddress.class) + ">, " +
StringUtil.simpleClassName(ByteBuf.class) + ')';
private static final InternalLogger logger = InternalLoggerFactory.getInstance(NioServerDatagramChannel.class);
private static
java.nio.channels.DatagramChannel newSocket(SelectorProvider provider) {
try {
/**
* Use the {@link SelectorProvider} to open {@link SocketChannel} and so remove condition in
* {@link SelectorProvider#provider()} which is called by each DatagramSessionChannel.open() otherwise.
*
* See <a href="https://github.com/netty/netty/issues/2308">#2308</a>.
*/
return provider.openDatagramChannel();
} catch (IOException e) {
throw new ChannelException("Failed to open a socket.", e);
}
}
private static
java.nio.channels.DatagramChannel newSocket(SelectorProvider provider, InternetProtocolFamily ipFamily) {
if (ipFamily == null) {
return newSocket(provider);
}
checkJavaVersion();
try {
return NioServerDatagramChannel7.newSocket(provider, ipFamily);
} catch (IOException e) {
throw new ChannelException("Failed to open a socket.", e);
}
}
private static
void checkJavaVersion() {
if (PlatformDependent.javaVersion() < 7) {
throw new UnsupportedOperationException("Only supported on java 7+.");
}
}
/**
* Checks if the specified buffer is a direct buffer and is composed of a single NIO buffer.
* (We check this because otherwise we need to make it a non-composite buffer.)
*/
private static
boolean isSingleDirectBuffer(ByteBuf buf) {
return buf.isDirect() && buf.nioBufferCount() == 1;
}
private static
long getChannelId(final InetSocketAddress remoteAddress) {
int address = remoteAddress.getAddress()
.hashCode(); // we want it as an int
int port = remoteAddress.getPort(); // this is really just 2 bytes
byte[] combined = new byte[8];
combined[0] = (byte) ((port >>> 24) & 0xFF);
combined[1] = (byte) ((port >>> 16) & 0xFF);
combined[2] = (byte) ((port >>> 8) & 0xFF);
combined[3] = (byte) ((port) & 0xFF);
combined[4] = (byte) ((address >>> 24) & 0xFF);
combined[5] = (byte) ((address >>> 16) & 0xFF);
combined[6] = (byte) ((address >>> 8) & 0xFF);
combined[7] = (byte) ((address) & 0xFF);
return Long_.from(combined);
}
private final ServerSocketChannelConfig config;
// Does not need to be thread safe, because access only happens in the event loop
private final LongObjectHashMap<DatagramSessionChannel> datagramChannels = new LongObjectHashMap<DatagramSessionChannel>();
private BroadcastServer broadcastServer;
/**
* Create a new instance which will use the Operation Systems default {@link InternetProtocolFamily}.
*/
public
NioServerDatagramChannel() {
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
/**
* Create a new instance using the given {@link SelectorProvider}
* which will use the Operation Systems default {@link InternetProtocolFamily}.
*/
public
NioServerDatagramChannel(SelectorProvider provider) {
this(newSocket(provider));
}
/**
* Create a new instance using the given {@link InternetProtocolFamily}. If {@code null} is used it will depend
* on the Operation Systems default which will be chosen.
*/
public
NioServerDatagramChannel(InternetProtocolFamily ipFamily) {
this(newSocket(DEFAULT_SELECTOR_PROVIDER, ipFamily));
}
/**
* Create a new instance using the given {@link SelectorProvider} and {@link InternetProtocolFamily}.
* If {@link InternetProtocolFamily} is {@code null} it will depend on the Operation Systems default
* which will be chosen.
*/
public
NioServerDatagramChannel(SelectorProvider provider, InternetProtocolFamily ipFamily) {
this(newSocket(provider, ipFamily));
}
/**
* Create a new instance from the given {@link java.nio.channels.DatagramChannel}.
*/
public
NioServerDatagramChannel(java.nio.channels.DatagramChannel socket) {
super(null, socket, SelectionKey.OP_READ);
config = new NioServerDatagramChannelConfig(this, socket);
broadcastServer = new BroadcastServer();
}
void clearReadPending0() {
clearReadPending();
}
@Override
protected
boolean closeOnReadError(Throwable cause) {
// We do not want to close on SocketException when using DatagramSessionChannel as we usually can continue receiving.
// See https://github.com/netty/netty/issues/5893
if (cause instanceof SocketException) {
return false;
}
return super.closeOnReadError(cause);
}
@Override
public
ServerSocketChannelConfig config() {
return config;
}
@Override
protected
boolean continueOnWriteError() {
// Continue on write error as a DatagramSessionChannel can write to multiple remote peers
//
// See https://github.com/netty/netty/issues/2665
return true;
}
@Override
protected
void doBind(SocketAddress localAddress) throws Exception {
doBind0(localAddress);
}
private
void doBind0(SocketAddress localAddress) throws Exception {
if (PlatformDependent.javaVersion() >= 7) {
SocketUtils.bind(javaChannel(), localAddress);
}
else {
javaChannel().socket()
.bind(localAddress);
}
}
// Always called from the EventLoop
@Override
protected
void doClose() throws Exception {
// have to close all of the fake DatagramChannels as well. Each one will remove itself from the channel map.
// We make a copy of this b/c of concurrent modification, in the event this is closed BEFORE the child-channels are closed
ArrayList<DatagramSessionChannel> channels = new ArrayList<DatagramSessionChannel>(datagramChannels.values());
for (DatagramSessionChannel datagramSessionChannel : channels) {
datagramSessionChannel.close();
}
javaChannel().close();
}
/**
* ADDED to support closing a DatagramSessionChannel. Always called from the EventLoop
*/
public
void doCloseChannel(final DatagramSessionChannel datagramSessionChannel) {
InetSocketAddress remoteAddress = datagramSessionChannel.remoteAddress();
long channelId = getChannelId(remoteAddress);
datagramChannels.remove(channelId);
}
@Override
protected
boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
// Unnecessary stuff
throw new UnsupportedOperationException();
}
@Override
protected
void doDisconnect() throws Exception {
// Unnecessary stuff
throw new UnsupportedOperationException();
}
@Override
protected
void doFinishConnect() throws Exception {
// Unnecessary stuff
throw new UnsupportedOperationException();
}
@Override
protected
int doReadMessages(List<Object> buf) throws Exception {
java.nio.channels.DatagramChannel ch = javaChannel();
ServerSocketChannelConfig config = config();
RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
ChannelPipeline pipeline = pipeline();
ByteBuf data = allocHandle.allocate(config.getAllocator());
allocHandle.attemptedBytesRead(data.writableBytes());
boolean free = true;
try {
ByteBuffer nioData = data.internalNioBuffer(data.writerIndex(), data.writableBytes());
int pos = nioData.position();
InetSocketAddress remoteAddress = (InetSocketAddress) ch.receive(nioData);
if (remoteAddress == null) {
return 0;
}
allocHandle.lastBytesRead(nioData.position() - pos);
ByteBuf byteBuf = data.writerIndex(data.writerIndex() + allocHandle.lastBytesRead());
// original behavior from NioDatagramChannel.
// buf.add(new DatagramPacket(byteBuf, localAddress(), remoteAddress));
// free = false;
// return 1;
// new behavior
// check to see if it's a broadcast packet or not
ByteBuf broadcast = broadcastServer.getBroadcastResponse(byteBuf, remoteAddress);
if (broadcast != null) {
// don't bother creating channels if this is a broadcast event. Just respond and be finished
doWriteBytes(broadcast, remoteAddress);
// no messages created (since we directly write to the channel).
return 0;
}
long channelId = getChannelId(remoteAddress);
// create a new channel or reuse existing one
DatagramSessionChannel channel = datagramChannels.get(channelId);
if (channel == null) {
try {
channel = new DatagramSessionChannel(this, remoteAddress);
datagramChannels.put(channelId, channel);
// This channel is registered automatically AFTER this read method completes
} catch (Throwable t) {
logger.warn("Failed to create a new datagram channel from a read operation.", t);
try {
channel.close();
} catch (Throwable t2) {
logger.warn("Failed to close the datagram channel.", t2);
}
return 0;
}
}
// set the bytes of the datagram channel
channel.setBuffer(byteBuf);
pipeline.fireChannelRead(channel);
// immediately trigger a read
channel.read();
free = false;
// we manually fireChannelRead + read (caller class calls readComplete for us)
return 0;
} catch (Throwable cause) {
PlatformDependent.throwException(cause);
return -1; // -1 means to close this channel
} finally {
if (free) {
data.release();
}
}
}
@Override
protected
boolean doWriteMessage(Object msg, ChannelOutboundBuffer in) throws Exception {
final SocketAddress remoteAddress;
final ByteBuf data;
if (msg instanceof AddressedEnvelope) {
@SuppressWarnings("unchecked")
AddressedEnvelope<ByteBuf, SocketAddress> envelope = (AddressedEnvelope<ByteBuf, SocketAddress>) msg;
remoteAddress = envelope.recipient();
data = envelope.content();
}
else {
data = (ByteBuf) msg;
remoteAddress = null;
}
return doWriteBytes(data, remoteAddress);
}
private
boolean doWriteBytes(final ByteBuf data, final SocketAddress remoteAddress) throws IOException {
final int dataLen = data.readableBytes();
if (dataLen == 0) {
return true;
}
final ByteBuffer nioData = data.internalNioBuffer(data.readerIndex(), dataLen);
final int writtenBytes;
if (remoteAddress != null) {
writtenBytes = javaChannel().send(nioData, remoteAddress);
}
else {
writtenBytes = javaChannel().write(nioData);
}
return writtenBytes > 0;
}
@Override
protected
Object filterOutboundMessage(Object msg) {
if (msg instanceof DatagramPacket) {
DatagramPacket p = (DatagramPacket) msg;
ByteBuf content = p.content();
if (isSingleDirectBuffer(content)) {
return p;
}
return new DatagramPacket(newDirectBuffer(p, content), p.recipient());
}
if (msg instanceof ByteBuf) {
ByteBuf buf = (ByteBuf) msg;
if (isSingleDirectBuffer(buf)) {
return buf;
}
return newDirectBuffer(buf);
}
if (msg instanceof AddressedEnvelope) {
@SuppressWarnings("unchecked")
AddressedEnvelope<Object, SocketAddress> e = (AddressedEnvelope<Object, SocketAddress>) msg;
if (e.content() instanceof ByteBuf) {
ByteBuf content = (ByteBuf) e.content();
if (isSingleDirectBuffer(content)) {
return e;
}
return new DefaultAddressedEnvelope<ByteBuf, SocketAddress>(newDirectBuffer(e, content), e.recipient());
}
}
throw new UnsupportedOperationException("unsupported message type: " + StringUtil.simpleClassName(msg) + EXPECTED_TYPES);
}
@Override
@SuppressWarnings("deprecation")
public
boolean isActive() {
java.nio.channels.DatagramChannel ch = javaChannel();
// we do not support registration options
// return ch.isOpen() && (config.getOption(ChannelOption.DATAGRAM_CHANNEL_ACTIVE_ON_REGISTRATION) && isRegistered() || ch.socket().isBound());
return ch.isOpen() || ch.socket()
.isBound();
}
@Override
protected
java.nio.channels.DatagramChannel javaChannel() {
return (java.nio.channels.DatagramChannel) super.javaChannel();
}
@Override
public
InetSocketAddress localAddress() {
return (InetSocketAddress) super.localAddress();
}
@Override
protected
SocketAddress localAddress0() {
return javaChannel().socket()
.getLocalSocketAddress();
}
@Override
public
ChannelMetadata metadata() {
return METADATA;
}
@Override
public
InetSocketAddress remoteAddress() {
return null;
}
@Override
protected
SocketAddress remoteAddress0() {
return null;
}
@Override
@Deprecated
protected
void setReadPending(boolean readPending) {
super.setReadPending(readPending);
}
@Override
public
String toString() {
return "NioServerDatagramChannel";
}
}

View File

@ -1,35 +0,0 @@
/*
* Copyright 2018 dorkbox, llc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.netty.channel.socket.nio;
import java.io.IOException;
import java.nio.channels.DatagramChannel;
import java.nio.channels.spi.SelectorProvider;
import io.netty.channel.socket.InternetProtocolFamily;
/**
* For Java7+ only!
*/
public
class NioServerDatagramChannel7 {
// NOTE: this is suppressed because we compile this for java7, and everything else for java6, and this is only called if we are java7+
@SuppressWarnings("Since15")
public static
DatagramChannel newSocket(final SelectorProvider provider, final InternetProtocolFamily ipFamily) throws IOException {
return provider.openDatagramChannel(ProtocolFamilyConverter.convert(ipFamily));
}
}

View File

@ -1,171 +0,0 @@
/*
* Copyright 2018 dorkbox, llc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.netty.channel.socket.nio;
import java.net.SocketException;
import java.nio.channels.DatagramChannel;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.socket.ServerSocketChannelConfig;
/**
* This is a basic implementation of a ChannelConfig, with the exception that we take in a DatagramSessionChannel, and modify only the
* options of that channel that make sense
*/
public final
class NioServerDatagramChannelConfig extends DefaultChannelConfig implements ServerSocketChannelConfig {
private final DatagramChannel datagramChannel;
public
NioServerDatagramChannelConfig(NioServerDatagramChannel channel, DatagramChannel datagramChannel) {
super(channel);
this.datagramChannel = datagramChannel;
}
@Override
public
int getBacklog() {
return 1;
}
@Override
public
ServerSocketChannelConfig setBacklog(final int backlog) {
return this;
}
@Override
public
boolean isReuseAddress() {
try {
return datagramChannel.socket()
.getReuseAddress();
} catch (SocketException e) {
throw new ChannelException(e);
}
}
@Override
public
ServerSocketChannelConfig setReuseAddress(final boolean reuseAddress) {
try {
datagramChannel.socket()
.setReuseAddress(true);
} catch (SocketException e) {
throw new ChannelException(e);
}
return this;
}
@Override
public
int getReceiveBufferSize() {
try {
return datagramChannel.socket()
.getReceiveBufferSize();
} catch (SocketException e) {
throw new ChannelException(e);
}
}
@Override
public
ServerSocketChannelConfig setReceiveBufferSize(final int receiveBufferSize) {
try {
datagramChannel.socket()
.setReceiveBufferSize(receiveBufferSize);
} catch (SocketException e) {
throw new ChannelException(e);
}
return this;
}
@Override
public
ServerSocketChannelConfig setPerformancePreferences(final int connectionTime, final int latency, final int bandwidth) {
return this;
}
@Override
public
ServerSocketChannelConfig setConnectTimeoutMillis(int timeout) {
return this;
}
@Override
@Deprecated
public
ServerSocketChannelConfig setMaxMessagesPerRead(int n) {
super.setMaxMessagesPerRead(n);
return this;
}
@Override
public
ServerSocketChannelConfig setWriteSpinCount(int spincount) {
super.setWriteSpinCount(spincount);
return this;
}
@Override
public
ServerSocketChannelConfig setAllocator(ByteBufAllocator alloc) {
super.setAllocator(alloc);
return this;
}
@Override
public
ServerSocketChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator alloc) {
super.setRecvByteBufAllocator(alloc);
return this;
}
@Override
public
ServerSocketChannelConfig setAutoRead(boolean autoread) {
super.setAutoRead(autoread);
return this;
}
@Override
public
ServerSocketChannelConfig setWriteBufferHighWaterMark(int writeBufferHighWaterMark) {
return (ServerSocketChannelConfig) super.setWriteBufferHighWaterMark(writeBufferHighWaterMark);
}
@Override
public
ServerSocketChannelConfig setWriteBufferLowWaterMark(int writeBufferLowWaterMark) {
return (ServerSocketChannelConfig) super.setWriteBufferLowWaterMark(writeBufferLowWaterMark);
}
@Override
public
ServerSocketChannelConfig setWriteBufferWaterMark(WriteBufferWaterMark writeBufferWaterMark) {
return (ServerSocketChannelConfig) super.setWriteBufferWaterMark(writeBufferWaterMark);
}
@Override
public
ServerSocketChannelConfig setMessageSizeEstimator(MessageSizeEstimator est) {
super.setMessageSizeEstimator(est);
return this;
}
}