第一步:设置开发环境

• 安装JDK,下载地址http://www.oracle.com/technetwork/java/javase/archive-139210.html
   • 下载netty包,下载地址http://netty.io/
   • 安装Eclipse

导入包

第二步:编写一个应答服务器

   通过创建ServerBootstrap对象来启动服务器,然后配置这个对象的相关选项,如端口、线程模式、事件循环,并且添加逻辑处理程序用来处理业务逻辑(下面是个简单的应答服务器例子)

package com.lzh.netty;
import io.netty.bootstrap.ServerBootstrap;
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;
/**
* 通过创建ServerBootstrap对象来启动服务器,然后配置这个对象的相关选项,如端口、线程模式、事件循环,并且添加逻辑处理程序用来处理业务逻辑
*/
public class NettyServer { private final int port;
public NettyServer(int port){
this.port=port;
}
public void start() throws Exception{
//1.第一个线程组是用于接收Client端连接的
EventLoopGroup bossGroup = new NioEventLoopGroup();
//2.第二个线程组是用于实际的业务处理的
EventLoopGroup workerGroup = new NioEventLoopGroup();
try{
//创建ServerBootstrap实例来引导绑定和启动服务器
ServerBootstrap b=new ServerBootstrap();
//1.设置InetSocketAddress让服务器监听某个端口已等待客户端连接。
//2.指定NIO的模式,如果是客户端就是NioSocketChannel
//3.调用childHandler方法传ChannelInitializer类型的参数,ChannelInitializer是个抽象类,所以需要实现initChannel方法,这个方法就是用来设置ChannelHandler
b.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)//连接数
.option(ChannelOption.SO_SNDBUF, 32*1024)//设置发送缓冲区
.option(ChannelOption.SO_RCVBUF, 32*1024)//设置接收缓冲区
.option(ChannelOption.SO_KEEPALIVE, true)//长连续
.childHandler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel cn) throws Exception {
cn.pipeline().addLast(new NettyServerHandler());
}
});
//绑定服务器等待直到绑定完成,调用sync()方法会阻塞直到服务器完成绑定,然后服务器等待通道关闭,因为使用sync(),所以关闭操作也会被阻塞
ChannelFuture f=b.bind(port).sync();
if (f.isSuccess()) {
System.out.println("启动Netty服务成功,端口号:" + this.port);
}
//这里会一直等待,直到socket被关闭
f.channel().closeFuture().sync();
}finally{
//关闭EventLoopGroup和释放所有资源,包括创建的线程
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception{
new NettyServer(8000).start();
}
}

实现服务器业务逻辑

package com.lzh.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter; public class NettyServerHandler extends ChannelInboundHandlerAdapter{ //重写channelRead方法,这个方法在任何时候都会被调用来接收数据
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf=(ByteBuf)msg;
byte [] bt=new byte[buf.readableBytes()];
buf.readBytes(bt);
System.out.println("Server received: " + new String(bt,"utf-8"));
//服务器返回客户端信息
String str="Hi client !!!";
ctx.writeAndFlush(Unpooled.copiedBuffer(str.getBytes()));
}
//重写ChannelHandler的exceptionCaught方法可以捕获服务器的异常,
//比如客户端连接服务器后强制关闭,服务器会抛出"客户端主机强制关闭错误",通过重写exceptionCaught方法就可以处理异常
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
cause.printStackTrace();
ctx.close();
}
}

说明:

1.Netty使用多个Channel Handler来达到对事件处理的分离,因为可以很容的添加、更新、删除业务逻辑处理handler。Handler很简单,它的每个方法都可以被重写,它的所有的方法中只有channelRead方法是必须要重写的。

2.重写ChannelHandler的exceptionCaught方法可以捕获服务器的异常,比如客户端连接服务器后强制关闭,服务器会抛出"客户端主机强制关闭错误",通过重写exceptionCaught方法就可以处理异常,比如发生异常后关闭ChannelHandlerContext。

第三步:编写应答程序的客户端

