什么是Echo服务?
就是回写应答服务,客户端发送什么数据,服务端就响应什么数据
什么作用呢?
检测调试对应的服务 压测网络编程模型
Step1:项目创建
选择Maven
Step2:外层pom引入依赖
线程组 启动类 处理器的类名都是有相对意义的 你去看Netty源码也是一样的
Step1:Server服务端
import io.netty.bootstrap.ServerBootstrap;
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.NioServerSocketChannel;
/**
* 启动类
*/
public class EchoServer {
/**
* 端口号
*/
private int port;
private EchoServer(int port){
this.port = port;
}
public static void main(String [] args) throws InterruptedException {
int port = 8080;
//判断传入参数是否有端口号
if (args.length > 0){
port = Integer.parseInt(args[0]);
}
new EchoServer(port).run();
}
/**
* 启动流程
* 使用Netty线程模型中的Reactor主从线程模型
*/
public void run() throws InterruptedException {
//Netty封装好的主从线程模型 配置服务端线程组
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
try {
//启动类
ServerBootstrap serverBootstrap = new ServerBootstrap();
//传入线程组 链式调用
serverBootstrap.group(bossGroup,workGroup)
.channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//一整串的handler串联起来
ch.pipeline().addLast(new EchoServerHandler());
}
});
System.out.println("Echo 服务器启动ing");
//绑定端口 同步等待 异步的 但需要等待绑定
ChannelFuture channelFuture =serverBootstrap.bind(port).sync();
//等待服务端监听端口关闭
channelFuture.channel().closeFuture().sync();
}catch (Exception e){
e.printStackTrace();
}finally {
//释放线程池 优雅退出
workGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
Step2:服务端处理器
package net.jhclass.echo;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
/**
* 处理器
*/
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
/**
* 数据读写
* @param ctx
* @param msg
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//转换
ByteBuf data = (ByteBuf) msg;
System.out.println("服务端收到数据:"+data.toString(CharsetUtil.UTF_8));
//回写
ctx.writeAndFlush(data);
}
/**
* 读写完成
* @param ctx
* @throws Exception
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("EchoServerHandler channelReadComplete");
}
/**
* 异常捕获
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
Step3:客户端
package net.jhclass.echo;
import io.netty.bootstrap.Bootstrap;
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;
import java.net.InetSocketAddress;
public class EchoClient {
private int port;
private String host;
private EchoClient(String host,int port){
this.host = host;
this.port = port;
}
public static void main(String [] args) throws InterruptedException {
new EchoClient("127.0.0.1",8080).start();
}
/**
* 开启连接
*/
public void start() throws InterruptedException {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.remoteAddress(new InetSocketAddress(host,port))
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new EchoClientHandler());
}
});
//连接到服务端 connect是异步连接,在调用同步等待sync,等待连接成功
ChannelFuture channelFuture = bootstrap.connect().sync();
//阻塞直到客户端通道关闭
channelFuture.channel().closeFuture().sync();
}finally {
//优雅退出 释放NIO线程组
group.shutdownGracefully();
}
}
}
Step4:客户端处理器
package net.jhclass.echo;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf msg) throws Exception {
//打印服务端发送的数据
System.out.println("Client received:"+msg.toString(CharsetUtil.UTF_8));
}
/**
* channe激活监听
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("Active");
//向服务端写数据
//writ需要先刷到管道里 flush才是推过去
ctx.writeAndFlush(Unpooled.copiedBuffer("jhclass.net",CharsetUtil.UTF_8));
}
/**
* 数据处理完后回调
* @param ctx
* @throws Exception
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("EchoClientHandler channelReadComplete");
}
/**
* 异常捕获
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
Step5:测试
1.首先启动EchoServer,等待接收请求中
2.启动EchoClient客户端发送 jhclass.net 可以看到执行顺序 Active监听器最先执行
3.EchoServer收到数据
客户端停掉后这段是不是很眼熟 zookeeper底层就是使用的Netty
流程分析
1)EcentLoop和EventLoopGroup
就是两个线程组 先这样去理解
2)Bootstrapt启动引导类
这个可不是前端的框架 任何一个系统都需要启动的过程,这个过程需要配置文件,预加载等 所以Server端需要这个,并且将两个线程组给它
3)Channel 生命周期 状态变化
客户端和服务端建立的连接 Socket连接 建立>连接成功>读取数据
4)ChannelHandler和ChannelPipline
ChannelHandler:Channel对应处理类,比如这个连接需要做日志打点,处理返回等
ChannelPipline:Handler打比方就是一个工人,Pipline就是流水线,你可以往里面加很多Handler
博主Netty专栏对上面类有重点分析文章 不懂的可以去看一看