Netty 系列(三)Netty 入门

Netty 是一个提供异步事件驱动的网络应用框架,用以快速开发高性能、高可靠性的网络服务器和客户端程序。更多请参考:Netty GithubNetty中文入门

一、获得 Netty

可以通过Maven安装Netty。查看Netty之HelloWorld快速入门,更多API

  1. <dependency>
  2. <groupId>io.netty</groupId>
  3. <artifactId>netty-all</artifactId>
  4. <version>5.0.0.Alpha2</version>
  5. </dependency>

二、Netty 服务端开发

现在让我们从服务端的处理器的实现开始,处理器是由 Netty 生成用来处理 I/O 事件的。

  1. public class ServerHandler extends ChannelHandlerAdapter { // (1)
  2. @Override
  3. public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { // (2)
  4. //1. 接收客户端的请求数据
  5. ByteBuf buf = (ByteBuf)msg;
  6. byte[] data = new byte[buf.readableBytes()];
  7. buf.readBytes(data);
  8. String request = new String(data, "utf-8");
  9. System.out.println("收到 client 请求数据:" + request);
  10. //2. 返回响应数据,ctx.write()后自动释放msg
  11. ChannelFuture f = ctx.writeAndFlush(Unpooled.copiedBuffer("netty".getBytes())); // (3)
  12. //2.1 写完成后会自动关闭 client,否则与 client 建立长连接
  13. f.addListener(ChannelFutureListener.CLOSE); // (4)
  14. }
  15. @Override
  16. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { // (5)
  17. cause.printStackTrace();
  18. ctx.close();
  19. }
  20. }
  1. DisCardServerHandler 继承自 ChannelHandlerAdapter,这个类实现了ChannelHandler接口,ChannelHandler提供了许多事件处理的接口方法,然后你可以覆盖这些方法。现在仅仅只需要继承ChannelHandlerAdapter类而不是你自己去实现接口方法。

  2. 这里我们覆盖了chanelRead()事件处理方法。每当从客户端收到新的数据时,这个方法会在收到消息时被调用,这个例子中,收到的消息的类型是ByteBuf

  3. ByteBuf是一个引用计数对象,这个对象必须显示地调用release()方法来释放。ctx.write()后自动释放 msg,否则,channelRead()方法就需要像下面的这段代码一样来手动释放 msg:

    1. @Override
    2. public void channelRead(ChannelHandlerContext ctx, Object msg) {
    3. try {
    4. // Do something with msg
    5. } finally {
    6. // ((ByteBuf) msg).release();
    7. ReferenceCountUtil.release(msg);
    8. }
    9. }
  4. 写完成后程序不会自动关闭与 client 的连接,你需要手动绑定 ChannelFuture 的监听事件,写完成后才会关闭连接,ChannelFutureListener.CLOSE 的实现如下:

    1. ChannelFutureListener CLOSE = new ChannelFutureListener() {
    2. public void operationComplete(ChannelFuture future) {
    3. future.channel().close();
    4. }
    5. };
  5. exceptionCaught()事件处理方法是当出现Throwable对象才会被调用,即当Netty由于IO错误或者处理器在处理事件时抛出的异常时。在大部分情况下,捕获的异常应该被记录下来并且把关联的channel给关闭掉。然而这个方法的处理方式会在遇到不同异常的情况下有不同的实现,比如你可能想在关闭连接之前发送一个错误码的响应消息。