package com.lzh.netty;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel; public class NettyClient { private final String host;
private final int port;
public NettyClient(String host,int port){
this.host=host;
this.port=port;
}
public void start() throws Exception{
//创建EventLoopGroup对象并设置到Bootstrap中,EventLoopGroup可以理解为是一个线程池,这个线程池用来处理连接、接受数据、发送数据
EventLoopGroup group =new NioEventLoopGroup();
try{
//创建Bootstrap对象用来引导启动客户端
Bootstrap b=new Bootstrap();
// 1.创建InetSocketAddress并设置到Bootstrap中,InetSocketAddress是指定连接的服务器地址
// 2.添加一个ChannelHandler,客户端成功连接服务器后就会被执行
b.group(group).channel(NioSocketChannel.class)
//.remoteAddress(new InetSocketAddress(host,port))
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//添加一个“入站”handler到ChannelPipeline
ch.pipeline().addLast(new NettyClientHandler());
}
});
//调用Bootstrap.connect()来连接服务器
ChannelFuture f=b.connect(host, port).sync(); f.channel().writeAndFlush(Unpooled.copiedBuffer("hello netty".getBytes()));
Thread.sleep(1000);
f.channel().writeAndFlush(Unpooled.copiedBuffer("hello netty1".getBytes()));
Thread.sleep(1000);
f.channel().writeAndFlush(Unpooled.copiedBuffer("hello netty2".getBytes()));
f.channel().closeFuture().sync();
}finally{
//关闭EventLoopGroup来释放资源
group.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws Exception{
new NettyClient("127.0.0.1", 8000).start();
} }

实现客户端的业务逻辑

package com.lzh.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
public class NettyClientHandler extends SimpleChannelInboundHandler<ByteBuf>{
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg)throws Exception {
ByteBuf buf=(ByteBuf)msg;
byte [] bt=new byte[buf.readableBytes()];
buf.readBytes(bt);
System.out.println("Client received: " + new String(bt,"utf-8"));
System.out.println("Client received: " + ByteBufUtil.hexDump(msg.readBytes(msg.readableBytes())));
}
//重写ChannelHandler的exceptionCaught方法可以捕获服务器的异常,
//比如客户端连接服务器后强制关闭,服务器会抛出"客户端主机强制关闭错误",通过重写exceptionCaught方法就可以处理异常
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
cause.printStackTrace();
ctx.close();
}
/**
* 1.为什么在这里使用的是SimpleChannelInboundHandler而不使用ChannelInboundHandlerAdapter?
* 主要原因是ChannelInboundHandlerAdapter在处理完消息后需要负责释放资源。在这里将调用ByteBuf.release()来释放资源。
* SimpleChannelInboundHandler会在完成channelRead0后释放消息,这是通过Netty处理所有消息的ChannelHandler实现了ReferenceCounted接口达到的。
* 2.为什么在服务器中不使用SimpleChannelInboundHandler呢?因为服务器要返回相同的消息给客户端,在服务器执行完成写操作之前不能释放调用读取到的消息,因为写操作是异步的,一旦写操作完成后,Netty中会自动释放消息。
*/
}

第四步:编译和运行服务器和客户端

服务端启动

客户端启动

