1、HttpServer 类 创建http 服务端
package com.bokeyuan.http.server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
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.codec.http.HttpServerCodec;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;
import java.net.InetSocketAddress;
/**
* netty server
* 2021/9/9
*/
@Slf4j
public class HttpServer {
int port ;
/**
* 是否有配置SSL
*/
private static final boolean SSL = System.getProperty("ssl") != null;
public HttpServer(int port){
this.port = port;
}
public void start() throws Exception{
ServerBootstrap bootstrap = new ServerBootstrap();
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup work = new NioEventLoopGroup();
bootstrap.group(boss,work)
.localAddress(new InetSocketAddress(port))//暴露本地的端口供请求
.option(ChannelOption.SO_BACKLOG, 1024) //存放已完成三次握手的请求的等待队列的最大长度,一定要设置option非childOption
.childOption(ChannelOption.SO_REUSEADDR, true)//复用地址,快速复用端口
.childOption(ChannelOption.TCP_NODELAY, true) //数据马上发送不需要延迟,一定要设置childOption非option
.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT) //使用池化的字节分配器
.handler(new LoggingHandler(LogLevel.INFO))
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer< SocketChannel >(){
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
if (SSL) { //配置Https通信
SelfSignedCertificate ssc = new SelfSignedCertificate();
SslContext sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
socketChannel.pipeline().addLast(sslCtx.newHandler(socketChannel.alloc()));
}
//通道设置,顺序别乱写
socketChannel.pipeline().addFirst(new IdleStateHandler(5, 5, 10));//心跳模式配置,当一个通道多长时间未进行读写时断开,读超时时长为5s
// socketChannel.pipeline().addLast(new HttpRequestDecoder());//in,因为基于Http协议,使用http的解码器
// socketChannel.pipeline().addLast(new HttpResponseEncoder()); //out,因为基于Http协议,使用http的编码器
socketChannel.pipeline().addLast(new HttpServerCodec());// http 编解码
socketChannel.pipeline().addLast("httpAggregator",new HttpObjectAggregator(512*1024)); // http消息聚合器,设置512*1024为接收的最大contentlength
socketChannel.pipeline().addLast(new ChunkedWriteHandler());//in+out,Netty提供了ChunkedWriteHandler来解决大文件或者码流传输过程中可能发生的内存溢出问题
//socketChannel.pipeline().addLast(new HttpRequestHandler());
socketChannel.pipeline().addLast(new HttpReqHandler());
}
});
ChannelFuture f = bootstrap.bind().sync();
//ChannelFuture f = bootstrap.bind(new InetSocketAddress(port)).sync();
System.out.println(" server start up on port : " + port);
f.channel().closeFuture().sync();
}
public static void main(String[] args) {
HttpServer httpServer = new HttpServer(8899);
try {
httpServer.start();
System.out.println("启动netty-http服务器成功.....");
} catch (Exception e) {
e.printStackTrace();
}
}
}
2、HttpReqHandler类 处理http请求
package com.bokeyuan.http.server;
import io.netty.buffer.ByteBuf;
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.CharsetUtil;
/**
* @author: void
* @date: 2021-09-09 16:41
* @description: http请求处理器
* @version: 1.0
*/
public class HttpReqHandler extends ChannelInboundHandlerAdapter {
//是否需要保持长连接
private boolean isKeepAlive = true;
//是否允许监听式刷出,为false时只刷出,为true时监听到刷出后再进行业务处理
private boolean enableListenerFlush =true;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String respMsg = "";
if(msg instanceof FullHttpRequest) {
FullHttpRequest fullHttpRequest = (FullHttpRequest) msg;//转换成一个Http请求对象
String uri = fullHttpRequest.uri();
String method = fullHttpRequest.method().name();
String bodyParam = getBodyParam(fullHttpRequest);
String contentType = fullHttpRequest.headers().get(HttpHeaderNames.CONTENT_TYPE);
System.out.println("请求uri:"+uri+",请求方式:"+method+",请求参数:"+bodyParam+",contentType:"+contentType);
//业务处理产生响应内容
respMsg = "响应内容";
}else{
respMsg = "bad request";
}
//正常数据刷出
FullHttpResponse response = buildResponse(respMsg);
//服务端设定长连接,防止客户端断开连接产生异常
response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
if (enableListenerFlush) {
ctx.channel().writeAndFlush(response)
.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
//数据刷出后的处理
if (future.isSuccess()) { //刷出成功
System.out.println("数据刷出成功");
}
if (!isKeepAlive) {//如果是短连接,刷出数据之后不管成功与否,server端都应该主动关闭这个通道
future.addListener(ChannelFutureListener.CLOSE);//监听链,刷出之后再监听
}
}
});
} else {
if (isKeepAlive) {
ctx.channel().writeAndFlush(response);
} else {
ctx.channel().writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
}
}
private String getBodyParam(FullHttpRequest fullHttpRequest) {
ByteBuf byteBuf = fullHttpRequest.content();
if (byteBuf != null) {
String body = byteBuf.toString(CharsetUtil.UTF_8);
return body;
}
return null;
}
private FullHttpResponse buildResponse(String msg) {
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
Unpooled.wrappedBuffer(msg.getBytes()));
response.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON + ";" + HttpHeaderValues.CHARSET + "=UTF-8");
response.headers().set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
response.headers().set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_HEADERS, "Origin, X-Requested-With, Content-Type, Accept,Accept-Charset");
response.headers().set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_METHODS, "GET,POST,PUT,DELETE");
response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
return response;
}
}