Ejemplo Sencillo de TCP/IP con netty

De ChuWiki
Revisión del 16:28 1 ago 2018 de Chudiang (Discusión | contribuciones) (El Servidor)

Saltar a: navegación, buscar

Contenido

Netty es una librería Java que nos facilita todo el uso de sockets. Está pensada para ser eficiente y consumir pocos recursos, además de tener ya implementados bastantes protocolos estándar, ahorrándonos el trabajo de hacerlo a nosotros o buscar otras librerías.

Veamos aquí un ejemplo básico de cómo hacer un socket TCP/IP. Haremos un servidor que admitirá varios clientes, cada cliente enviará un texto periodicamente y el servidor se lo reenviará al resto de clientes. Lo haremos con lo básico de Netty, sin utilizar ninguno de los encoder o decoder que netty nos proporiciona.

Aquí tienes el código de ejemplo de socket tcp/ip con netty

El Servidor

En netty el código para abrir un socket servidor TCP/IP es más o menos siempre el mismo, copy-paste y lo explicamos un poco

int port = 8080; // El puerto que queramos

EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
EventLoopGroup workerGroup = new NioEventLoopGroup(); // (2)

try {
   ServerBootstrap b = new ServerBootstrap(); // (3)
   b.group(bossGroup, workerGroup)  // (4)
      .channel(NioServerSocketChannel.class) // (5)
      .childHandler(new ChannelInitializer<SocketChannel>() { // (6)
          @Override
          public void initChannel(SocketChannel ch) throws Exception { // (7)
             ch.pipeline().addLast(serverHandler); // (8)
          }
      })
      .option(ChannelOption.SO_BACKLOG, 128)          // (9)
      .childOption(ChannelOption.SO_KEEPALIVE, true); // (10)
       
   ChannelFuture f = b.bind(port).sync(); // (11)

   f.channel().closeFuture().sync(); // (12)

Veamos todo esto

  • (1) Netty está orientado a eventos. Se producen eventos y se propagan cuando se abre una conexión, cuando se acepta un cliente, cuando se cierra una conexión, cuando llega un mensaje, etc, etc, etc. Todos estos eventos se tratan en un bucle de eventos en una serie de hilos que maneja netty. Netty tiene varias clases que implementan estos bucles de eventos con hilos, entre ellas NioEventLoopGroup. Así que lo primero que hacemos es crear una de estas para los eventos del socket servidor. Cada vez que se acepte una conexión de un cliente o se cierre, será este bucle de eventos en el encargado de detectarlo y avisar a todos los que tengan interés en ello.
  • (2) Idem a (1), pero es el bucle de eventos para los eventos de clientes ya conectados, cuando se envían o reciben mensajes de ellos, también cuando se desconecta un cliente concreto, etc. Resumiendo, (1) es para los eventos del socket servidor y (2) para los eventos en cada uno de los socket con los clientes.
  • (3) ServerBootstrap es una clase que nos abre un socket servidor de forma fácil. Podríamos hacerlo usando clases de más bajo nivel de netty (ServerSocketChannel) y así tendriamos más control y libertad para crearlo como queramos, pero en general no es necesario. Basta con instanciar esta clase.
  • (4) Le pasamos los dos bucles de eventos que creamos antes, el del servidor para que acepte conexiones y el de los eventos de cada uno de los clientes ya conectados.
  • (5) Estamos haciendo un Socket Servidor que acepte conexiones de clientes. Netty tiene varias clases que permiten hacer esto, aquí decimos la que queremos usar, NioServerSocketChannel. Esta usa la función select de java.nio. Hay otras implementaciones que usan entradas/salidas bloqueantes, o que usan epoll de linux y que sólo funcionan en linux. Para un java moderno, NioServerSocketChannel es una buena elección.
  • (6) Llamamos al método childHandler pasándole un ChannelInitializer. Cada vez que un cliente se conecte, se llamará a (7) ChannelInitializer.initChannel() pasándole el SocketChannel con el cliente que se acaba de conectar, es decir, la conexión con dicho cliente. Aquí es donde tenemos que hacer cosas de nuestra cosecha para tratar los mensajes que nos envíe el cliente o para enviarle mensajes al cliente.
  • (8) Cada vez que sucede algo con el cliente (llega un mensaje, le enviamos un mensaje, se conecta, se desconecta, etc), netty provoca eventos a los que nos avisa si nos suscribimos. en la línea (8) es donde lo hacemos. En la llamada a ch.pipeline().addLast() pasamos una clase nuestra, serverHandler, que implementa la interfaz de netty correspondiente para recibir todos los eventos que nos interesen en la conexión con el cliente. Veremos esto con más detalle más adelante.
  • (9) Con el método option() podemos pasar todas las opciones que queramos típicas de un socket servidor TCP/IP. El método option() afecta al socket servidor.
  • (10) con el método childOption() podemos pasar todas las opciones que queramos típicas de un socket servidor TCP/IP. El método childOption() afecta a los socket que se abran con el cliente, no al socket servidor.
  • (11) Hacemos el típico bind() del socket servidor, indicando el puerto que queremos. La llamada a bind() vuelve inmediatamente, con la llamada a sync() el código se queda aquí bloqueado hasta que el servidor está en marcha.
  • (12) f.channel() nos devuelve el canal (conexión) que hay en el socket servidor. closeFuture() nos devuelve una "cosa" (un future) que es algo que sucederá pero todavía no ha sucedido y ese algo, en este caso, es que se cierre el socket servidor. Si llamamos al método sync() de esa cosa futura, el método sync() se quedará bloqueado hasta que esa cosa futura (el cierre del socket) sea realidad. Como nadie cierra el socket (en este ejemplo), este sync() se quedará aquí para siempre. Si en algún sitio llamaramos a f.channel().close(), entonces se cerraría el socket y el sync() acabaría saliendo.