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())在选择循环中继续;
,我所能做的就是。如果您需要更多,我建议您的设计有问题。如果对等机没有正确关闭,则不会出现问题……在这种情况下,您不会被通知,也不会触发任何事件。