2012-02-25 65 views
8

我想在SocketChannel调用close方法时收到通知。我的第一个想法是创建一个包装,通知聆听者何时调用implCloseSelectableChannel方法(因为close方法本身在AbstractInterruptibleChannel中声明为final)。该解决方案的工作,但是当我试图用Selector注册它,我会得到一个IllegalSelectorException因为SelectorImpl以下检查:如何在SocketChannel关闭时收到通知?

/*  */ 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包中具有默认可见性。我可以看到从这里出发的唯一方法是制作我自己的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 2012-02-27 22:57:33

+0

@Jeffrey你需要调试你的应用程序。 – EJP 2012-02-28 00:22:22

+0

我的应用程序中没有错误,但是如果我忘记通知自己弹出一个错误。到目前为止,我已经知道了这个问题,有一个“doh”时刻,然后继续前进,但是这些日子里我会忘记通知自己,我无法弄清楚为什么。我想看看我是否可以修改'SocketChannel'的工作方式以节省一些麻烦。 – Jeffrey 2012-02-28 00:31:21

-1

这是很讨厌。你也许可以使用像http://www.csg.ci.i.u-tokyo.ac.jp/~chiba/javassist/这样的字节级别的aop软件包(使用aop你应该可以在你的回调函数的close方法中添加一个cutpoint)。

你也可以创建一个与sun包同名的包,并在那里实现接口。

但我看不到一个很好,干净的方式来做到这一点。

+0

这些都不是很好的编程实践,但我会记住它们。 – Jeffrey 2012-02-25 17:13:26

+0

我同意。我隐约记得几年前有同样的问题,没有找到更好的。 – 2012-02-25 17:20:39

相关问题