Как получить уведомление о закрытии SocketChannel? - PullRequest
8 голосов
/ 25 февраля 2012

Я хочу получать уведомление, когда для SocketChannel вызывается метод close.Моей первой мыслью было создание оболочки, которая уведомляет слушателя, когда вызывается метод implCloseSelectableChannel (поскольку сам метод close объявлен final в AbstractInterruptibleChannel).Это решение работает, но когда я попытался зарегистрировать его с Selector, я получил бы IllegalSelectorException из-за следующей проверки в SelectorImpl:

/*     */   protected final SelectionKey register(AbstractSelectableChannel paramAbstractSelectableChannel, int paramInt, Object paramObject)
/*     */   {
/* 128 */     if (!(paramAbstractSelectableChannel instanceof SelChImpl))
/* 129 */       throw new IllegalSelectorException();

Теперь я не могу переопределить register метод для делегирования обернутому SocketChannel, потому что он объявлен final в AbstractSelectableChannel, и я не могу реализовать 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);
    }
}

Ответы [ 2 ]

12 голосов
/ 27 февраля 2012

Если SocketChannel закрыт вами, вы закрываете его, поэтому вы можете уведомить себя любым удобным вам способом.

Если вы хотите получать уведомления, когда peer закрываетсясоединение , , OP_READ сработает и чтение вернет -1.

0 голосов
/ 25 февраля 2012

это противно.Вы можете использовать пакет aop уровня байтов, например http://www.csg.ci.i.u -tokyo.ac.jp / ~ chiba / javassist / (с помощью aop вы можете добавить точку обрезки в метод close, который выполняетcallback).

Вы также можете создать пакет с тем же именем, что и пакет sun, и реализовать там интерфейс.

, но я не вижу хорошего, чистого способа сделать это.

...