2012-02-25 21 views
8

SocketChannelcloseメソッドが呼び出されたときに通知を受けたい。私の最初の考えは、closeメソッド自体がでAbstractInterruptibleChannelと宣言されているので、implCloseSelectableChannelメソッドが呼び出されたときにリスナーに通知するラッパーを作成することでした。このソリューションは動作しますが、私はSelectorに登録しようとしたとき、私は理由SelectorImplで次のチェックのIllegalSelectorExceptionになるだろう:SocketChannelが閉じられたときの通知方法

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

今、私はラップSocketChannelに委任するregisterメソッドをオーバーライドすることはできませんので、 finalAbstractSelectableChannelにあり、sun.nio.chパッケージにデフォルトの可視性があるため、SelChImplを実装できません。私がここから進むために見ることができる唯一の方法は、私自身のSelectorProviderSelectorを作ることですが、それはあまりにも簡単なことのために過度のようです。

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); 
    } 
} 

答えて

12

SocketChannelがあなたによって閉鎖されている場合、あなたはそれを閉じているので、好きなように自分自身に通知することができます。

あなたはピア接続、OP_READが発火すると、読み取りが返す-1閉じたときに通知されるようにしたい場合。

+0

問題は、私のプログラムがうまく動作するためには、私に通知するために 'SocketChannel'を閉じる人が誰かが必要であるということです。もしそうでなければ、物事が破れ始める。私を怠惰な/忘れてしまったと呼びますが、これを達成する最も簡単な方法は 'SocketChannel'にコールバックを入れることだけです。この問題が引き続き発生する場合、私のデザインを再考する必要があるかもしれません。 – Jeffrey

+0

@Jeffreyアプリケーションをデバッグする必要があります。 – EJP

+0

私のアプリケーションにはバグはありませんが、自分自身に通知するのを忘れた場合は、エラーがポップアップします。これまでのところ、私は問題を理解し、 "瞬間"の瞬間を持ち越しましたが、最近のうちに私は自分自身に通知することを忘れてしまい、理由を理解することができません。 'SocketChannel'がどうやって問題を解決するのかを修正できるかどうかを知りたかったのです。 – Jeffrey

-1

厄介です。 http://www.csg.ci.i.u-tokyo.ac.jp/~chiba/javassist/のようなバイトレベルのAOPパッケージを使用することもできます(コールバックを行うcloseメソッドにカットポイントを追加できる必要があります)。

sunパッケージと同じ名前のパッケージを作成し、そこにintefaceを実装することもできます。

しかし、私はこれを行うための良い、きれいな方法を見ることができません。

+0

いずれも非常に優れたプログラミング手法のようではありませんが、私はそれらを念頭に置いています。 – Jeffrey

+0

私は同意します。何年も前にこの同じ問題を抱えていて、もっと良いものを見つけることを漠然として覚えています。 –

関連する問題