Netty入门系列(1) --使用Netty搭建服务端和客户端
引言
前面我们介绍了网络一些基本的概念,虽然说这些很难吧,但是至少要做到理解吧。有了之前的基础,我们来正式揭开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搭建服务端和客户端的更多相关文章
- Netty入门系列(2) --使用Netty解决粘包和拆包问题
前言 上一篇我们介绍了如果使用Netty来开发一个简单的服务端和客户端,接下来我们来讨论如何使用解码器来解决TCP的粘包和拆包问题 TCP为什么会粘包/拆包 我们知道,TCP是以一种流的方式来进行网络 ...
- Netty入门系列(3) --使用Netty进行编解码的操作
前言 何为编解码,通俗的来说,我们需要将一串文本信息从A发送到B并且将这段文本进行加工处理,如:A将信息文本信息编码为2进制信息进行传输.B接受到的消息是一串2进制信息,需要将其解码为文本信息才能正常 ...
- SuperSocket与Netty之实现protobuf协议,包括服务端和客户端
今天准备给大家介绍一个c#服务器框架(SuperSocket)和一个c#客户端框架(SuperSocket.ClientEngine).这两个框架的作者是园区里面的江大渔. 首先感谢他的无私开源贡献. ...
- webservice - 使用JAX-WS注解的方式快速搭建服务端和客户端
1.Define the interface import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebRe ...
- vue 快速入门 系列 —— 使用 vue-cli 3 搭建一个项目(上)
其他章节请看: vue 快速入门 系列 使用 vue-cli 3 搭建一个项目(上) 前面我们已经学习了一个成熟的脚手架(vue-cli),笔者希望通过这个脚手架快速搭建系统(或项目).而展开搭建最好 ...
- vue 快速入门 系列 —— 使用 vue-cli 3 搭建一个项目(下)
其他章节请看: vue 快速入门 系列 使用 vue-cli 3 搭建一个项目(下) 上篇 我们已经成功引入 element-ui.axios.mock.iconfont.nprogress,本篇继续 ...
- Netty学习笔记(二) 实现服务端和客户端
在Netty学习笔记(一) 实现DISCARD服务中,我们使用Netty和Python实现了简单的丢弃DISCARD服务,这篇,我们使用Netty实现服务端和客户端交互的需求. 前置工作 开发环境 J ...
- Netty 学习(一):服务端启动 & 客户端启动
Netty 学习(一):服务端启动 & 客户端启动 作者: Grey 原文地址: 博客园:Netty 学习(一):服务端启动 & 客户端启动 CSDN:Netty 学习(一):服务端启 ...
- Netty 学习(二):服务端与客户端通信
Netty 学习(二):服务端与客户端通信 作者: Grey 原文地址: 博客园:Netty 学习(二):服务端与客户端通信 CSDN:Netty 学习(二):服务端与客户端通信 说明 Netty 中 ...
随机推荐
- Leetcode周赛165
目录 找出井字棋的获胜者 思路 代码 不浪费原料的汉堡制作方案 思路 代码 统计全为 1 的正方形子矩阵 思路 代码 分割回文串 III 思路 代码 找出井字棋的获胜者 思路 模拟. 代码 class ...
- 借助模板类自动实现COM连接点接收器(Sink)
本文的更新:借助模板类自动实现COM连接点接收器(Sink)更新 (2014-06-09 17:09) 最初的代码源自free2000fly的一个标准的 COM 连接点接收器(Sink)的实现, 使用 ...
- 编译rabbitmq c++客户端
rabbitmq官网地址 rabbitmq客户端 c++版本地址 rabbitmq客户端 c版本地址 下载c++版本:根据编译说明 Pre-requisites boost-1.47.0 or new ...
- linux防止恶意采集攻防战
这两天ytkah开发的一个中大型项目被人盯上了,网站打开非常慢,查看了一下cpu.内存使用情况,30%左右占用不高,网络上下行就比较大了,IO实时流量达到40MB,IO总流量更是7TB,非常大的数据量 ...
- phpcms邮箱smtp配置163企业邮测试可用
前面我们给phpcms加了https,但是修改邮箱smtp配置一直提交不了,提示请填写接口地址,格式为:http://www.abc.com,结尾不包含"/",找了一下phpsso ...
- 01-linux-基本语法-sh文件
在一些开源库中,往往有一个 xxx.sh 的安装文件.命令“./ xxx.sh” 就可运行之. 其实内部是一些 linux 的语句.整合起来,方便使用而已. 介绍一下常用的一些Linux语句 cd b ...
- contest1 CF1084 div2 oooxx oooxo ooooo
题意: div2D (x)(x) 给出一棵树, 找出一条路径, 使得每一时刻点权和\(\ge\)边权和, 并且点权和\(-\)边权和最大 div2E (x)(o) 给出两个长度为\(n(\le 5e5 ...
- Sublime Text3 设置
主题:Spacegrey.sublime-theme 配色方案:Mariana 自动保存 参考:https://www.cnblogs.com/mzzz/p/6178341.html "sa ...
- TCP连接和连接释放(TCP的三次挥手和四次握手)
TCP的运输连接管理 TCP是面向连接的协议.运输连接是用来传送TCP报文的.TCP运输连接的建立和释放是每一次面向连接的通信中必不可少的过程.因此,运输连接就有三个阶段,即:连接建立.数据传送和连接 ...
- centos7 安装 mysql5.7.25
一.检查是否安装了mysql和mariadb,若已经安装就需要卸载. [root@localhost ~]# rpm -qa|grep mariadb // 查询出来已安装的mariadb [root ...