目录
目标
Netty版本
Netty官方API
客户端如何与服务器建立连接&连接成功后的操作方式
实现
如何处理客户端与服务器连接关闭后的操作
正确关闭连接的方式
方法一
方法二
了解Netty如何处理客户端与服务器之间的连接与关闭问题。
io.netty
netty-all
4.1.87.Final
Netty API Reference (4.1.87.Final)https://netty.io/4.1/api/index.html
Bootstrap的connect方法是实现了客户端与服务器之间的连接。需要明确一点,客户端与服务器之间的连接不是main线程在做,而是由我们指定的EventLoop来做。如此看来,connect方法是异步非阻塞的,如果连接还没有建立好就无法获取Channel,因此,ChannelFuture有2种方法处理连接建立后的操作,其中:
package com.ctx.netty;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringEncoder;
import lombok.extern.slf4j.Slf4j;
import java.nio.charset.Charset;
@Slf4j
public class NettyClient {
/**
* 建立连接后的操作:
* 方法一:用sync()方法。
*/
public void fun(ChannelFuture channelFuture) throws InterruptedException {
/**
* 如果连接还没有建立好就无法获取Channel,因此,有了sync()方法,
* 作用是阻塞等到连接建立好程序才向下运行。
*/
channelFuture.sync();
Channel channel = channelFuture.channel();
//打印main线程
log.info("channel==========={}",channel);
//向服务器发送数据
channel.writeAndFlush("Hello world!");
}
/**
* 建立连接后的操作:
* 方法二:用addListener()方法。异步调用回调对象的operationComplete方法。
*/
public void fun2(ChannelFuture channelFuture) throws InterruptedException {
//把ChannelFutureListener对象传递给EventLoopGroup线程,EventLoopGroup线程建立好连接以后调用operationComplete方法。
channelFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
Channel channel = channelFuture.channel();
//打印nioEventLoopGroup线程
log.info("channel==========={}",channel);
//向服务器发送数据
channel.writeAndFlush("Hello world!");
}
});
}
public static void main(String[] args) throws InterruptedException {
//启动Netty客户端
Bootstrap bootstrap = new Bootstrap();
//选择EventLoop
bootstrap.group(new NioEventLoopGroup());
//选择客户端Channel实现,Channel是数据的传输通道。
bootstrap.channel(NioSocketChannel.class);
//添加处理器,
bootstrap.handler(new ChannelInitializer() {
//连接建立后初始化Channel
@Override
protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
//把字符串编码成ByteBuf
nioSocketChannel.pipeline().addLast(new StringEncoder(Charset.forName("UTF-8")));
}
});
/**
* Netty客户端和服务端建立连接是由EventLoopGroup线程负责的,而不是由main线程负责的。
* 因此,这个建立连接的过程是异步非阻塞操作。
*/
ChannelFuture channelFuture = bootstrap.connect("localhost", 8999);
//new NettyClient().fun( channelFuture);
new NettyClient().fun2( channelFuture);
}
}
Channel的close方法用来关闭客户端与服务器之间地连接,但是close方法是个异步方法,如果需要关闭连接才处理后续的业务则要考虑不同线程的执行顺序问题。
package com.zteict.trans.test;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.nio.charset.Charset;
public class RespClient {
public static void main(String[] args) throws InterruptedException {
NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup();
try {
//启动Netty客户端
Bootstrap bootstrap = new Bootstrap();
//选择EventLoop
bootstrap.group(nioEventLoopGroup);
//选择客户端Channel实现,Channel是数据的传输通道。
bootstrap.channel(NioSocketChannel.class);
//添加处理器,
bootstrap.handler(new ChannelInitializer() {
//连接建立后初始化Channel
@Override
protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
nioSocketChannel.pipeline().addLast(new ChannelInboundHandlerAdapter() {
@Override
public void channelActive(ChannelHandlerContext ctx) {
}
//接收响应
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
}
});
}
});
ChannelFuture channelFuture = bootstrap.connect("172.19.216.135", 6379).sync();
channelFuture.channel().close();
channelFuture.channel().closeFuture().sync();
}finally{
System.out.println("连接关闭了。");
nioEventLoopGroup.shutdownGracefully();
}
}
}
package com.ctx.netty;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringEncoder;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class NettyClient {
public static void main(String[] args) throws InterruptedException {
NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup();
//启动Netty客户端
Bootstrap bootstrap = new Bootstrap();
//选择EventLoop
bootstrap.group(nioEventLoopGroup);
//选择客户端Channel实现,Channel是数据的传输通道。
bootstrap.channel(NioSocketChannel.class);
//添加处理器,
bootstrap.handler(new ChannelInitializer() {
//连接建立后初始化Channel
@Override
protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
//把字符串编码成ByteBuf
nioSocketChannel.pipeline().addLast(new StringEncoder());
}
});
ChannelFuture channelFuture = bootstrap.connect("localhost", 8999);
channelFuture.sync();
Channel channel = channelFuture.channel();
log.debug("{}"+channel);
channel.writeAndFlush("Hello World!") ;
//close()是异步方法,如果该方法下面没有阻塞,则无法保证线程的执行顺序。
channel.close();
channelFuture= channel.closeFuture();
//关闭连接的那个线程负责执行该方法。
channelFuture.addListener(new ChannelFutureListener() {
//关闭连接的那个线程负责执行该方法。
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
log.info("连接关闭了。");
/**
* 优雅地关闭nioEventLoopGroup,即:
* 拒绝接收新的任务,完成现有的任务。
*/
nioEventLoopGroup.shutdownGracefully();
}
});
}
}