Java 如何在SocketChannel关闭时收到通知?

Java 如何在SocketChannel关闭时收到通知?,java,wrapper,nio,Java,Wrapper,Nio,我希望在调用方法时收到通知。我的第一个想法是创建一个包装器,当调用implCloseSelectableChannel方法时通知侦听器(因为close方法本身在abstractinterruptablechannel中声明为final)。此解决方案有效,但当我尝试使用选择器注册它时,由于以下签入SelectorImpl,我会得到一个非法SelectorException: /* */ protected final SelectionKey register(AbstractSele

我希望在调用方法时收到通知。我的第一个想法是创建一个包装器,当调用
implCloseSelectableChannel
方法时通知侦听器(因为
close
方法本身在
abstractinterruptablechannel
中声明为
final
)。此解决方案有效,但当我尝试使用
选择器注册它时,由于以下签入
SelectorImpl
,我会得到一个
非法SelectorException

/*     */   protected final SelectionKey register(AbstractSelectableChannel paramAbstractSelectableChannel, int paramInt, Object paramObject)
/*     */   {
/* 128 */     if (!(paramAbstractSelectableChannel instanceof SelChImpl))
/* 129 */       throw new IllegalSelectorException();
现在,我无法覆盖
register
方法来委托给包装的
SocketChannel
,因为它在
AbstractSelectableChannel
中声明为
final
,并且我无法实现
SelChImpl
,因为它在
sun.nio.ch
包中具有默认可见性。从这里开始,我能看到的唯一方法是制作我自己的
SelectorProvider
Selector
,但对于如此简单的东西来说,这似乎是矫枉过正

SocketChannel
关闭时,是否有更简单的方法通知我,或者我是否需要重新考虑我的程序设计

SocketChannelWrapper
示例:

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);
    }
}
import java.io.IOException;
导入java.net.InetAddress;
导入java.net.InetSocketAddress;
导入java.net.Socket;
导入java.net.SocketAddress;
导入java.net.SocketOption;
导入java.net.UnknownHostException;
导入java.nio.ByteBuffer;
导入java.nio.channels.SelectionKey;
导入java.nio.channels.Selector;
导入java.nio.channels.ServerSocketChannel;
导入java.nio.channels.SocketChannel;
导入java.util.Iterator;
导入java.util.Set;
公共类SocketChannelWrapper扩展了SocketChannel{
私有静态接口CloseListener{
公共无效socketChannelClosed(SocketChannel通道);
}
专用最终插座通道插座;
私人的最终听众;
公共插座通道振打器(插座通道插座,闭合监听器l){
super(socket.provider());
this.socket=socket;
监听器=l;
}
@凌驾
public SocketAddress getLocalAddress()引发IOException{
返回socket.getLocalAddress();
}
@凌驾
public T getOption(SocketOption名称)引发IOException{
返回socket.getOption(名称);
}
@凌驾

public Set这太糟糕了。您可以使用字节级的aop包,如(使用aop,您应该能够在执行回调的close方法上添加一个切点)

您还可以创建一个与sun包同名的包,并在其中实现interface


但是我找不到一个好的、干净的方法来做这件事。

如果你关闭了
SocketChannel
,你就要关闭它,所以你可以随时通知自己


如果您希望在对等方关闭连接时收到通知,
OP_READ
将启动,并且读取将返回-1。

这两种方法都不是很好的编程实践,但我会记住它们。我同意。我隐约记得几年前也遇到过同样的问题,但没有找到更好的方法。问题是如果我的程序要正常工作,我需要关闭
SocketChannel
的人通知我。如果他们不关闭,事情就开始崩溃。称我懒惰/健忘,但实现这一点最简单的方法就是在
SocketChannel
中设置回调。如果这仍然是一个问题,我可能需要重新考虑我的设计。@Jeffrey你需要这样做吗调试您的应用程序。我的应用程序中没有bug,但如果我忘记通知自己,会弹出一个错误。到目前为止,我已经解决了问题,有一个“doh”我想看看我是否可以修改
SocketChannel
的工作方式来避免一些麻烦。@Jeffrey我有几英里的NIO代码,没有关闭通知。如果你关闭频道,它的密钥将被取消,它将被注销如果(!key.isValid())在选择循环中继续;
,我所能做的就是
。如果您需要更多,我建议您的设计有问题。如果对等机没有正确关闭,则不会出现问题……在这种情况下,您不会被通知,也不会触发任何事件。