引言

前面我们介绍了网络一些基本的概念,虽然说这些很难吧,但是至少要做到理解吧。有了之前的基础,我们来正式揭开Netty这神秘的面纱就会简单很多。

服务端

public class PrintServer {

    public void bind(int port) throws Exception {
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
.option(ChannelOption.SO_BACKLOG, 1024) //6
.childHandler(new ChannelInitializer<SocketChannel>() { //7
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new PrintServerHandler());
}
}); ChannelFuture f = b.bind(port).sync(); //8 f.channel().closeFuture().sync(); //9
} finally {
// 优雅退出,释放线程池资源
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
} /**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
int port = 8080;
new TimeServer().bind(port);
}
}

我们来分析一下上面的这段代码(下面的每一点对应上面的注释)

1~2:首先我们创建了两个NioEventLoopGroup实例,它是一个由Netty封装好的包含NIO的线程组。为什么创建两个?我想经过前面的学习大家应该都清楚了。对,因为Netty的底层是IO多路复用,bossGroup 是用于接收客户端的连接,原理就是一个实现的Selector的Reactor线程。而workerGroup用于进行SocketChannel的网络读写。

3:创建一个ServerBootstrap对象,可以把它想象成Netty的入口,通过这类来启动Netty,将所需要的参数传递到该类当中,大大降低了的开发难度。

4:将两个NioEventLoopGroup实例绑定到ServerBootstrap对象中。

5:创建Channel(典型的channel有NioSocketChannel,NioServerSocketChannel,OioSocketChannel,OioServerSocketChannel,EpollSocketChannel,EpollServerSocketChannel),这里创建的是NIOserverSocketChannel,它的功能可以理解为当接受到客户端的连接请求的时候,完成TCP三次握手,TCP物理链路建立成功。并将该“通道”与workerGroup线程组的某个线程相关联。

6:设置参数,这里设置的SO_BACKLOG,意思是客户端连接等待队列的长度为1024.

7:建立连接后的具体Handler。就是我们接受数据后的具体操作,例如:记录日志,对信息解码编码等。

8:绑定端口,同步等待成功

9:等待服务端监听端口关闭

绑定该服务端的Handler

public class PrintServerHandler extends ChannelHandlerAdapter {

    @Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
ByteBuf buf = (ByteBuf) msg; //1
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req); //将缓存区的字节数组复制到新建的req数组中
String body = new String(req, "UTF-8");
System.out.println(body);
String response= "打印成功";
ByteBuf resp = Unpooled.copiedBuffer(response.getBytes());
ctx.write(resp); //2
} @Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush(); //3
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
ctx.close();
}
}

PrintServerHandler 继承 ChannelHandlerAdapter ,在这里它的功能为 打印客户端发来的数据并且返回客户端打印成功。

我们只需要实现channelRead,exceptionCaught,前一个为接受消息具体逻辑的实现,后一个为发生异常后的具体逻辑实现。

1:我们可以看到,接受的消息被封装为了Object ,我们将其转换为ByteBuf ,前一章的讲解中也说明了该类的作用。我们需要读取的数据就在该缓存类中。

2~3:我们将写好的数据封装到ByteBuf中,然后通过write方法写回到客户端,这里的3调用flush方法的作用为,防止频繁的发送数据,write方法并不直接将数据写入SocketChannel中,而是把待发送的数据放到发送缓存数组中,再调用flush方法发送数据。

客户端

public class PrintClient {

    public void connect(int port, String host) throws Exception {
EventLoopGroup group = new NioEventLoopGroup(); //1
try {
Bootstrap b = new Bootstrap(); //2
b.group(group) //3
.channel(NioSocketChannel.class) //4
.option(ChannelOption.TCP_NODELAY, true) //5
.handler(new ChannelInitializer<SocketChannel>() { //6
@Override
public void initChannel(SocketChannel ch)
throws Exception {
ch.pipeline().addLast(new PrintClientHandler());
}
}); ChannelFuture f = b.connect(host, port).sync(); //7
f.channel().closeFuture().sync(); //8
} finally {
// 优雅退出,释放NIO线程组
group.shutdownGracefully();
}
} /**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
int port = 8080;
new TimeClient().connect(port, "127.0.0.1");
}
}

我们继续来分析一下上面的这段代码(下面的每一点对应上面的注释)

1:区别于服务端,我们在客户端只创建了一个NioEventLoopGroup实例,因为客户端你并不需要使用I/O多路复用模型,需要有一个Reactor来接受请求。只需要单纯的读写数据即可

2:区别于服务端,我们在客户端只需要创建一个Bootstrap对象,它是客户端辅助启动类,功能类似于ServerBootstrap。

3:将NioEventLoopGroup实例绑定到Bootstrap对象中。

4:创建Channel(典型的channel有NioSocketChannel,NioServerSocketChannel,OioSocketChannel,OioServerSocketChannel,EpollSocketChannel,EpollServerSocketChannel),区别与服务端,这里创建的是NIOSocketChannel.

5:设置参数,这里设置的TCP_NODELAY为true,意思是关闭延迟发送,一有消息就立即发送,默认为false。

6:建立连接后的具体Handler。注意这里区别与服务端,使用的是handler()而不是childHandler()。handler和childHandler的区别在于,handler是接受或发送之前的执行器;childHandler为建立连接之后的执行器。

7:发起异步连接操作

8:当代客户端链路关闭

绑定该客户端的Handler

public class PrintClientHandler extends ChannelHandlerAdapter {

    private static final Logger logger = Logger
.getLogger(TimeClientHandler.class.getName()); private final ByteBuf firstMessage; /**
* Creates a client-side handler.
*/
public TimeClientHandler() {
byte[] req = "你好服务端".getBytes();
firstMessage = Unpooled.buffer(req.length); //1
firstMessage.writeBytes(req); } @Override
public void channelActive(ChannelHandlerContext ctx) {
ctx.writeAndFlush(firstMessage); //2
} @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) //3
throws Exception {
ByteBuf buf = (ByteBuf) msg;
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
String body = new String(req, "UTF-8");
System.out.println("服务端回应消息 : " + body);
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { //4
// 释放资源
System.out.println("Unexpected exception from downstream : "
+ cause.getMessage());
ctx.close();
}
}