到目前为止一切都还比较顺利,接下来我拉需要编写一个 main() 方法来启动服务端的 ServerHandler。

  1. public class Server {
  2. private int port;
  3. public Server(int port) {
  4. this.port = port;
  5. }
  6. public void run() throws Exception {
  7. EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
  8. EventLoopGroup workerGroup = new NioEventLoopGroup();
  9. try {
  10. //1. 第一个线程组是用于接收Client端连接
  11. bossGroup = new NioEventLoopGroup();
  12. //2. 第二个线程组是用于处理实现的业务操作
  13. workerGroup = new NioEventLoopGroup();
  14. //3. ServerBootstrap 是一个启动NIO服务的辅助启动类
  15. ServerBootstrap b = new ServerBootstrap(); // (2)
  16. //3.1 将两个工作线程组加进来
  17. b.group(bossGroup, workerGroup)
  18. //3.2 指定使用NioServerSocketChannel这种类型的通道
  19. .channel(NioServerSocketChannel.class) // (3)
  20. //3.3 使用childHandler来绑定具体的事件处理器
  21. .childHandler(new ChannelInitializer<SocketChannel>() { // (4)
  22. @Override
  23. public void initChannel(SocketChannel ch) throws Exception {
  24. ch.pipeline().addLast(new ServerHandler());
  25. }
  26. })
  27. //3.4 设置TCP缓冲区大小,默认128,一般不用改
  28. .option(ChannelOption.SO_BACKLOG, 128) // (5)
  29. //3.5 设置发送缓冲区大小
  30. .option(ChannelOption.SO_SNDBUF, 32 * 1034)
  31. //3.6 设置接收缓冲区大小
  32. .option(ChannelOption.SO_RCVBUF, 32 * 1034)
  33. //3.7 KEEPALIVE
  34. .childOption(ChannelOption.SO_KEEPALIVE, true);
  35. //4. 绑定端口
  36. ChannelFuture f = b.bind(port).sync(); // (7)
  37. //5. 监听通道关闭 <=> 阻塞程序,不然Server直接执行完成后关闭,client就不可能连接上了
  38. //Thread.sleep(Integer.MAX_VALUE);
  39. f.channel().closeFuture().sync();
  40. } finally {
  41. //6. 修优雅退出,释放线程池资源
  42. workerGroup.shutdownGracefully();
  43. bossGroup.shutdownGracefully();
  44. }
  45. }
  46. public static void main(String[] args) throws Exception {
  47. int port;
  48. if (args.length > 0) {
  49. port = Integer.parseInt(args[0]);
  50. } else {
  51. port = 8765;
  52. }
  53. new Server(port).run();
  54. }
  55. }
  1. NioEventLoopGroup 是用来处理I/O操作的多线程事件循环器,Netty提供了许多不同的EventLoopGroup的实现用来处理不同传输协议。在这个例子中我们实现了一个服务端的应用,因此会有2个NioEventLoopGroup会被使用。第一个经常被叫做‘boss’,用来接收进来的连接。第二个经常被叫做‘worker’,用来处理已经被接收的连接,一旦‘boss’接收到连接,就会把连接信息注册到‘worker’上。如何知道多少个线程已经被使用,如何映射到已经创建的Channels上都需要依赖于EventLoopGroup的实现,并且可以通过构造函数来配置他们的关系。

  2. ServerBootstrap 是一个启动NIO服务的辅助启动类。你可以在这个服务中直接使用Channel,但是这会是一个复杂的处理过程,在很多情况下你并不需要这样做。

  3. 这里我们指定使用NioServerSocketChannel类来举例说明一个新的Channel如何接收进来的连接。

  4. 这里的事件处理类经常会被用来处理一个最近的已经接收的Channel。ChannelInitializer是一个特殊的处理类,他的目的是帮助使用者配置一个新的Channel。

  5. 你可以设置这里指定的通道实现的配置参数。我们正在写一个TCP/IP的服务端,因此我们被允许设置socket的参数选项比如tcpNoDelay和keepAlive。请参考ChannelOption和详细的ChannelConfig实现的接口文档以此可以对ChannelOptions的有一个大概的认识。

通过以步骤,一个服务端就搭建好了。

三、客户端开发

  1. public class Client {
  2. public static void main(String[] args) throws InterruptedException {
  3. EventLoopGroup workgroup = new NioEventLoopGroup();
  4. try {
  5. Bootstrap b = new Bootstrap();
  6. b.group(workgroup)
  7. .channel(NioSocketChannel.class)
  8. .handler(new ChannelInitializer<SocketChannel>() {
  9. @Override
  10. protected void initChannel(SocketChannel sc) throws Exception {
  11. sc.pipeline().addLast(new ClientHandler());
  12. }
  13. });
  14. //发起异步连接操作
  15. ChannelFuture f = b.connect("127.0.0.1", 8080).sync();
  16. //向服务器发送数据 buf
  17. f.channel().writeAndFlush(Unpooled.copiedBuffer("777".getBytes()));
  18. //等待客户端链路关闭
  19. f.channel().closeFuture().sync();
  20. } finally {
  21. //优雅退出,释放 NIO 线程组
  22. workgroup.shutdownGracefully();
  23. }
  24. }
  25. }

客户端业务处理ClientHandler

  1. public class ClientHandler extends ChannelHandlerAdapter {
  2. @Override
  3. public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)
  4. try {
  5. //读取buf中的数据
  6. ByteBuf buf = (ByteBuf) msg;
  7. byte[] data = new byte[buf.readableBytes()];
  8. buf.readBytes(data);
  9. System.out.println(new String(data));
  10. } finally {
  11. //释放 (ByteBuf) msg
  12. ReferenceCountUtil.release(msg);
  13. }
  14. }
  15. @Override
  16. public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) {
  17. e.printStackTrace();
  18. ctx.close();
  19. }
  20. }

总结

Netty自定义协议 https://my.oschina.net/OutOfMemory/blog/290180


每天用心记录一点点。内容也许不重要,但习惯很重要!

