Netty学习教程之基础使用篇

什么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方法的时候,这个方法默认已经帮我们释放了缓冲区。

总结

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

(0)

相关推荐

  • Netty学习教程之Netty与Marshalling结合发送对象

    前言 之前的一篇文章是Netty简单的学习,我们可以传递一个字符串,那么如果我们想要在Netty中传递一个对象该怎么办呢 ? 那么这个时候我们可以结合Marshalling来传递. 方法如下: 首先需要导入两个Marshalling的依赖包 jboss-marshalling-1.3.0.CR9.jar jboss-marshalling-serial-1.3.0.CR9.jar 注意:我开始学习的时候只导入了第一个jar包,没有导入第二个,结果是不报错,但是客户端和服务端之间传递不了消息.所以

  • Netty学习教程之基础使用篇

    什么Netty? Netty是由JBOSS提供的一个java开源框架.Netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速开发高性能.高可靠性的网络服务器和客户端程序. 也就是说,Netty 是一个基于NIO的客户.服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户,服务端应用.Netty相当简化和流线化了网络应用的编程开发过程,例如,TCP和UDP的socket服务开发. 我们下面编写四个类 1.用于接收数据的服务器端Socket

  • kotlin 官方学习教程之基础语法详解

    kotlin 官方学习教程之基础语法详解 Google 在今天的举行了 I/O 大会,大会主要主要展示内有容 Android O(Android 8.0)系统.Google Assistant 语音助手.Google 智能音箱.人工智能.机器学习.虚拟现实等.作为一个 Android 开发者,我关心的当然是 Android O(Android 8.0)系统了,那么关于 Android O 系统的一个重要消息是全面支持 Kotlin 编程语言,使得 Kotlin 成为了 Android 开发的官方

  • seajs学习教程之基础篇

    介绍 众所周知前端开发模块化已经是大势所趋,目前模块化的规范有很多,众所周知的有commonJS,Module/Wrappings和AMD等,而且ES6也着手开始制定模块化机制的实现.类似于c/c++的include,java中的import关键字,在js中也定义了require关键字,用以引进依赖模块. 由于规范的多样性,模块化的实现也是各有各的不同. nodejs遵从的就是commonJS规范,它有着一些形式上的约定: 1.require为函数,该函数接受一个字符串作为模块标示符 2.req

  • RxSwift学习教程之基础篇

    前言 我们在 iOS 开发过程中,几乎无时无刻都要面对异步事件的处理.例如,按键点击.数据保存..音频后台播放.交互动画展示.这些事件并不具备特定时序性,甚至它们可能同时发生. 虽然 Apple 提供了通知.代理.GCD.闭包等异步机制,但是这些机制缺乏一个统一的抽象表述.另外,这些机制在处理共享的可变数据或状态时不够清晰简练.当然,这并不是说编写优雅的异步代码不现实.毕竟与其他平台相比 iOS 的异步机制还是很强大的. 幸运的是,我们能够通过 RxSwift 优雅的处理异步代码. 至于 RxS

  • Vue + Webpack + Vue-loader学习教程之功能介绍篇

    Vue-loader 是什么? vue-loader 是一个加载器,能把如下格式的 Vue 组件转化成JavaScript模块. vue-loader 提供了一些非常酷炫的特性: ES2015默认可用: 在每个 Vue 组件内支持其他的 Webpack 加载器,如用于<style>的 SASS 和用于<template> 的 Jade. 把 <style> 和 <template> 内引用的静态资源作为模块依赖项对待,并用Webpack 加载器处理. 对每个

  • Vue + Webpack + Vue-loader学习教程之相关配置篇

    前言 之前已经介绍过了Vue + Webpack + Vue-loader的相关功能介绍,大家可以点击这篇文章了解详情.下面就来看看相关配置篇,感兴趣的可以参考学习. 使用预处理器 在 Webpack 中,所有的预处理器需要和一个相应的加载器一同使用.vue-loader 允许你用其它的 Webpack 加载器去处理 Vue 组件一部分代码.它会根据 lang 属性自动用适当的加载器去处理. CSS 例如,我们编译用 SASS 编译 <style> 标签: npm install sass-l

  • socket.io学习教程之基础介绍(一)

    前言 Web端与服务器间的实时数据传输的是一个很重要的需求,但最早只能通过AJAX轮询询实现.在WebSocket标准没有推出之前,AJAX轮询是唯一可行的方式(通过Flash浏览器也可以,但这里不做讨论).AJAX轮询原理是设置定时器,定时通过AJAX同步服务器数据.这种方式存在延时且对服务端造成很大负载.直到2011年,IETF才标准化WebSocket--一种基于TCP套接字进行收发数据的协议.现如今主流浏览器均已支持WebSocket. socket.io将数据传输部分独立出来形成了en

  • Spring boot学习教程之快速入门篇

    前言 首先来说一下为什么使用 Spring Boot,之前我用的后端 WEB 开发框架一直都是 PlayFramework的 1.2.7 版本(目前已经停止更新), 不得不说这个框架非常好用,但是由于 Play2.x 版本和 Play1.x 版本差别巨大,并且不兼容,所以现在面临着选择新的框架的问题,问了下身边的朋友,发现他们都在用 Spring ,然而我发现 Spring 的话,经常要配置各种东西,习惯了 Play 的简单明了的配置方式,确实有些不习惯 Spring ,这个时候发现了 Spri

  • Java8函数式接口的基础学习教程

    函数式接口 1.1 函数式接口概述 函数式接口:有且仅有一个抽象方法的接口 Java中的函数式编程体现就是Lambda表达式,所以函数式接口就是可以使用与Lambda使用的接口 只有确保接口只能够有且只有一个抽象方法,Lambda才能顺利的进行推导 检测接口是不是函数式接口: @FunctionalInterface 放在接口定义的上方:如果接口是函数式接口,编译通过,反之失败. 注意: 我们自己定义函数式接口的时候,@FunctionalInterface是可选的,就算不写,只要爆炸慢煮函数式

  • Javascript基础学习笔记(菜鸟必看篇)

    什么是变量? 变量是用于存储信息的容器 变量的声明 语法: var 变量名 变量名 = 值; 变量要先声明再赋值 变量可以重复赋值 变量的命名规则 变量必须以字母开头: 变量也能以$和_符号开头(不过我们不推荐这么做): 变量名称对大小写敏感(a和A是不同的变量). 语句 语句以一个分号结尾:如果省略分号,则由解析器确定语句的结尾. 有个好的编码习惯,都要以 ; 结尾 数据类型 在JavaScript中,一段信息就是一个值(value).值有不同的类型,大家最熟悉的类型是数字.字符串(strin

随机推荐