Netty服务端代码
项目结构
pom坐标
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.16.Final</version>
</dependency>
Server.java 函数启动入口
/**
* 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;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.stream.ChunkedWriteHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 服务入口
*/
public class Server {
private final static Logger log = LoggerFactory.getLogger(Server.class);
public static void main(String[] args) {
EventLoopGroup parentGroup = null;
EventLoopGroup childGroup = null;
try {
parentGroup = new NioEventLoopGroup(4);
childGroup = new NioEventLoopGroup(2);
ServerBootstrap server = new ServerBootstrap(); // 1. 绑定两个线程组分别用来处理客户端通道的accept和读写时间
server.group(parentGroup, childGroup); // 2. 绑定服务端通道NioServerSocketChannel
server.channel(NioServerSocketChannel.class); // 3. 给读写事件的线程通道绑定handler去真正处理读写
server.childHandler(new MyHandler()); // 4. Handler业务处理类
server.childOption(ChannelOption.SO_LINGER, null); // 5. 设置确定连接,关闭客户端直接释放
server.option(ChannelOption.SO_BACKLOG, 1024); // 6. 初始化服务端可连接队列
server.childOption(ChannelOption.SO_KEEPALIVE, false); // 7. 检测连接状态
ChannelFuture future = server.bind(80).sync(); // 6. 监听端口(服务器host和port端口),同步返回
log.info("-------------------- netty startup is success --------------------");
future.channel().closeFuture().sync(); // 7. 当通道关闭时继续向后执行,这是一个阻塞方法
} catch (Exception e) {
log.error("server error", e);
e.printStackTrace();
} finally {
childGroup.shutdownGracefully();
parentGroup.shutdownGracefully();
log.info("释放资源完成");
}
}
public static class MyHandler extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) {
socketChannel.pipeline().addLast(new HttpRequestDecoder()); //请求解码器
socketChannel.pipeline().addLast(new HttpObjectAggregator(65535)); //将HTTP消息的多个部分合成一条完整的HTTP消息
socketChannel.pipeline().addLast(new HttpResponseEncoder()); //响应转码器
socketChannel.pipeline().addLast(new ChunkedWriteHandler());
socketChannel.pipeline().addLast(new ParamCheck()); //自定义处理格式化请求数据
socketChannel.pipeline().addLast(new HttpHandler()); //自定义处理handler,处理业务逻辑
}
}
}
ParamCheck.java 请求参数拼接
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.handler.codec.http.multipart.Attribute;
import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
import io.netty.handler.codec.http.multipart.InterfaceHttpData;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 参数拼接
*/
public class ParamCheck extends MessageToMessageDecoder<FullHttpRequest> {
@Override
protected void decode(ChannelHandlerContext ctx, FullHttpRequest request, List<Object> out) throws Exception {
if (!request.decoderResult().isSuccess() || "/favicon.ico".equals(request.uri())) {//失败解析排除,图标请求排除
ctx.channel().close();//直接关闭后续操作
return;
}
Map<String, Object> params = new HashMap<>();//请求参数缓存
out.add(params);//请求参数写入管道
params.put("head", new HashMap<>());//保存请求头信息
params.put("body", new HashMap<>());//保存请求体信息
params.put("uri", request.uri());//记录请求uri
request.headers().entries().stream().forEach(f -> ((HashMap) params.get("head")).put(f.getKey(), f.getValue()));//记录请求头信息
String method = request.method().name();
if ("POST".equals(method)) {//获取post请求的参数
HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(new DefaultHttpDataFactory(false), request);
List<InterfaceHttpData> paramList = decoder.getBodyHttpDatas();
for (InterfaceHttpData param : paramList) {
if (param.getHttpDataType() == InterfaceHttpData.HttpDataType.Attribute) {
Attribute data = (Attribute) param;
((HashMap) params.get("body")).put(data.getName(), data.getValue());
}
}
return;
}
if ("GET".equals(method)) {//获取get请求的参数
QueryStringDecoder decoder = new QueryStringDecoder(request.uri());
decoder.parameters().entrySet().stream().forEach(f->((HashMap) params.get("body")).put(f.getKey(), f.getValue().get(0)));
return;
}
}
}
HttpHandler.java 业务逻辑处理类
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.*;
import io.netty.util.ReferenceCountUtil;
import java.io.ByteArrayInputStream;
import java.io.UnsupportedEncodingException;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
/**
* 业务处理类
*/
public class HttpHandler extends ChannelInboundHandlerAdapter {
/**
* 业务处理逻辑
*
* @param ctx
* @param msg
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
indirectResp(ctx, msg);//分批次响应,推荐海量数据使用这一种方式
//directResp(ctx, msg);//直接响应
}
/**
* 直接响应数据
*
* @param ctx
* @param msg
* @throws UnsupportedEncodingException
*/
public static void directResp(ChannelHandlerContext ctx, Object msg) throws UnsupportedEncodingException {
byte[] content = String.valueOf(msg).getBytes("utf-8");
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(content));
response.headers().set("Content-Type", "text/plain; charset=UTF-8");
response.headers().set("content-length", content.length);
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);//短连接,长连接不使用CLOSE,使用另一个参数
}
/**
* 间接响应数据,海量数据响应需要分批次响应
* 动态加载
* @param ctx
* @param msg
* @throws UnsupportedEncodingException
*/
public static void indirectResp(ChannelHandlerContext ctx, Object msg) throws Exception {
File file = new File("/home/data/bigData.log");//大文件
FileReader fr = new FileReader(file);
BufferedReader bf = new BufferedReader(fr);
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.OK);
response.headers().set("content-length", file.length());//设置字符串长度
response.headers().set("content-type", "text/json;charset=utf-8");
ctx.write(response);
String str;
// 按行读取字符串
long i = 0;//记录加载的行数
while ((str = bf.readLine()) != null) {
if (!ctx.channel().isActive()) {
System.out.println("远程客户端连接被迫关闭");
break;
}
ctx.write(Unpooled.copiedBuffer(str.getBytes()));//写入数据
i++;
Thread.sleep(10);
if (i % 10 == 0) ctx.flush();多少行进行一次数据刷新加载
}
bf.close();fr.close();//注意安全关闭资源
ChannelFuture lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
lastContentFuture.addListener(ChannelFutureListener.CLOSE);
ReferenceCountUtil.release(msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { super.exceptionCaught(ctx, cause);ctx.close(); }
@Override
public void channelReadComplete(ChannelHandlerContext ctx) { ctx.close(); }
}
启动项目后测试界面