Netty学习教程之基础使用篇

所属分类: 软件编程 / java 阅读数: 27
收藏 0 赞 0 分享

什么Netty?

Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。

也就是说,Netty 是一个基于NIO的客户、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户,服务端应用。Netty相当简化和流线化了网络应用的编程开发过程,例如,TCP和UDP的socket服务开发。

我们下面编写四个类

      1.用于接收数据的服务器端Socket

      2.用于接收客户端的消息,用于接收和反馈客户端发出的消息类ServertHandler

      3.用于发送数据的服务器端Client

      4.用于发送数据和接收服务器端发出的数据处理类ClientHandler

Socket.java

import io.netty.bootstrap.ServerBootstrap; 
import io.netty.buffer.ByteBuf; 
import io.netty.buffer.Unpooled; 
import io.netty.channel.ChannelFuture; 
import io.netty.channel.ChannelInitializer; 
import io.netty.channel.ChannelOption; 
import io.netty.channel.EventLoopGroup; 
import io.netty.channel.nio.NioEventLoopGroup; 
import io.netty.channel.socket.SocketChannel; 
import io.netty.channel.socket.nio.NioServerSocketChannel; 
import io.netty.handler.codec.DelimiterBasedFrameDecoder; 
import io.netty.handler.codec.string.StringDecoder; 
 
public class Server { 
  
 public static void main(String[] args) throws InterruptedException { 
  //1.第一个线程组是用于接收Client端连接的 
  EventLoopGroup bossGroup = new NioEventLoopGroup(); 
  //2.第二个线程组是用于实际的业务处理的 
  EventLoopGroup workerGroup = new NioEventLoopGroup(); 
  ServerBootstrap b = new ServerBootstrap(); 
  b.group(bossGroup, workerGroup);//绑定两个线程池 
  b.channel(NioServerSocketChannel.class);//指定NIO的模式,如果是客户端就是NioSocketChannel 
  b.option(ChannelOption.SO_BACKLOG, 1024);//TCP的缓冲区设置 
  b.option(ChannelOption.SO_SNDBUF, 32*1024);//设置发送缓冲的大小 
  b.option(ChannelOption.SO_RCVBUF, 32*1024);//设置接收缓冲区大小 
  b.option(ChannelOption.SO_KEEPALIVE, true);//保持连续 
  b.childHandler(new ChannelInitializer<SocketChannel>() { 
   @Override 
   protected void initChannel(SocketChannel sc) throws Exception { 
    ByteBuf buf = Unpooled.copiedBuffer("$_".getBytes());//拆包粘包定义结束字符串(第一种解决方案) 
    sc.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,buf));//在管道中加入结束字符串 
   // sc.pipeline().addLast(new FixedLengthFrameDecoder(200));第二种定长 
    sc.pipeline().addLast(new StringDecoder());//定义接收类型为字符串把ByteBuf转成String 
    sc.pipeline().addLast(new ServertHandler());//在这里配置具体数据接收方法的处理 
   } 
  }); 
  ChannelFuture future = b.bind(8765).sync();//绑定端口 
  future.channel().closeFuture().sync();//等待关闭(程序阻塞在这里等待客户端请求) 
  bossGroup.shutdownGracefully();//关闭线程 
  workerGroup.shutdownGracefully();//关闭线程 
 } 
} 

      1.在上面这个Server.java中,我们都要定义两个线程池,boss和worker,boss是用于管理连接到server端的client的连接数的线程池,而woeker是用于管理实际操作的线程池。

      2.ServerBootstrap用一个ServerSocketChannelFactory 来实例化。ServerSocketChannelFactory 有两种选择,一种是NioServerSocketChannelFactory,一种是OioServerSocketChannelFactory。 前者使用NIO,后则使用普通的阻塞式IO。它们都需要两个线程池实例作为参数来初始化,一个是boss线程池,一个是worker线程池。

      3.然后使ServerBookstrap管理boss和worker线程池。并且设置各个缓冲区的大小。

      4.这里的事件处理类经常会被用来处理一个最近的已经接收的Channel。ChannelInitializer是一个特殊的处理类,他的目的是帮助使用者配置一个新的Channel。也许你想通过增加一些处理类比如NettyServerHandler来配置一个新的Channel 或者其对应的ChannelPipeline来实现你的网络程序。 当你的程序变的复杂时,可能你会增加更多的处理类到pipline上,然后提取这些匿名类到最顶层的类上。

      5.在使用原始的encoder、decoder的情况下,Netty发送接收数据都是按照ByteBuf的形式,其它形式都是不合法的。 而在上面这个Socket中,我使用sc.pipeline().addLast()这个方法设置了接收为字符串类型,注意:只能设置接收为字符串类型,发送还是需要发送ByteBuf类型的数据。而且在这里我还设置了以$_为结尾的字符串就代表了本次请求字符串的结束。

      6.通过b.bind绑定端口,用于监听的端口号。

ServerHandler.java

public class ServertHandler extends ChannelHandlerAdapter { 
 
 @Override 
 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 
  String body = (String) msg; 
  System.out.println("server"+body);//前面已经定义了接收为字符串,这里直接接收字符串就可以 
  //服务端给客户端的响应 
  String response= " hi client!$_";//发送的数据以定义结束的字符串结尾 
  ctx.writeAndFlush(Unpooled.copiedBuffer(response.getBytes()));//发送必须还是ByteBuf类型 
 } 
 
 @Override 
 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 
   cause.printStackTrace(); 
   ctx.close(); 
 } 
  
} 

