2012-02-25 47 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

相關問題