【Netty】实战:基于Http的Web服务器

目录

一、实现ChannelHandler 

二、实现ChannelInitializer

三、实现服务器启动程序

四、测试


本文来实现一个简单的Web服务器,当用户在浏览器访问Web服务器时,可以返回响应的内容给用户。很简单,就三步。

一、实现ChannelHandler 

package cn.md.netty.httpserver;

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;

/**
 * * @Author: Martin
 * * @Date    2024/9/1 17:47
 * * @Description
 **/
public class HttpServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

    /**
     * Is called for each message of type {@link I}.
     *
     * @param ctx the {@link ChannelHandlerContext} which this {@link SimpleChannelInboundHandler}
     *            belongs to
     * @param msg the message to handle
     * @throws Exception is thrown if an error occurred
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
        // 打印Http请求
        printHttpRequest(msg);

        String uri = msg.uri();
        String resp;
        switch (uri) {
            case "/":
                resp = "hello world";
                break;
            case "/test":
                resp = "test";
                break;
            case "/hi":
                resp = "hello";
                break;
            default:
                resp = "404";
        }
        // 返回http格式响应
        returnHttpResp(ctx, resp);
    }

    private void returnHttpResp(ChannelHandlerContext ctx, String msg) {
        FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
                Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8));

        response.headers().set(HttpHeaderNames.CONTENT_LENGTH,msg.length());

        ctx.writeAndFlush(response)
                .addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
    }

    private void printHttpRequest(FullHttpRequest msg) {
        String uri = msg.uri();
        HttpMethod method = msg.method();
        HttpVersion httpVersion = msg.protocolVersion();
        // 打印请求行
        System.out.println("uri:" + uri + " method:" + method + " httpVersion:" + httpVersion);

        HttpHeaders headers = msg.headers();
        for (String name : headers.names()) {
            System.out.println(name + ":" + headers.get(name));
        }

        System.out.println("");
        System.out.println(msg.content().toString(CharsetUtil.UTF_8));
    }
}

二、实现ChannelInitializer

package cn.md.netty.httpserver;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;

/**
 * * @Author: Martin
 * * @Date    2024/9/1 17:55
 * * @Description
 **/
public class HttpServerChannelInitializer extends ChannelInitializer<SocketChannel> {

    /**
     * This method will be called once the {@link Channel} was registered. After the method returns this instance
     * will be removed from the {@link ChannelPipeline} of the {@link Channel}.
     *
     * @param ch the {@link Channel} which was registered.
     * @throws Exception is thrown if an error occurs. In that case it will be handled by
     *                   {@link #exceptionCaught(ChannelHandlerContext, Throwable)} which will by default close
     *                   the {@link Channel}.
     */
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        // 添加自定义的handler
        ch.pipeline().addLast("codec",new HttpServerCodec()) // 添加编解码器
                // 添加聚合器,聚合为一个完整的 FullHttpMessage
                .addLast("aggregator",new HttpObjectAggregator(1024*1024*10))
                .addLast("handler",new HttpServerHandler());
    }
}

三、实现服务器启动程序

package cn.md.netty.httpserver;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * * @Author: Martin
 * * @Date    2024/9/1 18:01
 * * @Description
 **/
public class HttpServer {

    public static void main(String[] args) {

        NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();

        ServerBootstrap serverBootstrap = new ServerBootstrap();

        try {
            ChannelFuture channelFuture = serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new HttpServerChannelInitializer())
                    //服务器在处理客户端连接请求时的等待队列长度。
                    //当服务器接收到客户端的连接请求时,如果服务器正在处理其他连接或者处于忙碌状态,新的连接请求将被放入等待队列中。
                    .option(ChannelOption.SO_BACKLOG, 128)
                    //底层套接字级别设置的选项,由操作系统的 TCP/IP 协议栈实现保活机制。
                    //当开启后,在一定时间没有数据传输时,操作系统自动发送保活探测报文来检测连接是否仍然有效。
                    .option(ChannelOption.SO_KEEPALIVE, true)
                    .bind(8888)
                    .sync();


            channelFuture.channel().closeFuture().sync();

        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }

    }

}

四、测试

 


我是马丁,如果你喜欢,麻烦点个赞~ 下期见~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

马丁代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值