Mohammad_Daoud

Java NIO Selector Sample

Aug 2nd, 2021 (edited)
1,157
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1.  
  2. import java.net.InetSocketAddress;
  3. import java.nio.ByteBuffer;
  4. import java.nio.channels.SelectionKey;
  5. import java.nio.channels.Selector;
  6. import java.nio.channels.ServerSocketChannel;
  7. import java.nio.channels.SocketChannel;
  8. import java.nio.charset.StandardCharsets;
  9. import java.util.HashMap;
  10. import java.util.concurrent.Executors;
  11. import java.util.concurrent.TimeUnit;
  12.  
  13. public class MainServer {
  14.  
  15.     ServerSocketChannel serverChannel; // for stream-oriented listening sockets
  16.     Selector selector; // the multiplexor of SelectChannel object
  17.     SelectionKey serverKey; // for representing the registration of a SelectableChannel with a Selector.
  18.  
  19.     MainServer(InetSocketAddress listenAddress) throws Throwable {
  20.         serverChannel = ServerSocketChannel.open(); // to open new ServerSocketChannel
  21.         serverChannel.configureBlocking(false); // to configure the ServerSocketChannel as non-blocking
  22.  
  23.         /** Selector.open() is to open the selector .
  24.          *  SelectionKey.OP_ACCEPT is for if the selector detects that the corresponding
  25.          *      server-socket channel is ready to accept another connection,
  26.          *      or has an error pending, then it will add OP_ACCEPT to the key's ready set.
  27.          */
  28.         serverKey = serverChannel.register(selector = Selector.open(), SelectionKey.OP_ACCEPT); // Registers this channel with the given selector, returning a selection key.
  29.         serverChannel.bind(listenAddress);// binds the ServerSocketChannel to the passed InetSocketAddress.
  30.  
  31.         Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
  32.             /* Executor is the interface which allows us to execute tasks on threads asynchronously
  33.              *  but here I make a single thread for the sample that we need to
  34.              */
  35.             try {
  36.                 loop();
  37.             } catch (Throwable t) {
  38.                 t.printStackTrace();
  39.             }
  40.         }, 0/* for the first execution is ran */, 500, TimeUnit.MILLISECONDS);
  41.     }
  42.  
  43.     static HashMap<SelectionKey, ClientSession> clientMap = new HashMap<SelectionKey, ClientSession>(); // the hashMap is to store all clients currently connected
  44.  
  45.     void loop() throws Throwable {
  46.         selector.selectNow();
  47.         /*
  48.          * DOSE NOT BLOCK ! , which means that if there is no keys ready in that given time it will return instantly and we won't have any key.
  49.          * otherwise if there are keys ready it will populate the key set and return the amount of keys that were added to the key set.
  50.          */
  51.  
  52.         for (SelectionKey key : selector.selectedKeys()) {
  53.             //this loop will go through the selected keys SO, if the selector select some keys that are ready for I/O operation it will put that key in that key set.
  54.             try {
  55.                 if (!key.isValid())
  56.                     continue;
  57.                 if (key == serverKey) {
  58.                     SocketChannel acceptedChannel = serverChannel.accept(); // accept a new channel
  59.                     if (acceptedChannel == null)
  60.                         continue;
  61.  
  62.                     acceptedChannel.configureBlocking(false); //non-blocking configure
  63.                     SelectionKey readKey = acceptedChannel.register(selector, SelectionKey.OP_READ);// for read operations
  64.                     clientMap.put(readKey, new ClientSession(readKey, acceptedChannel));// to add the read key to the hashMap (add client to the map)
  65.  
  66.                     System.out.println("New client ip=" + acceptedChannel.getRemoteAddress() +
  67.                             ", total clients=" + MainServer.clientMap.size());
  68.                 }// end isAcceptable condition
  69.  
  70.                 if (key.isReadable()) {
  71.                     ClientSession session = clientMap.get(key);
  72.  
  73.                     if (session == null)
  74.                         continue;
  75.  
  76.                     session.read();
  77.                 }// end isReadable condition
  78.  
  79.             } catch (Throwable t) {
  80.                 t.printStackTrace();
  81.             }
  82.         }// end foreach loop
  83.  
  84.         selector.selectedKeys().clear();
  85.     } // end loop() method
  86.  
  87.     public static void main(String[] args) throws Throwable {
  88.         new MainServer(new InetSocketAddress("localhost", 3333)); // calls the constructor passing new address to listen on.
  89.     }
  90. }
  91.  
  92. /*******************************************************************************************************************************/
  93.  class ClientSession {
  94.  
  95.     /*
  96.      * here in this class in every time new connection is made
  97.      * the class will be created to hold the information for this client .
  98.      * So, every client will have there own SelectionKey,SocketChannel and ByteBuffer
  99.      */
  100.     SelectionKey selectionKey;
  101.     SocketChannel channel;
  102.     ByteBuffer buffer;
  103.  
  104.     ClientSession(SelectionKey selectionKey, SocketChannel channel) throws Throwable {
  105.         this.selectionKey = selectionKey;
  106.         this.channel = (SocketChannel) channel.configureBlocking(false); // asynchronous non-blocking
  107.         buffer = ByteBuffer.allocateDirect(64); // 64 byte capacity
  108.     }
  109.  
  110.     void disconnect() {// this will just cancel the selection key and close the channel .
  111.  
  112.         MainServer.clientMap.remove(selectionKey); // remove the client from the map
  113.         try {
  114.             if (selectionKey != null)
  115.                 selectionKey.cancel();
  116.  
  117.             if (channel == null)
  118.                 return;
  119.  
  120.             System.out.println("bye bye " + (InetSocketAddress) channel.getRemoteAddress());
  121.             channel.close();
  122.         } catch (Throwable t) { /** quietly ignore  */}
  123.     }
  124.  
  125.     void read() {
  126.         /* TODO : handle the reading from channel .
  127.          * now in read method when the selector let us know that is a read event pending to call this method
  128.          * then we can handle the read event. So, we can read bytes from the channel and handle it however we need to.
  129.          */
  130.  
  131.         try {
  132.             int amount_read = -1; // to ignore any exception that channel.read(...) method wants to throw
  133.             try {
  134.                 amount_read = channel.read((ByteBuffer) buffer.clear());
  135.             } catch (Throwable t) {
  136.                 System.out.println("");
  137.             }
  138.  
  139.             if (amount_read == -1)
  140.                 disconnect();
  141.             //because the read method above will return The number of bytes read, possibly zero, or -1 if the channel has reached end-of-stream
  142.  
  143.             if (amount_read < 1)
  144.                 return; // if zero , we will return and wait until another read event triggers this method
  145.  
  146.             // if we make it pass then we will handle it whatever we want
  147.             System.out.println("sending back " + buffer.position() + " bytes");
  148.             System.out.println("sending message:\""+ StandardCharsets.UTF_8.decode(buffer.flip()).toString() +"\" back to : " + channel.getRemoteAddress());
  149.  
  150.             // turn this bus right around and send it back!
  151.             buffer.flip();// to make the buffer ready to read
  152.             channel.write(buffer);
  153.         } catch (Throwable t) {
  154.             disconnect();// to disconnect if something goes wrong
  155.             t.printStackTrace();
  156.         }
  157.     }
  158.  
  159. }
RAW Paste Data