第二章:第一个Netty程序的更多相关文章

  1. Netty In Action中国版 - 第二章:第一Netty程序

    本章介绍 获得Netty4最新的版本号 设置执行环境,以构建和执行netty程序 创建一个基于Netty的server和client 拦截和处理异常 编制和执行Nettyserver和client 本 ...

  2. Pro ASP.NET MVC –第二章 第一个MVC程序

    学习一个软件开发框架的最有效的方式就是了解并使用它.在本章,你将会创建一个简单基于ASP.NET MVC Framework的数据-实体应用程序.我们会该程序划分成若干小块,每次介绍一个部分,以便你能 ...

  3. Netty(1):第一个netty程序

    为什么选择Netty netty是业界最流行的NIO框架之一,它的健壮型,功能,性能,可定制性和可扩展性都是首屈一指的,Hadoop的RPC框架Avro就使用了netty作为底层的通信框架,此外net ...

  4. Prism 文档 第二章 初始化Prism应用程序

                                                                           第二章 初始化Prism应用程序 本章将讨论为了使一个Pr ...

  5. C#语言————第一章 第一个C#程序

    第一章    第一个C#程序 ******************C#程序***************     ①:建立项目:文件-->新建-->项目-->c#-->控制台程 ...

  6. JavaWeb学习总结第二篇--第一个JavaWeb程序

    JavaWeb学习总结第二篇—第一个JavaWeb程序 最近我在学院工作室学习并加入到研究生的项目中,在学长学姐的带领下,进入项目实践中,为该项目实现一个框架(用已有框架进行改写).于是我在这里记录下 ...

  7. C#第一章 第一个C#程序

    第一个C#程序 namespace 是C#中组织代码的方式,它的作用那个类似java中的包 using 在Java中作用如果导入其他包 应该是用import关键字而在C#中应使用using关键字来引用 ...

  8. 从零开始学习Hadoop--第2章 第一个MapReduce程序

    1.Hadoop从头说 1.1 Google是一家做搜索的公司 做搜索是技术难度很高的活.首先要存储很多的数据,要把全球的大部分网页都抓下来,可想而知存储量有多大.然后,要能快速检索网页,用户输入几个 ...

  9. Java面向对象编程 第二章 第一个Java应用

    2.1创建Java源文件 Java应用由一个或多个扩展名为".java"的文件构成,这些文件被称为Java源文件,从编译的角度,则被称为编译单元. 本章包含两个Java源文件:Do ...

随机推荐

  1. 移植Cocos2D到Android平台的原理

    幸运的,SpriteBuilder使得适配(安卓)多种多样的屏幕尺寸变得容易起来,因为Android Xcode插件允许你使用任何Cocos2D的特性并且可以继续使用很多iOS的框架(framewor ...

  2. TableLayout和Viewpager实现切换

    因为我是在之前的基础上写的,所以这个TableLayout和Viewpager实际上是写在Fragment上的.要写到Activity里其实也是一样的啦. 先看效果图,原谅我不会动态图,只能截个图啦 ...

  3. 双向链表设计与API实现

    为什么需要双向链表? 单链表的结点都只有一个指向下一个结点的指针 单链表的数据元素无法直接访问其前驱元素 逆序访问单链表中的元素是极其耗时的操作! 双向链表的定义 在单链表的结点中增加一个指向其前驱的 ...

  4. STL算法设计理念 - 谓词,一元谓词demo

    谓词: 一元函数对象:函数参数1个: 二元函数对象:函数参数2个: 一元谓词 函数参数1个,函数返回值是bool类型,可以作为一个判断式 谓词可以使一个仿函数,也可以是一个回调函数. demo 一元谓 ...

  5. SpriteBuilder中如何简单的重置APP保存的数据

    在任意一款APP中,我们可能需要在磁盘上保存一些游戏数据,以便在下一次运行APP时恢复游戏数据. 但是由于在测试阶段,我们需要快速恢复初始状态的游戏数据,该如何做呢? 非常简单,只需要将APP从真机或 ...

  6. 关机充电如何实现短按pwrkey灭屏

    目前关机充电PWRKEY实现长按开机和短按亮屏功能,灭屏是根据BL_SWITCH_TIMEOUTS时间,自动灭屏的:如果需要实现PWRKEY主动灭屏,请按照如下方法修改:     alps/media ...

  7. Linux文件与目录管理 - ls, cp, mv

    [root@www ~]# ls [-aAdfFhilnrRSt] 目录名称 [root@www ~]# ls [--color={never,auto,always}] 目录名称 [root@www ...

  8. 安卓学习笔记一 Activity延迟转跳实现欢迎界面

    新人学习安卓,为了刚好的学习,现做如下笔记..同时希望自己的经验可以帮助新人们学习入门. 几乎每个app都有个欢迎界面,我们可以使用Activity转跳来实现. 首先建立一个MainActivity ...

  9. android EventBus详解(三)

    post()方法调用流程 我们继续来看EventBus类,的另一个入口方法post() //已省略部分代码 public void post(Object event) { PostingThread ...

  10. git push 小结

    $ git push ssh://git@dev.lemote.com/rt4ls.git master // 把本地仓库提交到远程仓库的master分支中 $ git remote add orig ...