コードのすべての行がコメントを持って、慎重に見てください。
クライアント:
package com.jym.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
/**
* @program: NettyPro
* @description: Netty服务端练习
* @author: jym
* @create: 2020/02/07
*/
public class JymNettyServer {
public static void main(String[] args) throws InterruptedException {
// 创建bossGroup,workGroup
// 1.创建两个线程组bossGroup和workGroup
// 2.bossGroup只是处理连接请求,真正的和客户端处理,会交给workGroup完成
// 3.两个都是无限循环
// 4.bossGroup和workGroup还有的子线程(NioEventLoop)的个数,默认实际CPU的核数*2
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workGroup = new NioEventLoopGroup();
try {
// 创建服务器端启动的对象,可以配置启动参数
ServerBootstrap serverBootstrap = new ServerBootstrap();
// 使用链式编程来进行设置
// 设置两个线程组
serverBootstrap.group(bossGroup,workGroup)
// 使用NioServerSocketChannel作为服务器的通道实现
.channel(NioServerSocketChannel.class)
// 设置线程队列得到连接个数
.option(ChannelOption.SO_BACKLOG,128)
// 设置保持活动连接状态
.childOption(ChannelOption.SO_KEEPALIVE,true)
// 给我们的workGroup的EventLoop对应的管道设置处理器
// 创建一个通道测试对象
.childHandler(new ChannelInitializer<SocketChannel>() {
// 给pipeline设置处理区
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new JymNettyServerHandler());
}
});
System.out.println("服务器 is ready");
// 绑定端口并且同步,生成一个ChannelFuture对象,启动服务器
ChannelFuture sync = serverBootstrap.bind(6668).sync();
// 对关闭通道进行监听
sync.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
}
クライアントハンドラ
package com.jym.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
/**
* @program: NettyPro
* @description: 1.自定义handler,需要继承netty规定好的某个HandlerAdapter(规范)
* @description: 2.这时我们的自定义handler,才能称为一个handler
* @author: jym
* @create: 2020/02/08
*/
public class JymNettyServerHandler extends ChannelInboundHandlerAdapter {
/**
* 读取数据实际(这里我们可以读取从客户端发来的消息)
* 1.ChannelHandlerContext ctx:上下文对象,含有管道pipeline,通道,地址
* 2.Object msg: 客户端发送的数据,默认是object的形式
*/
@Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("服务器读取线程:" + Thread.currentThread().getName());
System.out.println("server ctx=" + ctx);
// 将msg转成一个byteBuf,是netty提供的 不是nio的
ByteBuf byteBuf = (ByteBuf)msg;
System.out.println("客户端发送的消息是:" + byteBuf.toString(CharsetUtil.UTF_8));
System.out.println("客户端地址为:" + ctx.channel().remoteAddress().toString());
}
/**
* 数据读取完毕
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
// writeAndFlush 是write+flush,将数据写入到缓存,并刷新
// 一般讲,我们对发送的数据进行一个编码
ctx.writeAndFlush(Unpooled.copiedBuffer("hello,客户端",CharsetUtil.UTF_8));
}
/**
* 处理异常,一般是关闭连接
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
サーバー
package com.jym.netty;
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;
/**
* @program: NettyPro
* @description: Netty客户端练习
* @author: jym
* @create: 2020/02/08
*/
public class JymNettyClient {
public static void main(String[] args) throws InterruptedException {
// 客户端需要一个事件循环组
EventLoopGroup group = new NioEventLoopGroup();
try {
// 创建客户端启动对象,注意:客户端使用的不是SeverBootstrap,而是Bootstrap
Bootstrap bootstrap = new Bootstrap();
// 设置相关参数
// 设置线程组
bootstrap.group(group)
// 设置通道的实现类(反射)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel socketChannel) throws Exception {
// 加入自己的处理器
socketChannel.pipeline().addLast(new JymNettyClientHandler());
}
});
System.out.println("客户端 is ok");
// 启动客户端去连接服务器端
// 关于 ChannelFuture 要分析,涉及到netty的异步模型
ChannelFuture sync = bootstrap.connect("127.0.0.1", 6668).sync();
// 给关闭通道进行监听
sync.channel().closeFuture().sync();
} finally {
group.shutdownGracefully();
}
}
}
サーバーハンドラ
package com.jym.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
/**
* @program: NettyPro
* @description: 处理器
* @author: jym
* @create: 2020/02/08
*/
public class JymNettyClientHandler extends ChannelInboundHandlerAdapter {
/**
* 通道就绪就会触发该方法
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("client :" + ctx);
ctx.writeAndFlush(Unpooled.copiedBuffer("hello,server ~o( =∩ω∩= )m", CharsetUtil.UTF_8));
}
/**
* 通道有读取时间时触发
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf byteBuf = (ByteBuf) msg;
System.out.println("服务器回复的消息:"+ byteBuf.toString(CharsetUtil.UTF_8));
System.out.println("服务器的地址:" + ctx.channel().remoteAddress());
}
/**
* 处理异常,关闭上下文
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
テスト結果:
サーバー:
クライアント(2):
要約:
1.印刷ChannelHandlerContext各接続オブジェクトが取得され、見ることができる異なるので、チャンネル(管)のそれぞれとパイプライン(チャネル)は、クライアントコンソールによって異なり
、2つのスレッド・グループに2.bossGroupワークグループあなたは番号が指定されていない場合は、コンストラクタ内のスレッドの数を指定することができ、CPUの核のデフォルト数* 2(と、CPUサーバクアッドコア、8スレッドの数)
3.sync.channel()。closeFuture() .sync()が閉じられていないが、リスナーのため閉鎖される
コンテキストオブジェクトであるオブジェクトのほとんど得ることができます4.我々プロセッサChannelHandlerContextオブジェクト
、5たびに、クライアントとサーバーの接続をサーバ・スレッドは同じではありません
チャネルおよびパイプラインに取得することができ6.ChannelHandlerContext、とすることをあなたは私を持って、私は関係であなたを持っています
System.out.println("===========channel和pipeline的关系=============");
Channel channel = ctx.channel();
ChannelPipeline pipeline1 = channel.pipeline();
// 本质是一个双向链表,出栈入栈问题
ChannelPipeline pipeline = ctx.pipeline();
Channel channel1 = pipeline.channel();
System.out.println("pipeline1里的channel与ctx里的channel是否为同一个对象:"+ (channel==channel1));
System.out.println("channel里的pipeline1与ctx里的pipeline是否为同一个对象:"+ (pipeline1==pipeline));
System.out.println("==============================================");
出力は次のようになります。
true
true