Java NIO Server Ejemplo (Bidireccional Asíncrono)
Si usas un sólo hilo por cada socket abierto en el servidor puedes consumir una candidad de recursos de memoria y CPU considerable. Con java.nio es más eficiente en este aspecto pero sube la latencia de la aplicación (no demasiado en este ejemplo)
1 - Java NIO Server
import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; 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.ArrayList; import java.util.Iterator; import java.util.Random; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; public class Server { private static int PORT = 8000; private static final long PAUSE_BETWEEEN_MSGS = 10; // millisecs private static ByteBuffer echoBuffer = ByteBuffer.allocate(1024); private static ConcurrentHashMap<Integer, SocketChannel> chm = new ConcurrentHashMap<Integer, SocketChannel>(); private static int msg = 0; public static void main(String args[]) throws Exception { // Create a new selector Selector selector = Selector.open(); // Open a listener on each port, and register each one ServerSocketChannel ssc = ServerSocketChannel.open(); ssc.configureBlocking(false); ServerSocket ss = ssc.socket(); InetSocketAddress address = new InetSocketAddress(PORT); ss.bind(address); //registers ACCEPT ssc.register(selector, SelectionKey.OP_ACCEPT); System.out.println("Going to listen on " + PORT); sendMsgsToRandomClients(); while (true) { selector.select(); Set<SelectionKey> selectedKeys = selector.selectedKeys(); Iterator<SelectionKey> it = selectedKeys.iterator(); String msg = new String(); while (it.hasNext()) { SelectionKey key = (SelectionKey) it.next(); if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) { // Accept the new connection ServerSocketChannel sscNew = (ServerSocketChannel) key .channel(); SocketChannel sc = sscNew.accept(); sc.configureBlocking(false); // Add the new connection to the selector sc.register(selector, SelectionKey.OP_READ); // Add the socket channel to the list chm.put(sc.hashCode(), sc); it.remove(); } else if ((key.readyOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) { // Read the data SocketChannel sc = (SocketChannel) key.channel(); int code = 0; while ((code = sc.read(echoBuffer)) > 0) { byte b[] = new byte[echoBuffer.position()]; echoBuffer.flip(); echoBuffer.get(b); msg+=new String(b, "UTF-8"); } // removes the new line if(msg.length()>1) msg = msg.substring(0, msg.length()-2); if (code == -1 || msg.toUpperCase().indexOf("BYE")>-1){ chm.remove(sc.hashCode()); sc.close(); } else { echoBuffer.clear(); } System.out.println("msg: " + msg + " from: " + sc + "code: " + code ); it.remove(); } } } } /** * This method sends messages to a random outPutStream */ private static void sendMsgsToRandomClients() { new Thread("Send-to-Clients") { public void run() { try { while (true) { Random generator = new Random(); if(chm.keySet().size()>0){ Integer randomKey = new ArrayList<Integer>( chm.keySet()).get(generator.nextInt(chm.keySet().size())); SocketChannel sc = chm.get(randomKey); try { msg++; ByteBuffer buf = ByteBuffer.wrap(("From server to Client msg nº - "+ msg + "\n").getBytes()); sc.write(buf); } catch (IOException e) { e.printStackTrace(); chm.remove(randomKey); } } Thread.sleep(PAUSE_BETWEEEN_MSGS); } } catch (Exception e) { e.printStackTrace(); } } }.start(); } }
1.1 Arrancar el Servidor
Compila y Arranca el servidor. Si es desde consola javac Server.java para compilar y Java Server para ejecutar. Si todo va bien deberías ver.
1.2 Prueba el Servidor
Usa telnet o similar para verificar que el cliente funciona. Telnet the Server ex. telnet localhost 8000 . (Si usas windows mas reciente que el XP tendrás que instalarlo o usar un cliente socket cualquiera)
Escribe "bye" y pulsa Entrar para cerrar la sesión con el servidor. Puedes usar tantos clientes telnet como quieras desde diferentes ordenadores o el mismo. Cuando se terminan todas las conexiones con el servidor, este muestra los resultados.
2 - Notas
- Si en Linux se produce el error Too Many Open files lanza el comando ulimit -n 4096 para 4096 archivos.
- Si tienes alguna idea o sugerencia aquí