Implement NIO datagram transport with the new API
- AbstractChannel now handles flushing a message buffer - Cleaned up DatagramChannel interface - Removed ProtocolFamily because a user can create an NIO DatagramChannel and specify it as a constructor parameter - UniqueName and UniqueKey constructors became public so that I don't need to create a subclass every time.
This commit is contained in:
parent
cd11786994
commit
c6f3b5762e
|
@ -11,11 +11,11 @@ public final class Signal extends Error {
|
||||||
private static final ConcurrentMap<String, Boolean> map =
|
private static final ConcurrentMap<String, Boolean> map =
|
||||||
new ConcurrentHashMap<String, Boolean>();
|
new ConcurrentHashMap<String, Boolean>();
|
||||||
|
|
||||||
private final SignalName uname;
|
private final UniqueName uname;
|
||||||
|
|
||||||
public Signal(String name) {
|
public Signal(String name) {
|
||||||
super(name);
|
super(name);
|
||||||
uname = new SignalName(name);
|
uname = new UniqueName(map, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void expect(Signal signal) {
|
public void expect(Signal signal) {
|
||||||
|
@ -38,10 +38,4 @@ public final class Signal extends Error {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return uname.name();
|
return uname.name();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SignalName extends UniqueName {
|
|
||||||
protected SignalName(String name) {
|
|
||||||
super(map, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ public class UniqueKey<T> extends UniqueName {
|
||||||
private final Class<T> valueType;
|
private final Class<T> valueType;
|
||||||
private final String strVal;
|
private final String strVal;
|
||||||
|
|
||||||
protected UniqueKey(ConcurrentMap<String, Boolean> map, String name, Class<T> valueType) {
|
public UniqueKey(ConcurrentMap<String, Boolean> map, String name, Class<T> valueType) {
|
||||||
super(map, name, valueType);
|
super(map, name, valueType);
|
||||||
this.valueType = valueType;
|
this.valueType = valueType;
|
||||||
strVal = name + '[' + valueType.getSimpleName() + ']';
|
strVal = name + '[' + valueType.getSimpleName() + ']';
|
||||||
|
|
|
@ -10,7 +10,7 @@ public class UniqueName implements Comparable<UniqueName> {
|
||||||
private final int id;
|
private final int id;
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
protected UniqueName(ConcurrentMap<String, Boolean> map, String name, Object... args) {
|
public UniqueName(ConcurrentMap<String, Boolean> map, String name, Object... args) {
|
||||||
if (map == null) {
|
if (map == null) {
|
||||||
throw new NullPointerException("map");
|
throw new NullPointerException("map");
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import java.io.IOException;
|
||||||
import java.net.ConnectException;
|
import java.net.ConnectException;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
import java.nio.channels.ClosedChannelException;
|
import java.nio.channels.ClosedChannelException;
|
||||||
|
import java.util.Queue;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
@ -715,20 +716,11 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
||||||
private void flush0() {
|
private void flush0() {
|
||||||
// Perform outbound I/O.
|
// Perform outbound I/O.
|
||||||
try {
|
try {
|
||||||
for (int i = config().getWriteSpinCount() - 1; i >= 0; i --) {
|
ChannelBufferHolder<Object> out = out();
|
||||||
int localFlushedAmount = doFlush(i == 0);
|
if (out.hasByteBuffer()) {
|
||||||
if (localFlushedAmount > 0) {
|
flushByteBuf(out.byteBuffer());
|
||||||
flushedAmount += localFlushedAmount;
|
} else {
|
||||||
notifyFlushFutures();
|
flushMessageBuf(out.messageBuffer());
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (out().isEmpty()) {
|
|
||||||
// Reset reader/writerIndex to 0 if the buffer is empty.
|
|
||||||
if (out().hasByteBuffer()) {
|
|
||||||
out().byteBuffer().clear();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
notifyFlushFutures(t);
|
notifyFlushFutures(t);
|
||||||
|
@ -741,6 +733,42 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void flushByteBuf(ChannelBuffer buf) throws Exception {
|
||||||
|
for (int i = config().getWriteSpinCount() - 1; i >= 0; i --) {
|
||||||
|
int localFlushedAmount = doFlush(i == 0);
|
||||||
|
if (localFlushedAmount > 0) {
|
||||||
|
flushedAmount += localFlushedAmount;
|
||||||
|
notifyFlushFutures();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!buf.readable()) {
|
||||||
|
// Reset reader/writerIndex to 0 if the buffer is empty.
|
||||||
|
buf.clear();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void flushMessageBuf(Queue<Object> buf) throws Exception {
|
||||||
|
final int writeSpinCount = config().getWriteSpinCount() - 1;
|
||||||
|
while (!buf.isEmpty()) {
|
||||||
|
boolean wrote = false;
|
||||||
|
for (int i = writeSpinCount; i >= 0; i --) {
|
||||||
|
int localFlushedAmount = doFlush(i == 0);
|
||||||
|
if (localFlushedAmount > 0) {
|
||||||
|
flushedAmount += localFlushedAmount;
|
||||||
|
wrote = true;
|
||||||
|
notifyFlushFutures();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wrote) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void notifyFlushFutures() {
|
private void notifyFlushFutures() {
|
||||||
FlushFutureEntry e = flushFuture;
|
FlushFutureEntry e = flushFuture;
|
||||||
if (e == null) {
|
if (e == null) {
|
||||||
|
|
|
@ -38,22 +38,63 @@ public interface DatagramChannel extends Channel {
|
||||||
/**
|
/**
|
||||||
* Joins a multicast group.
|
* Joins a multicast group.
|
||||||
*/
|
*/
|
||||||
void joinGroup(InetAddress multicastAddress, ChannelFuture future);
|
ChannelFuture joinGroup(InetAddress multicastAddress);
|
||||||
|
ChannelFuture joinGroup(InetAddress multicastAddress, ChannelFuture future);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Joins the specified multicast group at the specified interface.
|
* Joins the specified multicast group at the specified interface.
|
||||||
*/
|
*/
|
||||||
void joinGroup(InetSocketAddress multicastAddress, NetworkInterface networkInterface, ChannelFuture future);
|
ChannelFuture joinGroup(InetSocketAddress multicastAddress, NetworkInterface networkInterface);
|
||||||
|
ChannelFuture joinGroup(InetSocketAddress multicastAddress, NetworkInterface networkInterface, ChannelFuture future);
|
||||||
|
|
||||||
void joinGroup(InetAddress multicastAddress, NetworkInterface networkInterface, InetAddress source, ChannelFuture future);
|
ChannelFuture joinGroup(InetAddress multicastAddress, NetworkInterface networkInterface, InetAddress source);
|
||||||
|
ChannelFuture joinGroup(InetAddress multicastAddress, NetworkInterface networkInterface, InetAddress source, ChannelFuture future);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Leaves a multicast group.
|
* Leaves a multicast group.
|
||||||
*/
|
*/
|
||||||
void leaveGroup(InetAddress multicastAddress, ChannelFuture future);
|
ChannelFuture leaveGroup(InetAddress multicastAddress);
|
||||||
|
ChannelFuture leaveGroup(InetAddress multicastAddress, ChannelFuture future);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Leaves a multicast group on a specified local interface.
|
* Leaves a multicast group on a specified local interface.
|
||||||
*/
|
*/
|
||||||
void leaveGroup(InetSocketAddress multicastAddress, NetworkInterface networkInterface, ChannelFuture future);
|
ChannelFuture leaveGroup(InetSocketAddress multicastAddress, NetworkInterface networkInterface);
|
||||||
|
ChannelFuture leaveGroup(InetSocketAddress multicastAddress, NetworkInterface networkInterface, ChannelFuture future);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Leave the specified multicast group at the specified interface using the specified source.
|
||||||
|
*/
|
||||||
|
ChannelFuture leaveGroup(
|
||||||
|
InetAddress multicastAddress, NetworkInterface networkInterface, InetAddress source);
|
||||||
|
ChannelFuture leaveGroup(
|
||||||
|
InetAddress multicastAddress, NetworkInterface networkInterface, InetAddress source,
|
||||||
|
ChannelFuture future);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Block the given sourceToBlock address for the given multicastAddress on the given networkInterface
|
||||||
|
*/
|
||||||
|
ChannelFuture block(
|
||||||
|
InetAddress multicastAddress, NetworkInterface networkInterface,
|
||||||
|
InetAddress sourceToBlock);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Block the given sourceToBlock address for the given multicastAddress on the given networkInterface
|
||||||
|
*/
|
||||||
|
ChannelFuture block(
|
||||||
|
InetAddress multicastAddress, NetworkInterface networkInterface,
|
||||||
|
InetAddress sourceToBlock, ChannelFuture future);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Block the given sourceToBlock address for the given multicastAddress
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
ChannelFuture block(InetAddress multicastAddress, InetAddress sourceToBlock);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Block the given sourceToBlock address for the given multicastAddress
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
ChannelFuture block(
|
||||||
|
InetAddress multicastAddress, InetAddress sourceToBlock, ChannelFuture future);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
package io.netty.channel.socket;
|
||||||
|
|
||||||
|
import io.netty.buffer.ChannelBuffer;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
|
||||||
|
public class DatagramPacket {
|
||||||
|
|
||||||
|
private final ChannelBuffer data;
|
||||||
|
private final InetSocketAddress remoteAddress;
|
||||||
|
|
||||||
|
public DatagramPacket(ChannelBuffer data, InetSocketAddress remoteAddress) {
|
||||||
|
if (data == null) {
|
||||||
|
throw new NullPointerException("data");
|
||||||
|
}
|
||||||
|
if (remoteAddress == null) {
|
||||||
|
throw new NullPointerException("remoteAddress");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.data = data;
|
||||||
|
this.remoteAddress = remoteAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChannelBuffer data() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InetSocketAddress remoteAddress() {
|
||||||
|
return remoteAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "datagram(" + data.readableBytes() + "B, " + remoteAddress + ')';
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ import io.netty.channel.AbstractChannel;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.EventLoop;
|
import io.netty.channel.EventLoop;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
import java.nio.channels.SelectableChannel;
|
import java.nio.channels.SelectableChannel;
|
||||||
import java.nio.channels.SelectionKey;
|
import java.nio.channels.SelectionKey;
|
||||||
|
|
||||||
|
@ -32,6 +33,16 @@ public abstract class AbstractNioChannel extends AbstractChannel {
|
||||||
this.ch = ch;
|
this.ch = ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InetSocketAddress localAddress() {
|
||||||
|
return (InetSocketAddress) super.localAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InetSocketAddress remoteAddress() {
|
||||||
|
return (InetSocketAddress) super.remoteAddress();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected SelectableChannel javaChannel() {
|
protected SelectableChannel javaChannel() {
|
||||||
return ch;
|
return ch;
|
||||||
|
|
|
@ -15,14 +15,15 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.channel.socket.nio;
|
package io.netty.channel.socket.nio;
|
||||||
|
|
||||||
import static io.netty.channel.Channels.fireChannelOpen;
|
import io.netty.buffer.ChannelBuffers;
|
||||||
|
import io.netty.channel.ChannelBufferHolder;
|
||||||
|
import io.netty.channel.ChannelBufferHolders;
|
||||||
import io.netty.channel.ChannelException;
|
import io.netty.channel.ChannelException;
|
||||||
import io.netty.channel.ChannelFactory;
|
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
import io.netty.channel.ChannelSink;
|
|
||||||
import io.netty.channel.Channels;
|
|
||||||
import io.netty.channel.socket.DatagramChannelConfig;
|
import io.netty.channel.socket.DatagramChannelConfig;
|
||||||
|
import io.netty.channel.socket.DatagramPacket;
|
||||||
|
import io.netty.logging.InternalLogger;
|
||||||
|
import io.netty.logging.InternalLoggerFactory;
|
||||||
import io.netty.util.internal.DetectionUtil;
|
import io.netty.util.internal.DetectionUtil;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -31,151 +32,257 @@ import java.net.InetSocketAddress;
|
||||||
import java.net.NetworkInterface;
|
import java.net.NetworkInterface;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.DatagramChannel;
|
import java.nio.channels.DatagramChannel;
|
||||||
import java.nio.channels.MembershipKey;
|
import java.nio.channels.MembershipKey;
|
||||||
|
import java.nio.channels.SelectionKey;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Queue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides an NIO based {@link io.netty.channel.socket.DatagramChannel}.
|
* Provides an NIO based {@link io.netty.channel.socket.DatagramChannel}.
|
||||||
*/
|
*/
|
||||||
public final class NioDatagramChannel extends AbstractNioChannel implements io.netty.channel.socket.DatagramChannel {
|
public final class NioDatagramChannel extends AbstractNioChannel implements io.netty.channel.socket.DatagramChannel {
|
||||||
|
|
||||||
/**
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(NioDatagramChannel.class);
|
||||||
* The supported ProtocolFamily by UDP
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public enum ProtocolFamily {
|
|
||||||
INET,
|
|
||||||
INET6
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* The {@link DatagramChannelConfig}.
|
|
||||||
*/
|
|
||||||
private final NioDatagramChannelConfig config;
|
|
||||||
private Map<InetAddress, List<MembershipKey>> memberships;
|
|
||||||
|
|
||||||
static NioDatagramChannel create(ChannelFactory factory,
|
|
||||||
ChannelPipeline pipeline, ChannelSink sink, NioDatagramWorker worker, ProtocolFamily family) {
|
|
||||||
NioDatagramChannel instance =
|
|
||||||
new NioDatagramChannel(factory, pipeline, sink, worker, family);
|
|
||||||
fireChannelOpen(instance);
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private NioDatagramChannel(final ChannelFactory factory,
|
private final DatagramChannelConfig config;
|
||||||
final ChannelPipeline pipeline, final ChannelSink sink,
|
private final Map<InetAddress, List<MembershipKey>> memberships =
|
||||||
final NioDatagramWorker worker, ProtocolFamily family) {
|
new HashMap<InetAddress, List<MembershipKey>>();
|
||||||
super(null, factory, pipeline, sink, worker, new NioDatagramJdkChannel(openNonBlockingChannel(family)));
|
private final ChannelBufferHolder<Object> out = ChannelBufferHolders.messageBuffer();
|
||||||
config = new DefaultNioDatagramChannelConfig(getJdkChannel().getChannel());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static DatagramChannel openNonBlockingChannel(ProtocolFamily family) {
|
private static DatagramChannel newSocket() {
|
||||||
try {
|
try {
|
||||||
final DatagramChannel channel;
|
return DatagramChannel.open();
|
||||||
|
} catch (IOException e) {
|
||||||
// check if we are on java 7 or if the family was not specified
|
throw new ChannelException("Failed to open a socket.", e);
|
||||||
if (DetectionUtil.javaVersion() < 7 || family == null) {
|
|
||||||
channel = DatagramChannel.open();
|
|
||||||
} else {
|
|
||||||
// This block only works on java7++, but we checked before if we have it
|
|
||||||
switch (family) {
|
|
||||||
case INET:
|
|
||||||
channel = DatagramChannel.open(java.net.StandardProtocolFamily.INET);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case INET6:
|
|
||||||
channel = DatagramChannel.open(java.net.StandardProtocolFamily.INET6);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
channel.configureBlocking(false);
|
|
||||||
return channel;
|
|
||||||
} catch (final IOException e) {
|
|
||||||
throw new ChannelException("Failed to open a DatagramChannel.", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public NioDatagramChannel() {
|
||||||
|
this(newSocket());
|
||||||
|
}
|
||||||
|
|
||||||
|
public NioDatagramChannel(DatagramChannel socket) {
|
||||||
|
this(null, socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NioDatagramChannel(Integer id, DatagramChannel socket) {
|
||||||
|
super(null, id, socket);
|
||||||
|
try {
|
||||||
|
socket.configureBlocking(false);
|
||||||
|
} catch (IOException e) {
|
||||||
|
try {
|
||||||
|
socket.close();
|
||||||
|
} catch (IOException e2) {
|
||||||
|
if (logger.isWarnEnabled()) {
|
||||||
|
logger.warn(
|
||||||
|
"Failed to close a partially initialized socket.", e2);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ChannelException("Failed to enter non-blocking mode.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
config = new DefaultNioDatagramChannelConfig(socket);
|
||||||
|
|
||||||
@Override
|
|
||||||
protected NioDatagramJdkChannel getJdkChannel() {
|
|
||||||
return (NioDatagramJdkChannel) super.getJdkChannel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NioDatagramWorker getWorker() {
|
public DatagramChannelConfig config() {
|
||||||
return (NioDatagramWorker) super.getWorker();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isBound() {
|
|
||||||
return isOpen() && getJdkChannel().isSocketBound();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isConnected() {
|
|
||||||
return getJdkChannel().isConnected();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean setClosed() {
|
|
||||||
return super.setClosed();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public NioDatagramChannelConfig getConfig() {
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture joinGroup(InetAddress multicastAddress) {
|
public boolean isActive() {
|
||||||
try {
|
DatagramChannel ch = javaChannel();
|
||||||
return joinGroup(multicastAddress, NetworkInterface.getByInetAddress(getLocalAddress().getAddress()), null);
|
return ch.isOpen() && ch.socket().isBound();
|
||||||
} catch (SocketException e) {
|
}
|
||||||
return Channels.failedFuture(this, e);
|
|
||||||
|
@Override
|
||||||
|
protected DatagramChannel javaChannel() {
|
||||||
|
return (DatagramChannel) super.javaChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ChannelBufferHolder<Object> firstOut() {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SocketAddress localAddress0() {
|
||||||
|
return javaChannel().socket().getLocalSocketAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SocketAddress remoteAddress0() {
|
||||||
|
return javaChannel().socket().getRemoteSocketAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doBind(SocketAddress localAddress) throws Exception {
|
||||||
|
javaChannel().bind(localAddress);
|
||||||
|
selectionKey().interestOps(SelectionKey.OP_READ);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean doConnect(SocketAddress remoteAddress,
|
||||||
|
SocketAddress localAddress) throws Exception {
|
||||||
|
if (localAddress != null) {
|
||||||
|
javaChannel().socket().bind(localAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean success = false;
|
||||||
|
try {
|
||||||
|
javaChannel().connect(remoteAddress);
|
||||||
|
selectionKey().interestOps(selectionKey().interestOps() | SelectionKey.OP_READ);
|
||||||
|
success = true;
|
||||||
|
return true;
|
||||||
|
} finally {
|
||||||
|
if (!success) {
|
||||||
|
doClose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture joinGroup(InetSocketAddress multicastAddress, NetworkInterface networkInterface) {
|
protected void doFinishConnect() throws Exception {
|
||||||
return joinGroup(multicastAddress.getAddress(), networkInterface, null);
|
throw new Error();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Joins the specified multicast group at the specified interface using the specified source.
|
protected void doDisconnect() throws Exception {
|
||||||
*/
|
javaChannel().disconnect();
|
||||||
public ChannelFuture joinGroup(InetAddress multicastAddress, NetworkInterface networkInterface, InetAddress source) {
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doClose() throws Exception {
|
||||||
|
javaChannel().close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doDeregister() throws Exception {
|
||||||
|
selectionKey().cancel();
|
||||||
|
((SingleThreadSelectorEventLoop) eventLoop()).cancelledKeys ++;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int doRead(ChannelBufferHolder<Object> buf) throws Exception {
|
||||||
|
DatagramChannel ch = javaChannel();
|
||||||
|
ByteBuffer data = ByteBuffer.allocate(1024);
|
||||||
|
InetSocketAddress remoteAddress = (InetSocketAddress) ch.receive(data);
|
||||||
|
if (remoteAddress == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.flip();
|
||||||
|
buf.messageBuffer().add(new DatagramPacket(ChannelBuffers.wrappedBuffer(data), remoteAddress));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int doFlush(boolean lastSpin) throws Exception {
|
||||||
|
final Queue<Object> buf = unsafe().out().messageBuffer();
|
||||||
|
if (buf.isEmpty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DatagramPacket packet = (DatagramPacket) buf.peek();
|
||||||
|
final int writtenBytes = javaChannel().send(packet.data().toByteBuffer(), packet.remoteAddress());
|
||||||
|
|
||||||
|
final SelectionKey key = selectionKey();
|
||||||
|
final int interestOps = key.interestOps();
|
||||||
|
if (writtenBytes <= 0) {
|
||||||
|
// Did not write a packet.
|
||||||
|
// 1) If 'lastSpin' is false, the caller will call this method again real soon.
|
||||||
|
// - Do not update OP_WRITE.
|
||||||
|
// 2) If 'lastSpin' is true, the caller will not retry.
|
||||||
|
// - Set OP_WRITE so that the event loop calls flushForcibly() later.
|
||||||
|
if (lastSpin) {
|
||||||
|
if ((interestOps & SelectionKey.OP_WRITE) == 0) {
|
||||||
|
key.interestOps(interestOps | SelectionKey.OP_WRITE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrote a packet.
|
||||||
|
buf.remove();
|
||||||
|
if (buf.isEmpty()) {
|
||||||
|
// Wrote the outbound buffer completely - clear OP_WRITE.
|
||||||
|
if ((interestOps & SelectionKey.OP_WRITE) != 0) {
|
||||||
|
key.interestOps(interestOps & ~SelectionKey.OP_WRITE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChannelFuture joinGroup(InetAddress multicastAddress) {
|
||||||
|
return joinGroup(multicastAddress, newFuture());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChannelFuture joinGroup(InetAddress multicastAddress, ChannelFuture future) {
|
||||||
|
try {
|
||||||
|
return joinGroup(
|
||||||
|
multicastAddress,
|
||||||
|
NetworkInterface.getByInetAddress(localAddress().getAddress()),
|
||||||
|
null, future);
|
||||||
|
} catch (SocketException e) {
|
||||||
|
future.setFailure(e);
|
||||||
|
}
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChannelFuture joinGroup(
|
||||||
|
InetSocketAddress multicastAddress, NetworkInterface networkInterface) {
|
||||||
|
return joinGroup(multicastAddress, networkInterface, newFuture());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChannelFuture joinGroup(
|
||||||
|
InetSocketAddress multicastAddress, NetworkInterface networkInterface,
|
||||||
|
ChannelFuture future) {
|
||||||
|
return joinGroup(multicastAddress.getAddress(), networkInterface, null, future);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChannelFuture joinGroup(
|
||||||
|
InetAddress multicastAddress, NetworkInterface networkInterface, InetAddress source) {
|
||||||
|
return joinGroup(multicastAddress, networkInterface, source, newFuture());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChannelFuture joinGroup(
|
||||||
|
InetAddress multicastAddress, NetworkInterface networkInterface,
|
||||||
|
InetAddress source, ChannelFuture future) {
|
||||||
if (DetectionUtil.javaVersion() < 7) {
|
if (DetectionUtil.javaVersion() < 7) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
} else {
|
} else {
|
||||||
if (multicastAddress == null) {
|
if (multicastAddress == null) {
|
||||||
throw new NullPointerException("multicastAddress");
|
throw new NullPointerException("multicastAddress");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (networkInterface == null) {
|
if (networkInterface == null) {
|
||||||
throw new NullPointerException("networkInterface");
|
throw new NullPointerException("networkInterface");
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
MembershipKey key;
|
MembershipKey key;
|
||||||
if (source == null) {
|
if (source == null) {
|
||||||
key = getJdkChannel().getChannel().join(multicastAddress, networkInterface);
|
key = javaChannel().join(multicastAddress, networkInterface);
|
||||||
} else {
|
} else {
|
||||||
key = getJdkChannel().getChannel().join(multicastAddress, networkInterface, source);
|
key = javaChannel().join(multicastAddress, networkInterface, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (memberships == null) {
|
|
||||||
memberships = new HashMap<InetAddress, List<MembershipKey>>();
|
|
||||||
|
|
||||||
}
|
|
||||||
List<MembershipKey> keys = memberships.get(multicastAddress);
|
List<MembershipKey> keys = memberships.get(multicastAddress);
|
||||||
if (keys == null) {
|
if (keys == null) {
|
||||||
keys = new ArrayList<MembershipKey>();
|
keys = new ArrayList<MembershipKey>();
|
||||||
|
@ -183,77 +290,106 @@ public final class NioDatagramChannel extends AbstractNioChannel implements io.n
|
||||||
}
|
}
|
||||||
keys.add(key);
|
keys.add(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
future.setSuccess();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
return Channels.failedFuture(this, e);
|
future.setFailure(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Channels.succeededFuture(this);
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture leaveGroup(InetAddress multicastAddress) {
|
public ChannelFuture leaveGroup(InetAddress multicastAddress) {
|
||||||
try {
|
return leaveGroup(multicastAddress, newFuture());
|
||||||
return leaveGroup(multicastAddress, NetworkInterface.getByInetAddress(getLocalAddress().getAddress()), null);
|
|
||||||
} catch (SocketException e) {
|
|
||||||
return Channels.failedFuture(this, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture leaveGroup(InetSocketAddress multicastAddress,
|
public ChannelFuture leaveGroup(InetAddress multicastAddress, ChannelFuture future) {
|
||||||
NetworkInterface networkInterface) {
|
try {
|
||||||
return leaveGroup(multicastAddress.getAddress(), networkInterface, null);
|
return leaveGroup(multicastAddress, NetworkInterface.getByInetAddress(localAddress().getAddress()), null, future);
|
||||||
|
} catch (SocketException e) {
|
||||||
|
future.setFailure(e);
|
||||||
|
}
|
||||||
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Leave the specified multicast group at the specified interface using the specified source.
|
public ChannelFuture leaveGroup(
|
||||||
*/
|
InetSocketAddress multicastAddress, NetworkInterface networkInterface) {
|
||||||
public ChannelFuture leaveGroup(InetAddress multicastAddress,
|
return leaveGroup(multicastAddress, networkInterface, newFuture());
|
||||||
NetworkInterface networkInterface, InetAddress source) {
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChannelFuture leaveGroup(
|
||||||
|
InetSocketAddress multicastAddress,
|
||||||
|
NetworkInterface networkInterface, ChannelFuture future) {
|
||||||
|
return leaveGroup(multicastAddress.getAddress(), networkInterface, null, future);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChannelFuture leaveGroup(
|
||||||
|
InetAddress multicastAddress, NetworkInterface networkInterface, InetAddress source) {
|
||||||
|
return leaveGroup(multicastAddress, networkInterface, source, newFuture());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChannelFuture leaveGroup(
|
||||||
|
InetAddress multicastAddress, NetworkInterface networkInterface, InetAddress source,
|
||||||
|
ChannelFuture future) {
|
||||||
if (DetectionUtil.javaVersion() < 7) {
|
if (DetectionUtil.javaVersion() < 7) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
} else {
|
}
|
||||||
if (multicastAddress == null) {
|
if (multicastAddress == null) {
|
||||||
throw new NullPointerException("multicastAddress");
|
throw new NullPointerException("multicastAddress");
|
||||||
}
|
}
|
||||||
|
if (networkInterface == null) {
|
||||||
if (networkInterface == null) {
|
throw new NullPointerException("networkInterface");
|
||||||
throw new NullPointerException("networkInterface");
|
}
|
||||||
}
|
|
||||||
|
synchronized (this) {
|
||||||
synchronized (this) {
|
if (memberships != null) {
|
||||||
if (memberships != null) {
|
List<MembershipKey> keys = memberships.get(multicastAddress);
|
||||||
List<MembershipKey> keys = memberships.get(multicastAddress);
|
if (keys != null) {
|
||||||
if (keys != null) {
|
Iterator<MembershipKey> keyIt = keys.iterator();
|
||||||
Iterator<MembershipKey> keyIt = keys.iterator();
|
|
||||||
|
while (keyIt.hasNext()) {
|
||||||
while (keyIt.hasNext()) {
|
MembershipKey key = keyIt.next();
|
||||||
MembershipKey key = keyIt.next();
|
if (networkInterface.equals(key.networkInterface())) {
|
||||||
if (networkInterface.equals(key.networkInterface())) {
|
if (source == null && key.sourceAddress() == null || source != null && source.equals(key.sourceAddress())) {
|
||||||
if (source == null && key.sourceAddress() == null || (source != null && source.equals(key.sourceAddress()))) {
|
key.drop();
|
||||||
key.drop();
|
keyIt.remove();
|
||||||
keyIt.remove();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (keys.isEmpty()) {
|
|
||||||
memberships.remove(multicastAddress);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (keys.isEmpty()) {
|
||||||
|
memberships.remove(multicastAddress);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Channels.succeededFuture(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
future.setSuccess();
|
||||||
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Block the given sourceToBlock address for the given multicastAddress on the given networkInterface
|
* Block the given sourceToBlock address for the given multicastAddress on the given networkInterface
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public ChannelFuture block(InetAddress multicastAddress,
|
@Override
|
||||||
NetworkInterface networkInterface, InetAddress sourceToBlock) {
|
public ChannelFuture block(
|
||||||
|
InetAddress multicastAddress, NetworkInterface networkInterface,
|
||||||
|
InetAddress sourceToBlock) {
|
||||||
|
return block(multicastAddress, networkInterface, sourceToBlock, newFuture());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Block the given sourceToBlock address for the given multicastAddress on the given networkInterface
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public ChannelFuture block(
|
||||||
|
InetAddress multicastAddress, NetworkInterface networkInterface,
|
||||||
|
InetAddress sourceToBlock, ChannelFuture future) {
|
||||||
if (DetectionUtil.javaVersion() < 7) {
|
if (DetectionUtil.javaVersion() < 7) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
} else {
|
} else {
|
||||||
|
@ -263,7 +399,7 @@ public final class NioDatagramChannel extends AbstractNioChannel implements io.n
|
||||||
if (sourceToBlock == null) {
|
if (sourceToBlock == null) {
|
||||||
throw new NullPointerException("sourceToBlock");
|
throw new NullPointerException("sourceToBlock");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (networkInterface == null) {
|
if (networkInterface == null) {
|
||||||
throw new NullPointerException("networkInterface");
|
throw new NullPointerException("networkInterface");
|
||||||
}
|
}
|
||||||
|
@ -275,39 +411,41 @@ public final class NioDatagramChannel extends AbstractNioChannel implements io.n
|
||||||
try {
|
try {
|
||||||
key.block(sourceToBlock);
|
key.block(sourceToBlock);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
return Channels.failedFuture(this, e);
|
future.setFailure(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Channels.succeededFuture(this);
|
future.setSuccess();
|
||||||
|
return future;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Block the given sourceToBlock address for the given multicastAddress
|
* Block the given sourceToBlock address for the given multicastAddress
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public ChannelFuture block(InetAddress multicastAddress, InetAddress sourceToBlock) {
|
|
||||||
try {
|
|
||||||
block(multicastAddress, NetworkInterface.getByInetAddress(getLocalAddress().getAddress()), sourceToBlock);
|
|
||||||
} catch (SocketException e) {
|
|
||||||
return Channels.failedFuture(this, e);
|
|
||||||
}
|
|
||||||
return Channels.succeededFuture(this);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture write(Object message, SocketAddress remoteAddress) {
|
public ChannelFuture block(InetAddress multicastAddress, InetAddress sourceToBlock) {
|
||||||
if (remoteAddress == null || remoteAddress.equals(getRemoteAddress())) {
|
return block(multicastAddress, sourceToBlock, newFuture());
|
||||||
return super.write(message, null);
|
}
|
||||||
} else {
|
|
||||||
return super.write(message, remoteAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Block the given sourceToBlock address for the given multicastAddress
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public ChannelFuture block(
|
||||||
|
InetAddress multicastAddress, InetAddress sourceToBlock, ChannelFuture future) {
|
||||||
|
try {
|
||||||
|
return block(
|
||||||
|
multicastAddress,
|
||||||
|
NetworkInterface.getByInetAddress(localAddress().getAddress()),
|
||||||
|
sourceToBlock, future);
|
||||||
|
} catch (SocketException e) {
|
||||||
|
future.setFailure(e);
|
||||||
|
}
|
||||||
|
return future;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,169 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2011 The Netty Project
|
|
||||||
*
|
|
||||||
* The Netty Project licenses this file to you 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 static io.netty.channel.Channels.*;
|
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.net.SocketAddress;
|
|
||||||
|
|
||||||
import io.netty.channel.ChannelEvent;
|
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
import io.netty.channel.ChannelState;
|
|
||||||
import io.netty.channel.ChannelStateEvent;
|
|
||||||
import io.netty.channel.MessageEvent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Receives downstream events from a {@link ChannelPipeline}. It contains
|
|
||||||
* an array of I/O workers.
|
|
||||||
*/
|
|
||||||
class NioDatagramPipelineSink extends AbstractNioChannelSink {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle downstream event.
|
|
||||||
*
|
|
||||||
* @param pipeline the {@link ChannelPipeline} that passes down the
|
|
||||||
* downstream event.
|
|
||||||
* @param e The downstream event.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void eventSunk(final ChannelPipeline pipeline, final ChannelEvent e)
|
|
||||||
throws Exception {
|
|
||||||
final NioDatagramChannel channel = (NioDatagramChannel) e.getChannel();
|
|
||||||
final ChannelFuture future = e.getFuture();
|
|
||||||
if (e instanceof ChannelStateEvent) {
|
|
||||||
final ChannelStateEvent stateEvent = (ChannelStateEvent) e;
|
|
||||||
final ChannelState state = stateEvent.getState();
|
|
||||||
final Object value = stateEvent.getValue();
|
|
||||||
switch (state) {
|
|
||||||
case OPEN:
|
|
||||||
if (Boolean.FALSE.equals(value)) {
|
|
||||||
channel.getWorker().close(channel, future);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case BOUND:
|
|
||||||
if (value != null) {
|
|
||||||
bind(channel, future, (InetSocketAddress) value);
|
|
||||||
} else {
|
|
||||||
channel.getWorker().close(channel, future);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CONNECTED:
|
|
||||||
if (value != null) {
|
|
||||||
connect(channel, future, (InetSocketAddress) value);
|
|
||||||
} else {
|
|
||||||
channel.getWorker().disconnect(channel, future);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case INTEREST_OPS:
|
|
||||||
channel.getWorker().setInterestOps(channel, future, ((Integer) value).intValue());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (e instanceof MessageEvent) {
|
|
||||||
final MessageEvent event = (MessageEvent) e;
|
|
||||||
final boolean offered = channel.writeBufferQueue.offer(event);
|
|
||||||
assert offered;
|
|
||||||
channel.getWorker().writeFromUserCode(channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void close(NioDatagramChannel channel, ChannelFuture future) {
|
|
||||||
try {
|
|
||||||
channel.getJdkChannel().closeSocket();
|
|
||||||
if (channel.setClosed()) {
|
|
||||||
future.setSuccess();
|
|
||||||
if (channel.isBound()) {
|
|
||||||
fireChannelUnbound(channel);
|
|
||||||
}
|
|
||||||
fireChannelClosed(channel);
|
|
||||||
} else {
|
|
||||||
future.setSuccess();
|
|
||||||
}
|
|
||||||
} catch (final Throwable t) {
|
|
||||||
future.setFailure(t);
|
|
||||||
fireExceptionCaught(channel, t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Will bind the DatagramSocket to the passed-in address.
|
|
||||||
* Every call bind will spawn a new thread using the that basically in turn
|
|
||||||
*/
|
|
||||||
private void bind(final NioDatagramChannel channel,
|
|
||||||
final ChannelFuture future, final InetSocketAddress address) {
|
|
||||||
boolean bound = false;
|
|
||||||
boolean started = false;
|
|
||||||
try {
|
|
||||||
// First bind the DatagramSocket the specified port.
|
|
||||||
channel.getJdkChannel().bind(address);
|
|
||||||
bound = true;
|
|
||||||
|
|
||||||
future.setSuccess();
|
|
||||||
fireChannelBound(channel, address);
|
|
||||||
|
|
||||||
channel.getWorker().registerWithWorker(channel, null);
|
|
||||||
started = true;
|
|
||||||
} catch (final Throwable t) {
|
|
||||||
future.setFailure(t);
|
|
||||||
fireExceptionCaught(channel, t);
|
|
||||||
} finally {
|
|
||||||
if (!started && bound) {
|
|
||||||
close(channel, future);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void connect(
|
|
||||||
NioDatagramChannel channel, ChannelFuture future,
|
|
||||||
SocketAddress remoteAddress) {
|
|
||||||
|
|
||||||
boolean bound = channel.isBound();
|
|
||||||
boolean connected = false;
|
|
||||||
boolean workerStarted = false;
|
|
||||||
|
|
||||||
future.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
|
|
||||||
|
|
||||||
// Clear the cached address so that the next getRemoteAddress() call
|
|
||||||
// updates the cache.
|
|
||||||
channel.remoteAddress = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
channel.getJdkChannel().connect(remoteAddress);
|
|
||||||
connected = true;
|
|
||||||
|
|
||||||
// Fire events.
|
|
||||||
future.setSuccess();
|
|
||||||
if (!bound) {
|
|
||||||
fireChannelBound(channel, channel.getLocalAddress());
|
|
||||||
}
|
|
||||||
fireChannelConnected(channel, channel.getRemoteAddress());
|
|
||||||
|
|
||||||
if (!bound) {
|
|
||||||
channel.getWorker().registerWithWorker(channel, future);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Throwable t) {
|
|
||||||
future.setFailure(t);
|
|
||||||
fireExceptionCaught(channel, t);
|
|
||||||
} finally {
|
|
||||||
if (connected && !workerStarted) {
|
|
||||||
channel.getWorker().close(channel, future);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,303 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2011 The Netty Project
|
|
||||||
*
|
|
||||||
* The Netty Project licenses this file to you 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 static io.netty.channel.Channels.fireChannelDisconnected;
|
|
||||||
import static io.netty.channel.Channels.fireChannelDisconnectedLater;
|
|
||||||
import static io.netty.channel.Channels.fireExceptionCaught;
|
|
||||||
import static io.netty.channel.Channels.fireExceptionCaughtLater;
|
|
||||||
import static io.netty.channel.Channels.fireMessageReceived;
|
|
||||||
import static io.netty.channel.Channels.succeededFuture;
|
|
||||||
import io.netty.buffer.ChannelBufferFactory;
|
|
||||||
import io.netty.channel.ChannelException;
|
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.Channels;
|
|
||||||
import io.netty.channel.MessageEvent;
|
|
||||||
import io.netty.channel.ReceiveBufferSizePredictor;
|
|
||||||
import io.netty.channel.socket.nio.SendBufferPool.SendBuffer;
|
|
||||||
|
|
||||||
import java.net.SocketAddress;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.AsynchronousCloseException;
|
|
||||||
import java.nio.channels.ClosedChannelException;
|
|
||||||
import java.nio.channels.DatagramChannel;
|
|
||||||
import java.nio.channels.SelectionKey;
|
|
||||||
import java.nio.channels.Selector;
|
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A class responsible for registering channels with {@link Selector}.
|
|
||||||
* It also implements the {@link Selector} loop.
|
|
||||||
*/
|
|
||||||
public class NioDatagramWorker extends SingleThreadSelectorEventLoop {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sole constructor.
|
|
||||||
*
|
|
||||||
* @param executor the {@link Executor} used to execute {@link Runnable}s
|
|
||||||
* such as {@link ChannelRegistionTask}
|
|
||||||
*/
|
|
||||||
NioDatagramWorker(final Executor executor) {
|
|
||||||
super(executor);
|
|
||||||
}
|
|
||||||
|
|
||||||
NioDatagramWorker(final Executor executor, boolean allowShutdownOnIdle) {
|
|
||||||
super(executor, allowShutdownOnIdle);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean read(final SelectionKey key) {
|
|
||||||
final NioDatagramChannel channel = (NioDatagramChannel) key.attachment();
|
|
||||||
ReceiveBufferSizePredictor predictor =
|
|
||||||
channel.getConfig().getReceiveBufferSizePredictor();
|
|
||||||
final ChannelBufferFactory bufferFactory = channel.getConfig().getBufferFactory();
|
|
||||||
final DatagramChannel nioChannel = (DatagramChannel) key.channel();
|
|
||||||
|
|
||||||
// Allocating a non-direct buffer with a max udp packge size.
|
|
||||||
// Would using a direct buffer be more efficient or would this negatively
|
|
||||||
// effect performance, as direct buffer allocation has a higher upfront cost
|
|
||||||
// where as a ByteBuffer is heap allocated.
|
|
||||||
final ByteBuffer byteBuffer = ByteBuffer.allocate(
|
|
||||||
predictor.nextReceiveBufferSize()).order(bufferFactory.getDefaultOrder());
|
|
||||||
|
|
||||||
boolean failure = true;
|
|
||||||
SocketAddress remoteAddress = null;
|
|
||||||
try {
|
|
||||||
// Receive from the channel in a non blocking mode. We have already been notified that
|
|
||||||
// the channel is ready to receive.
|
|
||||||
remoteAddress = nioChannel.receive(byteBuffer);
|
|
||||||
failure = false;
|
|
||||||
} catch (ClosedChannelException e) {
|
|
||||||
// Can happen, and does not need a user attention.
|
|
||||||
} catch (Throwable t) {
|
|
||||||
fireExceptionCaught(channel, t);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remoteAddress != null) {
|
|
||||||
// Flip the buffer so that we can wrap it.
|
|
||||||
byteBuffer.flip();
|
|
||||||
|
|
||||||
int readBytes = byteBuffer.remaining();
|
|
||||||
if (readBytes > 0) {
|
|
||||||
// Update the predictor.
|
|
||||||
predictor.previousReceiveBufferSize(readBytes);
|
|
||||||
|
|
||||||
// Notify the interested parties about the newly arrived message.
|
|
||||||
fireMessageReceived(
|
|
||||||
channel, bufferFactory.getBuffer(byteBuffer), remoteAddress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (failure) {
|
|
||||||
key.cancel(); // Some JDK implementations run into an infinite loop without this.
|
|
||||||
close(channel, succeededFuture(channel));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void disconnect(NioDatagramChannel channel, ChannelFuture future) {
|
|
||||||
boolean connected = channel.isConnected();
|
|
||||||
boolean iothread = isIoThread();
|
|
||||||
try {
|
|
||||||
channel.getJdkChannel().disconnectSocket();
|
|
||||||
future.setSuccess();
|
|
||||||
if (connected) {
|
|
||||||
if (iothread) {
|
|
||||||
fireChannelDisconnected(channel);
|
|
||||||
} else {
|
|
||||||
fireChannelDisconnectedLater(channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Throwable t) {
|
|
||||||
future.setFailure(t);
|
|
||||||
if (iothread) {
|
|
||||||
fireExceptionCaught(channel, t);
|
|
||||||
} else {
|
|
||||||
fireExceptionCaughtLater(channel, t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void registerTask(AbstractNioChannel channel, ChannelFuture future) {
|
|
||||||
final SocketAddress localAddress = channel.getLocalAddress();
|
|
||||||
if (localAddress == null) {
|
|
||||||
if (future != null) {
|
|
||||||
future.setFailure(new ClosedChannelException());
|
|
||||||
}
|
|
||||||
close(channel, succeededFuture(channel));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
synchronized (channel.interestOpsLock) {
|
|
||||||
channel.getJdkChannel().register(
|
|
||||||
selector, channel.getRawInterestOps(), channel);
|
|
||||||
}
|
|
||||||
if (future != null) {
|
|
||||||
future.setSuccess();
|
|
||||||
}
|
|
||||||
} catch (final ClosedChannelException e) {
|
|
||||||
if (future != null) {
|
|
||||||
future.setFailure(e);
|
|
||||||
}
|
|
||||||
close(channel, succeededFuture(channel));
|
|
||||||
throw new ChannelException(
|
|
||||||
"Failed to register a socket to the selector.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeFromUserCode(final AbstractNioChannel channel) {
|
|
||||||
/*
|
|
||||||
* Note that we are not checking if the channel is connected. Connected
|
|
||||||
* has a different meaning in UDP and means that the channels socket is
|
|
||||||
* configured to only send and receive from a given remote peer.
|
|
||||||
*/
|
|
||||||
if (!channel.isBound()) {
|
|
||||||
cleanUpWriteBuffer(channel);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scheduleWriteIfNecessary(channel)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// From here, we are sure Thread.currentThread() == workerThread.
|
|
||||||
|
|
||||||
if (channel.writeSuspended) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (channel.inWriteNowLoop) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
write0(channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void write0(final AbstractNioChannel channel) {
|
|
||||||
|
|
||||||
boolean addOpWrite = false;
|
|
||||||
boolean removeOpWrite = false;
|
|
||||||
|
|
||||||
long writtenBytes = 0;
|
|
||||||
|
|
||||||
final SendBufferPool sendBufferPool = this.sendBufferPool;
|
|
||||||
final DatagramChannel ch = ((NioDatagramChannel) channel).getJdkChannel().getChannel();
|
|
||||||
final Queue<MessageEvent> writeBuffer = channel.writeBufferQueue;
|
|
||||||
final int writeSpinCount = channel.getConfig().getWriteSpinCount();
|
|
||||||
synchronized (channel.writeLock) {
|
|
||||||
// inform the channel that write is in-progress
|
|
||||||
channel.inWriteNowLoop = true;
|
|
||||||
|
|
||||||
// loop forever...
|
|
||||||
for (;;) {
|
|
||||||
MessageEvent evt = channel.currentWriteEvent;
|
|
||||||
SendBuffer buf;
|
|
||||||
if (evt == null) {
|
|
||||||
if ((channel.currentWriteEvent = evt = writeBuffer.poll()) == null) {
|
|
||||||
removeOpWrite = true;
|
|
||||||
channel.writeSuspended = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
channel.currentWriteBuffer = buf = sendBufferPool.acquire(evt.getMessage());
|
|
||||||
} else {
|
|
||||||
buf = channel.currentWriteBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
long localWrittenBytes = 0;
|
|
||||||
SocketAddress raddr = evt.getRemoteAddress();
|
|
||||||
if (raddr == null) {
|
|
||||||
for (int i = writeSpinCount; i > 0; i --) {
|
|
||||||
localWrittenBytes = buf.transferTo(ch);
|
|
||||||
if (localWrittenBytes != 0) {
|
|
||||||
writtenBytes += localWrittenBytes;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (buf.finished()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (int i = writeSpinCount; i > 0; i --) {
|
|
||||||
localWrittenBytes = buf.transferTo(ch, raddr);
|
|
||||||
if (localWrittenBytes != 0) {
|
|
||||||
writtenBytes += localWrittenBytes;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (buf.finished()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (localWrittenBytes > 0 || buf.finished()) {
|
|
||||||
// Successful write - proceed to the next message.
|
|
||||||
buf.release();
|
|
||||||
ChannelFuture future = evt.getFuture();
|
|
||||||
channel.currentWriteEvent = null;
|
|
||||||
channel.currentWriteBuffer = null;
|
|
||||||
evt = null;
|
|
||||||
buf = null;
|
|
||||||
future.setSuccess();
|
|
||||||
} else {
|
|
||||||
// Not written at all - perhaps the kernel buffer is full.
|
|
||||||
addOpWrite = true;
|
|
||||||
channel.writeSuspended = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} catch (final AsynchronousCloseException e) {
|
|
||||||
// Doesn't need a user attention - ignore.
|
|
||||||
} catch (final Throwable t) {
|
|
||||||
buf.release();
|
|
||||||
ChannelFuture future = evt.getFuture();
|
|
||||||
channel.currentWriteEvent = null;
|
|
||||||
channel.currentWriteBuffer = null;
|
|
||||||
buf = null;
|
|
||||||
evt = null;
|
|
||||||
future.setFailure(t);
|
|
||||||
fireExceptionCaught(channel, t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
channel.inWriteNowLoop = false;
|
|
||||||
|
|
||||||
// Initially, the following block was executed after releasing
|
|
||||||
// the writeLock, but there was a race condition, and it has to be
|
|
||||||
// executed before releasing the writeLock:
|
|
||||||
//
|
|
||||||
// https://issues.jboss.org/browse/NETTY-410
|
|
||||||
//
|
|
||||||
if (addOpWrite) {
|
|
||||||
setOpWrite(channel);
|
|
||||||
} else if (removeOpWrite) {
|
|
||||||
clearOpWrite(channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Channels.fireWriteComplete(channel, writtenBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user