Netty 系列(三)Netty 入门的更多相关文章

  1. Netty 系列之 Netty 高性能之道 高性能的三个主题 Netty使得开发者能够轻松地接受大量打开的套接字 Java 序列化

    Netty系列之Netty高性能之道 https://www.infoq.cn/article/netty-high-performance 李林锋 2014 年 5 月 29 日 话题:性能调优语言 ...

  2. 【读后感】Netty 系列之 Netty 高性能之道 - 相比 Mina 怎样 ?

    [读后感]Netty 系列之 Netty 高性能之道 - 相比 Mina 怎样 ? 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商 ...

  3. netty系列之:netty架构概述

    目录 简介 netty架构图 丰富的Buffer数据机构 零拷贝 统一的API 事件驱动 其他优秀的特性 总结 简介 Netty为什么这么优秀,它在JDK本身的NIO基础上又做了什么改进呢?它的架构和 ...

  4. Netty 系列三(ByteBuf).

    一.概述和原理 网络数据传输的基本单位总是字节,Netty 提供了 ByteBuf 作为它的字节容器,既解决了 JDK API 的局限性,又为网络应用程序提供了更好的 API,ByteBuf 的优点: ...

  5. Netty 系列之 Netty 高性能之道

    1. 背景 1.1. 惊人的性能数据 最近一个圈内朋友通过私信告诉我,通过使用 Netty4 + Thrift 压缩二进制编解码技术,他们实现了 10 W TPS(1 K 的复杂 POJO 对象)的跨 ...

  6. Netty系列之Netty高性能之道

    转载自http://www.infoq.com/cn/articles/netty-high-performance 1. 背景 1.1. 惊人的性能数据 最近一个圈内朋友通过私信告诉我,通过使用Ne ...

  7. 转:Netty系列之Netty高性能之道

    1. 背景 1.1. 惊人的性能数据 最近一个圈内朋友通过私信告诉我,通过使用Netty4 + Thrift压缩二进制编解码技术,他们实现了10W TPS(1K的复杂POJO对象)的跨节点远程服务调用 ...

  8. Netty系列之Netty线程模型

    Reference: http://www.infoq.com/cn/articles/netty-threading-model 1. 背景 1.1. Java线程模型的演进 1.1.1. 单线程 ...

  9. Netty系列之Netty百万级推送服务设计要点

    1. 背景 1.1. 话题来源 最近很多从事移动互联网和物联网开发的同学给我发邮件或者微博私信我,咨询推送服务相关的问题.问题五花八门,在帮助大家答疑解惑的过程中,我也对问题进行了总结,大概可以归纳为 ...

  10. 【netty】Netty系列之Netty百万级推送服务设计要点

    1. 背景 1.1. 话题来源 最近很多从事移动互联网和物联网开发的同学给我发邮件或者微博私信我,咨询推送服务相关的问题.问题五花八门,在帮助大家答疑解惑的过程中,我也对问题进行了总结,大概可以归纳为 ...

随机推荐

  1. centos73下php5.6安装GD库

    yum --enablerepo=remi-php56 install php-gd php-mysql php-mbstring php-xml php-mcrypt YUM安装的 找到了源  分分 ...

  2. linux计划任务crontab的使用

    参考网站:https://www.cnblogs.com/intval/p/5763929.html 编辑计划任务:    crontab -e 查看计划任务:    crontab -l 使用实例: ...

  3. THREE.JS 场景世界坐标和平面二维坐标互转

    <!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8&qu ...

  4. 转发 DDoS攻防战 (一) : 概述

     岁寒 然后知松柏之后凋也   岁寒 然后知松柏之后凋也 ——论语·子罕 (此图摘自<Web脚本攻击与防御技术核心剖析>一书,作者:郝永清先生)    DDoS,即 Distributed ...

  5. delphi android路径 TPath 文件路径,文件管理

    获取Android相关文档路径 delphi 新路径.文件功能 IOUtils单元,文件路径,文件管理 http://docwiki.embarcadero.com/RADStudio/Berlin/ ...

  6. Java 阿里云 邮件(带附件)发送

    简单的使用. 阿里云每天免费200封 1000封才2块钱..465端口 使用正常25 端口 不正常 package com.gwzx.framework.utils; import java.util ...

  7. Linux 的伪终端的基本原理 及其在远程登录(SSH,telnet等)中的应用

    本文介绍了linux中伪终端的创建,介绍了终端的回显.行缓存.控制字符等特性,并在此基础上解释和模拟了telnet.SSH开启远程会话的过程. 一.轻量级远程登录 之前制作的一块嵌入式板子,安装了嵌入 ...

  8. object-c语法

    Objective-C:C的超集 Objective-Objective-C是C语言的严格超集--任何C语言程序不经修改就可以直接通过Objective-C编译器,在Objective-C中使用C语言 ...

  9. <c:forEach>取得集合数量

    这个问题曾经也困扰了我好久,不过以后都没有用过也都忘记了,不过今天在做项目 的时候又遇到了,花费了很久的时间都没有成功.也试了它的很多属性,例如:varStatus.last.${status.cou ...

  10. commonCookie.js

    /** * Created with JetBrains WebStorm. * NAME: commonCookie.js */(function(window,document){ var com ...