ServertHandler继承自 ChannelHandlerAdapter,这个类实现了ChannelHandler接口,ChannelHandler提供了许多事件处理的接口方法,然后你可以覆盖这些方法。现在仅仅只需要继承ChannelHandlerAdapter类而不是你自己去实现接口方法。

1.由于我们再server端开始的时候已经定义了接收类型为String,所以在这里我们接收到的msg直接强转成String就可以了。同时也要定义以什么为一次请求的结尾。

Client.java

public class Client { 
 
 public static void main(String[] args) throws InterruptedException { 
  EventLoopGroup worker = new NioEventLoopGroup(); 
  Bootstrap b = new Bootstrap(); 
  b.group(worker) 
  .channel(NioSocketChannel.class) 
  .handler(new ChannelInitializer<SocketChannel>() { 
   @Override 
   protected void initChannel(SocketChannel sc) throws Exception { 
    ByteBuf buf = Unpooled.copiedBuffer("$_".getBytes()); 
    sc.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,buf)); 
    sc.pipeline().addLast(new StringDecoder()); 
    sc.pipeline().addLast(new ClientHandler()); 
   } 
  }); 
  ChannelFuture f=b.connect("127.0.0.1",8765).sync(); 
  f.channel().writeAndFlush(Unpooled.copiedBuffer(" hi server2$_".getBytes())); 
  f.channel().writeAndFlush(Unpooled.copiedBuffer(" hi server3$_".getBytes())); 
  f.channel().writeAndFlush(Unpooled.copiedBuffer(" hi server4$_".getBytes())); 
  f.channel().closeFuture().sync(); 
  worker.shutdownGracefully(); 
 } 
} 

client端和Socket端几乎代码相同,只是client端用的不是ServerBootstrap而是Bootstrap来管理连接。这里没什么好说的。

ClientHandler.java

public class ClientHandler extends ChannelHandlerAdapter{ 
 @Override 
 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 
  try { 
   System.out.println("client"+msg.toString()); 
  } finally { 
   ReferenceCountUtil.release(msg);//释放缓冲区 
  } 
 } 
 
 @Override 
 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 
   cause.printStackTrace(); 
   ctx.close(); 
 } 
} 

ClientHandler和ServertHandler代码和原理也是一样,只是在client端我们要释放缓冲区。为什么在ServerHandler我们不需要释放呢 ?因为在ServertHandler我们调用ctx.writeAndFlush方法的时候,这个方法默认已经帮我们释放了缓冲区。

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

更多精彩内容其他人还在看

Java的面向对象编程基本概念学习笔记整理

这篇文章主要介绍了Java的面向对象编程基本概念学习笔记整理,包括类与方法以及多态等支持面向对象语言中的重要特点,需要的朋友可以参考下
收藏 0 赞 0 分享

Eclipse下编写java程序突然不会自动生成R.java文件和包的解决办法

这篇文章主要介绍了Eclipse下编写java程序突然不会自动生成R.java文件和包的解决办法 的相关资料,需要的朋友可以参考下
收藏 0 赞 0 分享

基于Java实现杨辉三角 LeetCode Pascal's Triangle

这篇文章主要介绍了基于Java实现杨辉三角 LeetCode Pascal's Triangle的相关资料,需要的朋友可以参考下
收藏 0 赞 0 分享

Java中Spring获取bean方法小结

Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架,如何在程序中获取Spring配置的bean呢?下面通过本文给大家介绍Java中Spring获取bean方法小结,对spring获取bean方法相关知识感兴趣的朋友一起学习吧
收藏 0 赞 0 分享

如何计算Java对象占用了多少空间?

在Java中没有sizeof运算符,所以没办法知道一个对象到底占用了多大的空间,但是在分配对象的时候会有一些基本的规则,我们根据这些规则大致能判断出来对象大小,需要的朋友可以参考下
收藏 0 赞 0 分享

剖析Java中的事件处理与异常处理机制

这篇文章主要介绍了Java中的事件处理与异常处理机制,讲解Java是如何对事件或者异常作出响应以及定义异常的一些方法,需要的朋友可以参考下
收藏 0 赞 0 分享

详解Java的Struts2框架的结构及其数据转移方式

这篇文章主要介绍了详解Java的Struts2框架的结构及其数据转移方式,Struts框架是Java的SSH三大web开发框架之一,需要的朋友可以参考下
收藏 0 赞 0 分享

Java封装好的mail包发送电子邮件的类

本文给大家分享了2个java封装好的mail包发送电子邮件的类,并附上使用方法,小伙伴们可以根据自己的需求自由选择。
收藏 0 赞 0 分享

在Java的Struts中判断是否调用AJAX及用拦截器对其优化

这篇文章主要介绍了在Java的Struts中判断是否调用AJAX及用拦截器对其优化的方法,Struts框架是Java的SSH三大web开发框架之一,需要的朋友可以参考下
收藏 0 赞 0 分享

java多线程Future和Callable类示例分享

JAVA多线程实现方式主要有三种:继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程。其中前两种方式线程执行完后都没有返回值,只有最后一种是带返回值的。今天我们就来研究下Future和Callab
收藏 0 赞 0 分享
查看更多