1. Netty编码实现
现在,我们开始编写一个最简单的Netty示例,在这之前我们先熟悉一下最基本的编码实现步骤!
- Netty实现通信的步骤:(客户端与服务器端基本一致)
- 创建两个的NIO线程组,一个专门用于网络事件处理(接受客户端的连接),另一个则进行网络通信读写。
- 创建一个ServerBootstrap对象,配置Netty的一系列参数,例如接受传出数据的缓存大小等等。
- 创建一个实际处理数据的类Channellnitializer,进行初始化的准备工作,比如设置接受传出数据的字符集、格式、已经实际处理数据的接口。
- 绑定端口,执行同步阻塞方法等待服务器端启动即可。
-
Netty的使用非常简单,仅仅引入依赖即可快速开始:
<dependency>
<groupld>io.netty</groupId
<artifactId>netty-all</artifactld
<version>4.1.12.Final</version>
</dependency>
2. Netty编码实现步骤
2.1 Netty Server:服务端
Netty Server端需要编写Server 与ServerHandler两个核心类!
- Server
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 static void main(String[] args) throws Exception{
//1、创建两个线程组:一个用于进行网络连接接受的另一个用于我们的实际处理(网络通信的读写)
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
//2、通过辅助类去构造server/client
ServerBootstrap b = new ServerBootstrap();
//3.进行NioServer的基础配置
//3.1绑定两个线程组
b.group(bossGroup, workGroup)
//3.2因为是server端,所以需要配置NioServerSocketChannel
.channel(NioServerSocketChannel.class)
//3.3设置链接超时时间
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
//3.4设置TCPbacklog参数=sync队列+accept队列
.option(ChannelOption.SO_BACKLOG, 1024)
//3.5设置配置项通信不延迟
.childOption(ChannelOption.TCP_NODELAY, true)
//3.6设置配置项接收与发送缓存区大小
.childOption(ChannelOption.SO_RCVBUF, 1024 * 32)
.childOption(ChannelOption.SO_SNDBUF, 1024 * 32)
//3.7进行初始化 ChannelInitializer,用于构建双向链表"pipeline”添加业务handler处理
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws Exception {
//3.8这里仅仅只是添加一个业务处理器:ServerHandler(后面我们要针对他进行编码)
//粘包/拆包方案一:定长(5个字节)
//sc.pipeline().addLast(new FixedLengthFrameDecoder(5));
//将buffer转换成字符串,channelRead中可以使用 (String)msg;
sc.pipeline().addLast(new StringDecoder());
sc.pipeline().addLast(new ServerHandler());
}
});
//4. 绑定连接 , 服务器端绑定端口并启动服务;使用channe1级别的监听close端口阻塞的方式
ChannelFuture cf = b.bind(8765).sync();
cf.channel().closeFuture().sync();
//5. 释放资源
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
- ServerHandler.java
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class ServerHandler extends ChannelInboundHandlerAdapter {
/**
* 通道激活方法
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println(" server channel active... ");
}
/**
* 读写数据核心方法
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String request = (String)msg;
System.out.println("Server :" + msg);
String response = request ;
ctx.writeAndFlush(Unpooled.copiedBuffer(response.getBytes()));
}
/**
* 读通道数据完成
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
}
/**
* 捕获异常方法
* @param ctx
* @param t
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable t) throws Exception {
}
}
2.2 Netty Client:客户端
Netty Client端需要编写Clent与ClientHandler两个核心类!
- Clent
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 {
public static void main(String[] args) throws Exception {
//1、创建两个线程组:只需要一个线程组用于我们的实际处理(网络通信的读写)
EventLoopGroup group = new NioEventLoopGroup();
//2.通过辅助类去构造client,然后进行配置响应的配置参数
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
//2.1设置链接超时时间
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
//2.2设置配置项接收与发送缓存区大小
.option(ChannelOption.SO_RCVBUF, 1024 * 32)
.option(ChannelOption.SO_SNDBUF, 1024 * 32)
//3. 初始化 ChannelInitializer
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws Exception {
//粘包/拆包方案一:定长(5个字节)
//sc.pipeline().addLast(new FixedLengthFrameDecoder(5));
//将buffer转换成字符串,channelRead中可以使用 (String)msg;
sc.pipeline().addLast(new StringDecoder());
//3.1 添加客户端业务处理类ClientHandler
sc.pipeline().addLast(new ClientHandler());
}
});
//4.服务器端绑定端口并启动服务;使用channel级别的监听close端口阻塞的方式
ChannelFuture cf = b.connect("127.0.0.1", 8765).sync();
//5.发送一条数据到服务器端
cf.channel().writeAndFlush(Unpooled.wrappedBuffer("hello netty!".getBytes()));
Thread.sleep(2000);
//6.休眠一秒钟后再发送一条数据到服务端
cf.channel().writeAndFlush(Unpooled.copiedBuffer("hello netty again!".getBytes()));
//7.同步阻塞关闭监听并释放资源
cf.channel().closeFuture().sync();
group.shutdownGracefully();
}
}
- ClientHandler
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class ClientHandler extends ChannelInboundHandlerAdapter{
/**
* 通道激活方法
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("client channel active... ");
}
/**
* 读数据核心方法
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String response = (String)msg;
System.out.println("Client: " + response);
}
/**
* 读通道数据完成
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
}
/**
* 捕获异常方法
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
}
}