234c4c70db
- Also: - Made the test case more robust - Added a simple concurrent buffer modification test (needs more work)
224 lines
7.8 KiB
Java
224 lines
7.8 KiB
Java
/*
|
|
* 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 io.netty.channel.AbstractChannel;
|
|
import io.netty.channel.Channel;
|
|
import io.netty.channel.ChannelBufferHolder;
|
|
import io.netty.channel.ChannelException;
|
|
import io.netty.channel.ChannelFuture;
|
|
import io.netty.channel.EventLoop;
|
|
import io.netty.logging.InternalLogger;
|
|
import io.netty.logging.InternalLoggerFactory;
|
|
|
|
import java.io.IOException;
|
|
import java.net.ConnectException;
|
|
import java.net.InetSocketAddress;
|
|
import java.net.SocketAddress;
|
|
import java.nio.channels.SelectableChannel;
|
|
import java.nio.channels.SelectionKey;
|
|
import java.util.concurrent.ScheduledFuture;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
public abstract class AbstractNioChannel extends AbstractChannel {
|
|
|
|
private static final InternalLogger logger =
|
|
InternalLoggerFactory.getInstance(AbstractNioChannel.class);
|
|
|
|
private final SelectableChannel ch;
|
|
private final int defaultInterestOps;
|
|
private volatile SelectionKey selectionKey;
|
|
|
|
/**
|
|
* The future of the current connection attempt. If not null, subsequent
|
|
* connection attempts will fail.
|
|
*/
|
|
private ChannelFuture connectFuture;
|
|
private ScheduledFuture<?> connectTimeoutFuture;
|
|
private ConnectException connectTimeoutException;
|
|
|
|
protected AbstractNioChannel(
|
|
Channel parent, Integer id, ChannelBufferHolder<?> outboundBuffer,
|
|
SelectableChannel ch, int defaultInterestOps) {
|
|
super(parent, id, outboundBuffer);
|
|
this.ch = ch;
|
|
this.defaultInterestOps = defaultInterestOps;
|
|
try {
|
|
ch.configureBlocking(false);
|
|
} catch (IOException e) {
|
|
try {
|
|
ch.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);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean isOpen() {
|
|
return ch.isOpen();
|
|
}
|
|
|
|
@Override
|
|
public InetSocketAddress localAddress() {
|
|
return (InetSocketAddress) super.localAddress();
|
|
}
|
|
|
|
@Override
|
|
public InetSocketAddress remoteAddress() {
|
|
return (InetSocketAddress) super.remoteAddress();
|
|
}
|
|
|
|
@Override
|
|
public NioUnsafe unsafe() {
|
|
return (NioUnsafe) super.unsafe();
|
|
}
|
|
|
|
protected SelectableChannel javaChannel() {
|
|
return ch;
|
|
}
|
|
|
|
protected SelectionKey selectionKey() {
|
|
assert selectionKey != null;
|
|
return selectionKey;
|
|
}
|
|
|
|
public interface NioUnsafe extends Unsafe {
|
|
java.nio.channels.Channel ch();
|
|
void finishConnect();
|
|
void read();
|
|
}
|
|
|
|
protected abstract class AbstractNioUnsafe extends AbstractUnsafe implements NioUnsafe {
|
|
@Override
|
|
public java.nio.channels.Channel ch() {
|
|
return javaChannel();
|
|
}
|
|
|
|
@Override
|
|
public void connect(final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelFuture future) {
|
|
if (eventLoop().inEventLoop()) {
|
|
if (!ensureOpen(future)) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
if (connectFuture != null) {
|
|
throw new IllegalStateException("connection attempt already made");
|
|
}
|
|
|
|
boolean wasActive = isActive();
|
|
if (doConnect(remoteAddress, localAddress)) {
|
|
future.setSuccess();
|
|
if (!wasActive && isActive()) {
|
|
pipeline().fireChannelActive();
|
|
}
|
|
} else {
|
|
connectFuture = future;
|
|
|
|
// Schedule connect timeout.
|
|
int connectTimeoutMillis = config().getConnectTimeoutMillis();
|
|
if (connectTimeoutMillis > 0) {
|
|
connectTimeoutFuture = eventLoop().schedule(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (connectTimeoutException == null) {
|
|
connectTimeoutException = new ConnectException("connection timed out");
|
|
}
|
|
ChannelFuture connectFuture = AbstractNioChannel.this.connectFuture;
|
|
if (connectFuture == null) {
|
|
return;
|
|
} else {
|
|
if (connectFuture.setFailure(connectTimeoutException)) {
|
|
pipeline().fireExceptionCaught(connectTimeoutException);
|
|
close(voidFuture());
|
|
}
|
|
}
|
|
}
|
|
}, connectTimeoutMillis, TimeUnit.MILLISECONDS);
|
|
}
|
|
}
|
|
} catch (Throwable t) {
|
|
future.setFailure(t);
|
|
pipeline().fireExceptionCaught(t);
|
|
closeIfClosed();
|
|
}
|
|
} else {
|
|
eventLoop().execute(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
connect(remoteAddress, localAddress, future);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void finishConnect() {
|
|
assert eventLoop().inEventLoop();
|
|
assert connectFuture != null;
|
|
try {
|
|
boolean wasActive = isActive();
|
|
doFinishConnect();
|
|
connectFuture.setSuccess();
|
|
if (!wasActive && isActive()) {
|
|
pipeline().fireChannelActive();
|
|
}
|
|
} catch (Throwable t) {
|
|
connectFuture.setFailure(t);
|
|
pipeline().fireExceptionCaught(t);
|
|
closeIfClosed();
|
|
} finally {
|
|
connectTimeoutFuture.cancel(false);
|
|
connectFuture = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected boolean isCompatible(EventLoop loop) {
|
|
return loop instanceof NioChildEventLoop;
|
|
}
|
|
|
|
@Override
|
|
protected boolean isFlushPending() {
|
|
SelectionKey selectionKey = this.selectionKey;
|
|
return selectionKey.isValid() && (selectionKey.interestOps() & SelectionKey.OP_WRITE) != 0;
|
|
}
|
|
|
|
@Override
|
|
protected Runnable doRegister() throws Exception {
|
|
NioChildEventLoop loop = (NioChildEventLoop) eventLoop();
|
|
selectionKey = javaChannel().register(
|
|
loop.selector, isActive()? defaultInterestOps : 0, this);
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
protected void doDeregister() throws Exception {
|
|
((NioChildEventLoop) eventLoop()).cancel(selectionKey());
|
|
}
|
|
|
|
protected abstract boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception;
|
|
protected abstract void doFinishConnect() throws Exception;
|
|
}
|