package com.atguigu.netty.NettyHttp;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
/**
* @Description: HTTP服务
* @author: Freedom
* @QQ: 1556507698
* @date:2022/6/24 10:41
*/
public class HTTPServer {
public static void main(String[] args) throws Exception{
//1. 创建两个线程组 bossGroup 和 workerGroup
//2. bossGroup 只是处理连接请求 , 真正的和客户端业务处理,会交给 workerGroup完成
//3. 两个都是无限循环
//4. bossGroup 和 workerGroup 含有的子线程(NioEventLoop)的个数
// 默认实际 cpu核数 * 2
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup(); //8
try {
//2.创建服务器端的启动对象,配置参数
ServerBootstrap serverBootStrap = new ServerBootstrap();
//3.设置线程组
serverBootStrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new HTTPServerInitializer());
System.out.println("HTTP 服务器启动成功");
//4.绑定端口号
ChannelFuture channelFuture = serverBootStrap.bind(1192).sync();
//5.监听事件关闭
channelFuture.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
创建一个通道初始化对象 并给WorkGroup的 EventLoop 对应的管道设置处理器 可以是Netty提供的 也可以是自定义的
package com.atguigu.netty.NettyHttp;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;
/**
* @Description: ···
* @author: Freedom
* @QQ: 1556507698
* @date:2022/6/24 10:43
*/
public class HTTPServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//向管道加入处理器
//得到管道
ChannelPipeline pipeline = ch.pipeline();
//加入netty提供的HttpServerCodec ; code =》 coder - decoder
//netty 提供的处理http的编解码器
pipeline.addLast("my http server codec",new HttpServerCodec());
//增加自定义的处理器Handle
pipeline.addLast("自定义Handler",new HTTPServerHandle());
}
}
package com.atguigu.netty.NettyHttp;
import io.netty.buffer.ByteBuf;
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.CharsetUtil;
/**
* @Description: ···
* @author: Freedom
* @QQ: 1556507698
* @date:2022/6/24 10:41
*/
public class HTTPServerHandle extends SimpleChannelInboundHandler<HttpObject> {
/**
* SimpleChannelInboundHandler<I> extends ChannelInboundHandlerAdapter
* 当有读取事件时触发此方法
* HttpObject 表示客户端和服务器端相互通讯的数据被封装成HttpObject
* @param ctx
* @param msg
* @throws Exception
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
//判断msg是不是一个HttpRequest请求
if (msg instanceof HttpObject){
System.out.println("msg 类型="+ msg.getClass());
System.out.println("客户端地址="+ ctx.channel().remoteAddress());
//回复信息给浏览器 得做成Http协议的形式
//创建bytebuf
ByteBuf cotent = Unpooled.copiedBuffer(" ByteBuf cotent = Unpooled.copiedBuffer(\"我是服务器返回消息\", CharsetUtil.UTF_8);\n", CharsetUtil.UTF_8);
//构造一个http的响应 httpResponse 参数:1.协议版本 2.响应状态码3.内容
FullHttpResponse httpResponse = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
cotent);
//设置内容类型
httpResponse.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/plain");
//设置返回数据长度
httpResponse.headers().set(HttpHeaderNames.CONTENT_LENGTH,cotent.readableBytes());
//将构建好的httpResponse 返回
ctx.writeAndFlush(httpResponse);
}
}
}
服务启动后访问监听的浏览器端口即可得到返回消息
此时控制台打印
HTTP 服务器启动成功
客户端地址=/0:0:0:0:0:0:0:1:60504
客户端地址=/0:0:0:0:0:0:0:1:60504
客户端地址=/0:0:0:0:0:0:0:1:60505
客户端地址=/0:0:0:0:0:0:0:1:60505
客户端地址=/0:0:0:0:0:0:0:1:60509
客户端地址=/0:0:0:0:0:0:0:1:60509
客户端地址=/0:0:0:0:0:0:0:1:60510
客户端地址=/0:0:0:0:0:0:0:1:60510
会发现浏览器发送了两次请求
一次是请求网站,一次是请求网站的图标
进行请求过滤
//获取到消息
HttpRequest httpRequest = (HttpRequest) msg;
//获取uri 统一资源定位
URI uri = new URI(httpRequest.uri());
//如果uri的path路径包含请求图标
if ("/favicon.ico".equals(uri.getPath())){
System.out.println("请求了图标资源,不做相应");
return;
}