2012-02-25 8 views
8

Desidero ricevere una notifica quando un SocketChannel ha il suo metodo close chiamato. Il mio primo pensiero è stato quello di creare un wrapper che avvisasse un ascoltatore quando viene chiamato il metodo implCloseSelectableChannel (poiché il metodo close è dichiarato final in AbstractInterruptibleChannel). Questa soluzione funziona, ma quando ho provato a registrarlo con un Selector vorrei ottenere un IllegalSelectorException per i seguenti motivi check-in SelectorImpl:Come essere avvisati quando un SocketChannel viene chiuso?

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

ora non posso ignorare il metodo register di delegare al avvolto SocketChannel perché è dichiarato final in AbstractSelectableChannel e non è possibile implementare SelChImpl perché ha la visibilità predefinita nel pacchetto sun.nio.ch. L'unico modo che posso vedere per procedere da qui sarebbe quello di creare il mio SelectorProvider e Selector, ma sembra eccessivo per qualcosa di così semplice.

C'è un modo più semplice per essere avvisati quando un SocketChannel è stato chiuso o devo ripensare la progettazione del mio programma?

SocketChannelWrapper esempio:

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

risposta

12

Se il SocketChannel viene chiuso da te, lo stai chiudendo, in modo da poterti notificare come preferisci.

Se si desidera ricevere una notifica quando il pari chiude la collegamento,, OP_READ scatta e una lettura tornerà -1.

+0

Il problema è che se il mio programma deve funzionare correttamente, ho bisogno di chi sta chiudendo il 'SocketChannel' per notificarmi. Se non lo fanno, le cose iniziano a rompersi. Chiamami pigro/smemorato, ma il modo più semplice per farlo sarebbe avere semplicemente il callback proprio nel 'SocketChannel'. Potrei dover ripensare al mio design se questo continua ad essere un problema. – Jeffrey

+0

@Jeffrey È necessario eseguire il debug dell'applicazione. – EJP

+0

Non ho bug nella mia applicazione, ma se dimentico di notificarmi appare un errore. Finora ho capito il problema, ho avuto un "doh" e sono andato avanti, ma uno di questi giorni mi dimenticherò di informarmi e non riuscirò a capire perché. Volevo vedere se potevo modificare il modo in cui 'SocketChannel' funzionava per salvarmi qualche problema. – Jeffrey

-1

che è brutto. potresti forse usare un pacchetto aop a livello di byte come http://www.csg.ci.i.u-tokyo.ac.jp/~chiba/javassist/ (con aop si dovrebbe poter essere in grado di aggiungere un punto di divisione sul metodo close che fa il callback).

si potrebbe anche creare un pacchetto con lo stesso nome del pacchetto sole e implementare l'intefaccia in là.

ma non riesco a vedere un modo buono e pulito per farlo.

+0

Nessuna di queste sembra una buona prassi di programmazione, ma le terrò a mente. – Jeffrey

+0

sono d'accordo. Ricordo vagamente di aver avuto lo stesso problema anni fa e di non aver trovato nulla di meglio. –