服务端:
package netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
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 TimeServer {
public void bind(int port){
//NioEventLoopGroup是一个线程组,它包含了一组NIO线程,
// 这里的两个线程组一个是用于服务端接受客户端的连接,
// 另一个用于SocketChannel的网络读写
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//netty用于启动NIO服务端的辅助启动类
ServerBootstrap b = new ServerBootstrap();
//设置线程组
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)//设置channel
.option(ChannelOption.SO_BACKLOG, 1024)//设置channel的TCP参数
.childHandler(new ChildChannelHandler());//绑定IO事件处理类
//绑定监听端口,调用同步阻塞方法等待绑定完成
ChannelFuture f = b.bind(port).sync();
//阻塞,等待服务端链路关闭后main函数才退出
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//优雅退出,释放跟shutdownGracefully相关联的所有资源
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
private class ChildChannelHandler extends ChannelInitializer{
@Override
protected void initChannel(SocketChannel serverSocket) throws Exception {
//增加时间处理类
serverSocket.pipeline().addLast(new TimeServerHandler());
}
}
public static void main(String[] args) {
int port = 8080;
new TimeServer().bind(port);
}
}
服务端Handler:
package netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import java.util.Date;
/**
* 对网络事件进行读写操作
*/
public class TimeServerHandler extends ChannelHandlerAdapter {
/**
* 当异常发生时
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//super.exceptionCaught(ctx, cause);
ctx.close();
}
/**
* 读取缓冲区里面的数据,处理并返回
* @param ctx
* @param msg
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//super.channelRead(ctx, msg);
ByteBuf buf = (ByteBuf) msg;
//buf.readableBytes()返回缓冲区可读的字节数
byte [] bytes = new byte[buf.readableBytes()];
//读取缓冲区的数据至byte数组
buf.readBytes(bytes);
String data = new String(bytes,"UTF-8");
String currentTime = "query time order".equals(data)? new Date(System.currentTimeMillis()).toString(): "bad order";
//复制bytes数组的内容,构造一个缓冲区对象ByteBuf
ByteBuf rsp = Unpooled.copiedBuffer(currentTime.getBytes());
//异步发送消息给客户端
ctx.write(rsp);
}
/**
*
* @param ctx
* @throws Exception
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
// super.channelReadComplete(ctx);
//ctx.write(rsp)发送消息时不是马上发送,而是将消息发送到发送缓冲区,等到读完成后
//再调用flush方法将发送缓冲区的消息全部写到SocketChannel中,发送给客户端
ctx.flush();
}
}
客户端:
package netty;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
/**
* netty的客户端
*/
public class TimeClient {
public void connect(String host, int port){
//NioEventLoopGroup是一个线程组,它包含了一组NIO线程
EventLoopGroup group = new NioEventLoopGroup();
try {
//客户端辅助启动类
Bootstrap b = new Bootstrap();
//设置线程组
b.group(group)
.channel(NioSocketChannel.class)//设置Channel
.option(ChannelOption.TCP_NODELAY, true)//设置TCP的参数
.handler(new ChannelInitializer() {//匿名内部类设置handler
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new TimeClientHandler());
}
});
//异步连接客户端,同步阻塞直到连接成功
ChannelFuture f = b.connect(host, port).sync();
//阻塞,等待客户端链路关闭后main函数才退出
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) {
String host = "127.0.0.1";
int port = 8080;
new TimeClient().connect(host, port);
}
}
客户端的Handler:
package netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
public class TimeClientHandler extends ChannelHandlerAdapter {
private ByteBuf firstMessage;
/**
* 初始化ByteBuf,初始化发送给服务端的消息
*/
public TimeClientHandler(){
byte [] bytes = "query time order".getBytes();
firstMessage = Unpooled.buffer(bytes.length);
firstMessage.writeBytes(bytes);
}
/**
* 连接服务端成功后开始发送消息
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//super.channelActive(ctx);
ctx.writeAndFlush(firstMessage);
}
/**
* 读取客户端的返回消息
* @param ctx
* @param msg
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// super.channelRead(ctx, msg);
ByteBuf buf = (ByteBuf) msg;
byte [] bytes = new byte[buf.readableBytes()];
buf.readBytes(bytes);
String time = new String(bytes, "UTF-8");
System.out.println("receive time from server, time = " + time);
}
/**
* 发生异常时关闭ctx
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//super.exceptionCaught(ctx, cause);
ctx.close();
}
}
解决半包读写问题:什么叫TCP的半包读写问题,就是TCP读取数据包时,不是一个报文一个报文的读取,有可能一次读取到2个或者以上的报文,这叫粘包,又或者将一个报文拆分成2部分读取,这叫拆包,以上这些都会导致半包读写问题;
服务端TimeServer的改造:
private class ChildChannelHandler extends ChannelInitializer{
@Override
protected void initChannel(SocketChannel serverSocket) throws Exception {
//新增解码器
serverSocket.pipeline().addLast(new LineBasedFrameDecoder(1024));
serverSocket.pipeline().addLast(new StringDecoder());
serverSocket.pipeline().addLast(new TimeServerHandler());
}
}
服务端TimeServerHandler的改造:
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//super.channelRead(ctx, msg);
//直接返回解码后的body
String body = (String) msg;
System.out.println("the time server reveive order : " + body +"; the counter is :" + ++counter);
String currentTime = "query time order".equals(body)? new Date(System.currentTimeMillis()).toString(): "bad order";
currentTime = currentTime + System.getProperty("line.separator");
//复制bytes数组的内容,构造一个缓冲区对象ByteBuf
ByteBuf rsp = Unpooled.copiedBuffer(currentTime.getBytes());
//异步发送消息给客户端
ctx.write(rsp);
}
客户端TimeClient的改造:
b.group(group)
.channel(NioSocketChannel.class)//设置Channel
.option(ChannelOption.TCP_NODELAY, true)//设置TCP的参数
.handler(new ChannelInitializer() {//匿名内部类设置handler
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024));
socketChannel.pipeline().addLast(new StringDecoder());
socketChannel.pipeline().addLast(new TimeClientHandler());
}
});
客户端TimeClientHandler的改造:
/**
* 初始化ByteBuf,初始化发送给服务端的消息
*/
public TimeClientHandler(){
req = ("query time order" + System.getProperty("line.separator")).getBytes();
System.out.println("line.separator = " + System.getProperty("line.separator"));
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//super.channelActive(ctx);
ByteBuf buf = null;
for(int i=0; i< 100; i++){
buf = Unpooled.buffer(req.length);
buf.writeBytes(req);
ctx.writeAndFlush(buf);
}
}
/**
* 读取客户端的返回消息
* @param ctx
* @param msg
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// super.channelRead(ctx, msg);
String body = (String) msg;
System.out.println("Now is :" + body +"; the counter is :" + ++counter );
}
以DelimiterBasedFrameDecoder特定分割符做结束标志的消息的解码:
EchoServer:
package delimiter;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import nettybanbao.TimeServerHandler;
/**
* 时间服务器,当有客户端请求时,返回服务端当前时间
*/
public class EchoServer {
public void bind(int port){
//NioEventLoopGroup是一个线程组,它包含了一组NIO线程,
// 这里的两个线程组一个是用于服务端接受客户端的连接,
// 另一个用于SocketChannel的网络读写
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//netty用于启动NIO服务端的辅助启动类
ServerBootstrap b = new ServerBootstrap();
//设置线程组
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)//设置channel
.option(ChannelOption.SO_BACKLOG, 100)//设置channel的TCP参数
.childHandler(new ChildChannelHandler());//绑定IO事件处理类
//绑定监听端口,调用同步阻塞方法等待绑定完成
ChannelFuture f = b.bind(port).sync();
//阻塞,等待服务端链路关闭后main函数才退出
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//优雅退出,释放跟shutdownGracefully相关联的所有资源
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
private class ChildChannelHandler extends ChannelInitializer{
@Override
protected void initChannel(SocketChannel serverSocket) throws Exception {
//创建分隔符缓冲对象ByteBuf,以“$_”作为分隔符
ByteBuf delimter = Unpooled.copiedBuffer("$_".getBytes());
//DelimiterBasedFrameDecoder有2个参数,
// 第一个参数是单条消息的最大长度,当达到该长度后还没有查找到分隔符,则抛出异常
//第二个参数是分隔符缓冲对象
serverSocket.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimter));
serverSocket.pipeline().addLast(new StringDecoder());
serverSocket.pipeline().addLast(new EchoServerHandler());
}
}
public static void main(String[] args) {
int port = 8080;
new EchoServer().bind(port);
}
}
EchoServerHandler:
package delimiter;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import java.util.Date;
/**
* 对网络事件进行读写操作
*/
public class EchoServerHandler extends ChannelHandlerAdapter {
private int counter = 0;
/**
* 当异常发生时
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//super.exceptionCaught(ctx, cause);
ctx.close();
}
/**
* 读取缓冲区里面的数据,处理并返回
* @param ctx
* @param msg
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//super.channelRead(ctx, msg);
String body = (String) msg;
System.out.println("this is : " + ++counter +"times receive client : [" + body + "]");
body +="$_";
ByteBuf echo = Unpooled.copiedBuffer(body.getBytes());
ctx.writeAndFlush(echo);
}
}
EchoClient:
package delimiter;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
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 io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import nettybanbao.TimeClientHandler;
/**
* netty的客户端
*/
public class EchoClient {
public void connect(String host, int port){
//NioEventLoopGroup是一个线程组,它包含了一组NIO线程
EventLoopGroup group = new NioEventLoopGroup();
try {
//客户端辅助启动类
Bootstrap b = new Bootstrap();
//设置线程组
b.group(group)
.channel(NioSocketChannel.class)//设置Channel
.option(ChannelOption.TCP_NODELAY, true)//设置TCP的参数
.handler(new ChannelInitializer() {//匿名内部类设置handler
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ByteBuf delimter = Unpooled.copiedBuffer("$_".getBytes());
//DelimiterBasedFrameDecoder有2个参数,
// 第一个参数是单条消息的最大长度,当达到该长度后还没有查找到分隔符,则抛出异常
//第二个参数是分隔符缓冲对象
socketChannel.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimter));
socketChannel.pipeline().addLast(new StringDecoder());
socketChannel.pipeline().addLast(new EchoClientHandler());
}
});
//异步连接客户端,同步阻塞直到连接成功
ChannelFuture f = b.connect(host, port).sync();
//阻塞,等待客户端链路关闭后main函数才退出
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) {
String host = "127.0.0.1";
int port = 8080;
new EchoClient().connect(host, port);
}
}
EchoClientHandler:
package delimiter;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
public class EchoClientHandler extends ChannelHandlerAdapter {
private int counter;
static final String ECHO_REQ = "Hi, chengkui. welcome to netty.$_";
/**
* 初始化ByteBuf,初始化发送给服务端的消息
*/
public EchoClientHandler(){
}
/**
* 连接服务端成功后开始发送消息
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//super.channelActive(ctx);
for(int i=0; i< 10; i++){
ctx.writeAndFlush(Unpooled.copiedBuffer(ECHO_REQ.getBytes()));
}
}
/**
* 读取客户端的返回消息
* @param ctx
* @param msg
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("this is " + ++counter +"times receive server: [" + msg +"]");
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
/**
* 发生异常时关闭ctx
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//super.exceptionCaught(ctx, cause);
ctx.close();
}
}
FixedLengthFrameDecoder是一个固定长度的解码器,他按照固定的长度对消息自动解码,如果是半包消息,他会缓存半包消息并等待下个包到达后进行拼包,知道读取到一个完整的包;
EchoServer:
package fixlength;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.FixedLengthFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
/**
* 时间服务器,当有客户端请求时,返回服务端当前时间
*/
public class EchoServer {
public void bind(int port){
//NioEventLoopGroup是一个线程组,它包含了一组NIO线程,
// 这里的两个线程组一个是用于服务端接受客户端的连接,
// 另一个用于SocketChannel的网络读写
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//netty用于启动NIO服务端的辅助启动类
ServerBootstrap b = new ServerBootstrap();
//设置线程组
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)//设置channel
.option(ChannelOption.SO_BACKLOG, 100)//设置channel的TCP参数
.childHandler(new ChildChannelHandler());//绑定IO事件处理类
//绑定监听端口,调用同步阻塞方法等待绑定完成
ChannelFuture f = b.bind(port).sync();
//阻塞,等待服务端链路关闭后main函数才退出
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//优雅退出,释放跟shutdownGracefully相关联的所有资源
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
private class ChildChannelHandler extends ChannelInitializer{
@Override
protected void initChannel(SocketChannel serverSocket) throws Exception {
//FixedLengthFrameDecoder是一个固定长度的解码器,这里的长度设置为20
serverSocket.pipeline().addLast(new FixedLengthFrameDecoder(20));
serverSocket.pipeline().addLast(new StringDecoder());
serverSocket.pipeline().addLast(new EchoServerHandler());
}
}
public static void main(String[] args) {
int port = 8080;
new EchoServer().bind(port);
}
}
EchoServerHandler:
package fixlength;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
/**
* 对网络事件进行读写操作
*/
public class EchoServerHandler extends ChannelHandlerAdapter {
/**
* 当异常发生时
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//super.exceptionCaught(ctx, cause);
ctx.close();
}
/**
* 读取缓冲区里面的数据,处理并返回
* @param ctx
* @param msg
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("Receive client:" + msg);
}
}
EchoClient:
package fixlength;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
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 io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.FixedLengthFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
/**
* netty的客户端
*/
public class EchoClient {
public void connect(String host, int port){
//NioEventLoopGroup是一个线程组,它包含了一组NIO线程
EventLoopGroup group = new NioEventLoopGroup();
try {
//客户端辅助启动类
Bootstrap b = new Bootstrap();
//设置线程组
b.group(group)
.channel(NioSocketChannel.class)//设置Channel
.option(ChannelOption.TCP_NODELAY, true)//设置TCP的参数
.handler(new ChannelInitializer() {//匿名内部类设置handler
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new FixedLengthFrameDecoder(20));
socketChannel.pipeline().addLast(new StringDecoder());
socketChannel.pipeline().addLast(new EchoClientHandler());
}
});
//异步连接客户端,同步阻塞直到连接成功
ChannelFuture f = b.connect(host, port).sync();
//阻塞,等待客户端链路关闭后main函数才退出
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) {
String host = "127.0.0.1";
int port = 8080;
new EchoClient().connect(host, port);
}
}
EchoClientHandler:
package fixlength;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
public class EchoClientHandler extends ChannelHandlerAdapter {
static final String ECHO_REQ = "Hello, chengkui weicome to netty";
/**
* 初始化ByteBuf,初始化发送给服务端的消息
*/
public EchoClientHandler(){
}
/**
* 连接服务端成功后开始发送消息
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.copiedBuffer(ECHO_REQ.getBytes()));
}
/**
* 读取客户端的返回消息
* @param ctx
* @param msg
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("receive server: [" + msg +"]");
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
/**
* 发生异常时关闭ctx
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//super.exceptionCaught(ctx, cause);
ctx.close();
}
}