1、关于Netty编程常用类的介绍
BootStrap和ServerBootstrap类似,不过他是对非服务端的channel而言,比如客户端。
如果你只指定了一个EventLoopGroup那他就会即作为一个‘boss’线程,也会作为一个‘workder’线程,尽管客户端不需要使用到‘boss’线程。
代替NioServerSocketChannel的是NioSocketChannel,这个类在客户端channel被创建时使用。
不像在使用ServerBootstrap时需要用childOption()方法,因为客户端的SocketChannel没有父channel的概念。在客户端用connect()方法代替了bind()方法。
2、如果在获取消息后,只是进行读数据,没有回写数据。则需要手动的释放msg
public class ClientHandler extends ChannelHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
try {
ByteBuf buf = (ByteBuf) msg;
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
String body = new String(req, "utf-8");
System.out.println("Client :" + body);
// 只是读数据,没有写数据的话
// 需要自己手动的释放的消息
} finally {
ReferenceCountUtil.release(msg);
}
}
}
3、关于服务端长连接和短连接的实现
当服务端完成一次数据响应后,断开与客户端的连接,就是短连接。在Netty中可以设置,服务端在响应数据完成后,关闭与客户端的连接。也就是短连接的实现。
public class ServerHandler extends ChannelHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
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("Server: from client " + body);
// 服务端,回写数据给客户端
String response = "Hi Client,I am Server ... ";
ByteBuf resp = Unpooled.copiedBuffer(response.getBytes());
ctx.writeAndFlush(resp);
// 当服务端完成写操作后,关闭与客户端的连接
// ctx.writeAndFlush(Unpooled.copiedBuffer(response.getBytes()))
// .addListener(ChannelFutureListener.CLOSE);
// 上面就是长连接和短连接的实现
// 关闭应该是通过Server端来完成,因为在客户端写数据完成后,关闭连接的话。就接受不到服务端的数据了
// 如果只是读数据,就要释放msg
// 如果用了,write就不需要手动的释放msg了
}
}
4、服务端的实现
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 Server {
public void bind(int port) throws Exception {
// 服务器线程组 用于网络事件的处理 一个用于服务器接收客户端的连接
// 另一个线程组用于处理SocketChannel的网络读写
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// NIO服务器端的辅助启动类 降低服务器开发难度
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)// 类似NIO中serverSocketChannel
.option(ChannelOption.SO_BACKLOG, 1024)// 配置TCP参数
.option(ChannelOption.SO_BACKLOG, 1024) // 设置tcp缓冲区
.option(ChannelOption.SO_SNDBUF, 32 * 1024) // 设置发送缓冲大小
.option(ChannelOption.SO_RCVBUF, 32 * 1024) // 这是接收缓冲大小
.option(ChannelOption.SO_KEEPALIVE, true) // 保持连接
.childHandler(new ChildChannelHandler());// 最后绑定I/O事件的处理类
// 处理网络IO事件
// 服务器启动后 绑定监听端口 同步等待成功 主要用于异步操作的通知回调 回调处理用的ChildChannelHandler
ChannelFuture f = serverBootstrap.bind(port).sync();
System.out.println("Server启动");
// 等待服务端监听端口关闭
f.channel().closeFuture().sync();
} finally {
// 优雅退出 释放线程池资源
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
System.out.println("服务器优雅的释放了线程资源...");
}
}
/**
* 网络事件处理器
*/
private class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ServerHandler());
}
}
public static void main(String[] args) throws Exception {
int port = 9998;
new Server().bind(port);
}
}
5、服务端Handler的实现
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
public class ServerHandler extends ChannelHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
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("Server: from client " + body);
// 服务端,回写数据给客户端
String response = "Hi Client,I am Server ... ";
ByteBuf resp = Unpooled.copiedBuffer(response.getBytes());
ctx.writeAndFlush(resp);
// 当服务端完成写操作后,关闭与客户端的连接
// ctx.writeAndFlush(Unpooled.copiedBuffer(response.getBytes()))
// .addListener(ChannelFutureListener.CLOSE);
// 上面就是长连接和短连接的实现
// 关闭应该是通过Server端来完成,因为在客户端写数据完成后,关闭连接的话。就接受不到服务端的数据了
// 如果只是读数据,就要释放msg
// 如果用了,write就不需要手动的释放msg了
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
ctx.close();
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
}
6、客户端的实现
import io.netty.bootstrap.Bootstrap;
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;
public class Client {
/**
* 连接服务器
*
* @param port
* @param host
* @throws Exception
*/
public void connect(int port, String host) throws Exception {
// 配置客户端NIO线程组
EventLoopGroup group = new NioEventLoopGroup();
try {
// 客户端辅助启动类 对客户端配置
Bootstrap b = new Bootstrap();
b.group(group)//
.channel(NioSocketChannel.class)//
.option(ChannelOption.TCP_NODELAY, true)//
.handler(new ClientChannelHandler());//
// 异步链接服务器 同步等待链接成功
ChannelFuture f = b.connect(host, port).sync();
// 发送消息
Thread.sleep(1000);
f.channel().writeAndFlush(Unpooled.copiedBuffer("777".getBytes()));
f.channel().writeAndFlush(Unpooled.copiedBuffer("666".getBytes()));
Thread.sleep(2000);
f.channel().writeAndFlush(Unpooled.copiedBuffer("888".getBytes()));
// 等待链接关闭
f.channel().closeFuture().sync();
} finally {
group.shutdownGracefully();
System.out.println("客户端优雅的释放了线程资源...");
}
}
/**
* 网络事件处理器
*/
private class ClientChannelHandler extends
ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ClientHandler());
}
}
public static void main(String[] args) throws Exception {
new Client().connect(9998, "127.0.0.1");
}
}
7、客户端Handler的实现
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.ReferenceCountUtil;
public class ClientHandler extends ChannelHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
try {
ByteBuf buf = (ByteBuf) msg;
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
String body = new String(req, "utf-8");
System.out.println("Client :" + body);
// 只是读数据,没有写数据的话
// 需要自己手动的释放的消息
} finally {
ReferenceCountUtil.release(msg);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
ctx.close();
}
}
8、
源码下载