I want to be notified when a SocketChannel
has its close
method called. My first thought was to create a wrapper which notifies a listener when the implCloseSelectableChannel
method is called (since the close
method itself is declared final
in AbstractInterruptibleChannel
). This solution works, but when I tried to register it with a Selector
I would get an IllegalSelectorException
because of the following check in SelectorImpl
:
/* */ protected final SelectionKey register(AbstractSelectableChannel paramAbstractSelectableChannel, int paramInt, Object paramObject)
/* */ {
/* 128 */ if (!(paramAbstractSelectableChannel instanceof SelChImpl))
/* 129 */ throw new IllegalSelectorException();
Now I can't override the register
method to delegate to the wrapped SocketChannel
because it's declared final
in AbstractSelectableChannel
and I can't implement SelChImpl
because it has default visibility in the sun.nio.ch
package. The only way I can see to proceed from here would be to make my own SelectorProvider
and Selector
, but that seems like overkill for something so simple.
Is there an easier way to be notified when a SocketChannel
has been closed or do I need to rethink my program design?
SocketChannelWrapper
example:
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketOption;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class SocketChannelWrapper extends SocketChannel {
private static interface CloseListener {
public void socketChannelClosed(SocketChannel channel);
}
private final SocketChannel socket;
private final CloseListener listener;
public SocketChannelWrapper(SocketChannel socket, CloseListener l) {
super(socket.provider());
this.socket = socket;
listener = l;
}
@Override
public SocketAddress getLocalAddress() throws IOException {
return socket.getLocalAddress();
}
@Override
public <T> T getOption(SocketOption<T> name) throws IOException {
return socket.getOption(name);
}
@Override
public Set<SocketOption<?>> supportedOptions() {
return socket.supportedOptions();
}
@Override
public SocketChannel bind(SocketAddress local) throws IOException {
return socket.bind(local);
}
@Override
public <T> SocketChannel setOption(SocketOption<T> name, T value)
throws IOException {
return socket.setOption(name, value);
}
@Override
public SocketChannel shutdownInput() throws IOException {
return socket.shutdownInput();
}
@Override
public SocketChannel shutdownOutput() throws IOException {
return socket.shutdownOutput();
}
@Override
public Socket socket() {
return socket.socket();
}
@Override
public boolean isConnected() {
return socket.isConnected();
}
@Override
public boolean isConnectionPending() {
return socket.isConnectionPending();
}
@Override
public boolean connect(SocketAddress remote) throws IOException {
return socket.connect(remote);
}
@Override
public boolean finishConnect() throws IOException {
return socket.finishConnect();
}
@Override
public SocketAddress getRemoteAddress() throws IOException {
return socket.getRemoteAddress();
}
@Override
public int read(ByteBuffer dst) throws IOException {
return socket.read(dst);
}
@Override
public long read(ByteBuffer[] dsts, int offset, int length)
throws IOException {
return socket.read(dsts, offset, length);
}
@Override
public int write(ByteBuffer src) throws IOException {
return socket.write(src);
}
@Override
public long write(ByteBuffer[] srcs, int offset, int length)
throws IOException {
return socket.write(srcs, offset, length);
}
@Override
protected void implCloseSelectableChannel() throws IOException {
socket.close();
listener.socketChannelClosed(this);
}
@Override
protected void implConfigureBlocking(boolean block) throws IOException {
socket.configureBlocking(block);
}
public static void main(String[] args) throws UnknownHostException,
IOException {
final Selector selector = Selector.open();
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
selector.select();
Iterator<SelectionKey> itr = selector.selectedKeys()
.iterator();
while (itr.hasNext()) {
SelectionKey key = itr.next();
itr.remove();
if (key.isValid()) {
if (key.isAcceptable()) {
((ServerSocketChannel) key.channel())
.accept();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
t.setDaemon(true);
ServerSocketChannel server = ServerSocketChannel.open().bind(
new InetSocketAddress(1234));
server.configureBlocking(false);
server.register(selector, SelectionKey.OP_ACCEPT);
t.start();
SocketChannel socket = new SocketChannelWrapper(
SocketChannel.open(new InetSocketAddress(InetAddress
.getLocalHost(), 1234)), new CloseListener() {
@Override
public void socketChannelClosed(SocketChannel channel) {
System.out.println("Socket closed!");
}
});
socket.configureBlocking(false);
// socket.close(); //prints out "Socket closed!"
socket.register(selector, SelectionKey.OP_READ);
}
}
If the SocketChannel
is closed by you, you are closing it, so you can notify yourself any way you like.
If you want to be notified when the peer closes the connection,, OP_READ
will fire and a read will return -1.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With