问题描述
我希望在有方法。我的第一个想法是创建一个包装器,当调用 implCloseSelectableChannel
方法时通知监听器(因为 close
方法本身在 AbstractInterruptibleChannel
中声明 final
。这个解决方案有效,但是当我尝试用 Selector
注册它时,我会得到一个 IllegalSelectorException
,因为下面的检查在 SelectorImpl
:
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();
现在我无法覆盖寄存器
委托给包装 SocketChannel
的方法,因为它在 AbstractSelectableChannel $ c $中声明为
final
c>我无法实现 SelChImpl
,因为它在 sun.nio.ch
包中具有默认可见性。我可以看到从这里开始的唯一方法是制作我自己的 SelectorProvider
和选择器
,但这似乎是对于这么简单的事情有点过分。
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.
当 SocketChannel
关闭或做什么时,是否有更简单的方式得到通知我需要重新考虑我的程序设计吗?
Is there an easier way to be notified when a SocketChannel
has been closed or do I need to rethink my program design?
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);
}
}
推荐答案
如果 SocketChannel
已关闭,您正在关闭它,因此您可以按照自己喜欢的方式通知自己。
If the SocketChannel
is closed by you, you are closing it, so you can notify yourself any way you like.
如果您希望在 peer 关闭连接时收到通知, OP_READ
将会触发并且读取将返回 - 1。
If you want to be notified when the peer closes the connection,, OP_READ
will fire and a read will return -1.
这篇关于如何在SocketChannel关闭时收到通知?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!