本文使用netty自定义了一个http协议的服务器。
1.创建一个maven项目,在pom.xml文件中导入依赖并刷新
<!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.16.Final</version>
</dependency>
2.搭建一个Netty服务器,我们只需要两个类——一个是启动类,负责启动(BootStrap)和main方法;一个是ChannelHandler,负责具体的业务逻辑。
启动类:
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
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 java.util.Date;
/**
* Created by RoyDeng on 20/03/22.
*/
public class HttpServer {
private final static int port = 8080;
public static void main(String[] args) throws Exception {
System.out.println(new Date().toLocaleString()+"netty服务器启动了");
start();
}
public static void start() throws Exception {
//ServerBootstrap作为一个启动辅助类,通过他可以很方便的创建一个Netty服务端
ServerBootstrap b = new ServerBootstrap();
//创建线程池
NioEventLoopGroup group = new NioEventLoopGroup();
b.group(group)
//指定使用NioServerSocketChannel来处理连接请求,放射生成一个channel连接
.channel(NioServerSocketChannel.class)
//配置handler和childHandler,数据处理器
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch)
throws Exception {
System.out.println("initChannel ch:" + ch);
ch.pipeline()
// HttpRequestDecoder,用于解码request
.addLast("decoder", new HttpRequestDecoder())
// HttpResponseEncoder,用于编码response
.addLast("encoder", new HttpResponseEncoder())
//aggregator消息聚合器,参数含义是消息合并的数据大小,如此代表聚合的消息内容长度不超过512kb
.addLast("aggregator", new HttpObjectAggregator(512 * 1024))
//添加我们自己的处理接口
.addLast("handler", new HttpHandler()); // 4
}
})
.option(ChannelOption.SO_BACKLOG, 128) // 配置TCP参数
.childOption(ChannelOption.SO_KEEPALIVE, Boolean.TRUE);//
//启动服务器
b.bind(port).sync();
}
}
handler线程处理类
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.AsciiString;
import java.util.Date;
/**
* Handler需要声明泛型为<FullHttpRequest>,声明之后,只有msg为FullHttpRequest的消息才能进来
*/
public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
private AsciiString contentType = HttpHeaderValues.TEXT_PLAIN;
@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
System.out.println(new Date().toLocaleString()+"class:" + msg.getClass().getName());
/*
生成response,使用的FullHttpResponse,同FullHttpRequest类似,
通过这个我们就不用将response拆分成多个channel返回给请求端了
*/
DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
// 响应内容:test --> 使用平台的默认字符集将字符串编码为 byte 序列,存为新的 byte 数组中
Unpooled.wrappedBuffer("test".getBytes()));
HttpHeaders heads = response.headers();
//定义响应头的content_type、content_length、connection:keep_alive
heads.add(HttpHeaderNames.CONTENT_TYPE, contentType + "; charset=UTF-8");
heads.add(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()); // 3
heads.add(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
ctx.write(response);
}
//读取完成,输出缓冲流
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println(new Date().toLocaleString()+"channelReadComplete");
super.channelReadComplete(ctx);
//清空连接数据
ctx.flush();
}
//异常捕获
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("exceptionCaught");
if(null != cause) cause.printStackTrace();
if(null != ctx) ctx.close();
}
}
运行,浏览器输入:http://localhost:8080