PrintClientHandler 继承 ChannelHandlerAdapter ,在这里它的功能为 发送数据并打印服务端发来的数据。

我们只需要实现channelActive,channelRead,exceptionCaught,第一个为建立连接后立即执行,后两个与一个为接受消息具体逻辑的实现,另一个为发生异常后的具体逻辑实现。

1:将发送的信息封装到ByteBuf中。

2:发送消息。

3:接受客户端的消息并打印

4:发生异常时,打印异常信息,释放客户端资源

总结

这是一个入门程序,对应前面所讲的I/O多路复用模型以及NIO的特性,能很有效的理解该模式的编程方式。如果这几段代码看着很费劲,那么可以看看之前博主的Netty基础系列。

如果博主哪里说得有问题,希望大家提出来,一起进步~

Netty入门系列(1) --使用Netty搭建服务端和客户端的更多相关文章

  1. Netty入门系列(2) --使用Netty解决粘包和拆包问题

    前言 上一篇我们介绍了如果使用Netty来开发一个简单的服务端和客户端,接下来我们来讨论如何使用解码器来解决TCP的粘包和拆包问题 TCP为什么会粘包/拆包 我们知道,TCP是以一种流的方式来进行网络 ...

  2. Netty入门系列(3) --使用Netty进行编解码的操作

    前言 何为编解码,通俗的来说,我们需要将一串文本信息从A发送到B并且将这段文本进行加工处理,如:A将信息文本信息编码为2进制信息进行传输.B接受到的消息是一串2进制信息,需要将其解码为文本信息才能正常 ...

  3. SuperSocket与Netty之实现protobuf协议,包括服务端和客户端

    今天准备给大家介绍一个c#服务器框架(SuperSocket)和一个c#客户端框架(SuperSocket.ClientEngine).这两个框架的作者是园区里面的江大渔. 首先感谢他的无私开源贡献. ...

  4. webservice - 使用JAX-WS注解的方式快速搭建服务端和客户端

    1.Define the interface import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebRe ...

  5. vue 快速入门 系列 —— 使用 vue-cli 3 搭建一个项目(上)

    其他章节请看: vue 快速入门 系列 使用 vue-cli 3 搭建一个项目(上) 前面我们已经学习了一个成熟的脚手架(vue-cli),笔者希望通过这个脚手架快速搭建系统(或项目).而展开搭建最好 ...

  6. vue 快速入门 系列 —— 使用 vue-cli 3 搭建一个项目(下)

    其他章节请看: vue 快速入门 系列 使用 vue-cli 3 搭建一个项目(下) 上篇 我们已经成功引入 element-ui.axios.mock.iconfont.nprogress,本篇继续 ...

  7. Netty学习笔记(二) 实现服务端和客户端

    在Netty学习笔记(一) 实现DISCARD服务中,我们使用Netty和Python实现了简单的丢弃DISCARD服务,这篇,我们使用Netty实现服务端和客户端交互的需求. 前置工作 开发环境 J ...

  8. Netty 学习(一):服务端启动 & 客户端启动

    Netty 学习(一):服务端启动 & 客户端启动 作者: Grey 原文地址: 博客园:Netty 学习(一):服务端启动 & 客户端启动 CSDN:Netty 学习(一):服务端启 ...

  9. Netty 学习(二):服务端与客户端通信

    Netty 学习(二):服务端与客户端通信 作者: Grey 原文地址: 博客园:Netty 学习(二):服务端与客户端通信 CSDN:Netty 学习(二):服务端与客户端通信 说明 Netty 中 ...

随机推荐

  1. 从架构开始谈dubbo(一)

    架构发展史 一.单体应用架构      当网站流量很小时,所有的功能写在一个项目中,打包部署在tomcat中.          例如:公司管理系统,超市的收银系统         也可以将单体应用部 ...

  2. 进程及Python实现

    进程杂谈 #进程就是正在执行的一个过程,是对正在运行程序的一个抽象 #进程由程序.数据集和进程控制块(最重要的,进程切换 状态如何保存,恢复和记录)组成 """ 进程调度 ...

  3. Browse Princeton's Series (by Date) in Princeton Economic History of the Western World

    Browse Princeton's Series (by Date) in Princeton Economic History of the Western World Joel Mokyr, S ...

  4. js日志组件封装

    js日志组件~~ 1 function Logger(level) { if (!(this instanceof Logger)) { return new Logger(); } var ERRO ...

  5. epoll及实现http多任务(python)

    1.epoll用到了文件描述符的概念: 首先,操作系统中一切皆文件 文件与文件描述符fd 文件是应用程序与系统(包括特定硬件设备)之间的桥梁,而文件描述符就是应用程序使用这个"桥梁" ...

  6. ESA2GJK1DH1K升级篇: 远程升级准备工作: 安装Web服务器

    前言 大家可以安装Apache,Tomcat,nginx 等Web服务器软件,这篇文章安装 OpenResty 作为Web服务器软件,该软件安装在云端电脑,如果想 安装到自己本地电脑实现该功能,可使用 ...

  7. Python实现电子词典

    代码一览: dictionary/├── code│   ├── client.py│   ├── func.py│   ├── server.py│   └── settings.py├── dat ...

  8. 洛谷 p1516 青蛙的约会 题解

    dalao们真是太强了,吊打我无名蒟蒻 我连题解都看不懂,在此篇题解中,我尽量用语言描述,不用公式推导(dalao喜欢看公式的话绕道,这篇题解留给像我一样弱的) 进入正题 如果不会扩展欧里几德的话请先 ...

  9. 【CF10D】 LCIS

    题目链接 最长公共上升子序列 \(f[i][j]\)表示\(A\)的前\(i\)个数,匹配\(B\)的第\(j\)个数,且\(B[j]\)必选时的最长公共上升子序列长度 转移: if(A[i]==B[ ...

  10. nuxt或者vue,axios中如何发送多个请求

    在使用vue或者nuxt中,我们需要使用axios去发送多个http请求,参考了axios的官方说明你也许会想到使用axios.all发送请求,但是这样可能会出现一些异常错误: (node:9360) ...