Netty學習筆記(五) 使用Netty構建靜態網頁服務器


👆關注微信公眾號,獲取更多編程內容


昨天在繼續完善基於Netty構建的聊天室系統的過程中,發現了一個有意思的知識點,特此拿來做一個簡單的靜態網頁服務器,好好的玩一玩Netty。

但是不管怎么說利用netty實現各種功能的流程都是類似的

  • 配置ServerHandle
  • (可選)實現自定義的編碼器
  • 完成ServerBootStarp的配置
  • 啟動服務
  • 連接到該服務

好的,那么我們基於此來實現一個簡單靜態網頁需求,要求實現能夠通過地址訪問html,js,css,以及圖片等資源文件,那么開始吧

靜態網頁資源服務器

HttpServerHandleAdapter

這里是最為復雜的步驟,具體代碼可以看注釋。

package com.zhoutao123.simpleChat.html;

import io.netty.channel.*;
import io.netty.handler.codec.http.*;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.stream.ChunkedNioFile;

import java.io.File;
import java.io.RandomAccessFile;

public class HttpServerHandleAdapter extends SimpleChannelInboundHandler<FullHttpRequest> {

    // 資源所在路徑
    private static final String location;

    // 404文件頁面地址
    private static final File NOT_FOUND;

    static {
        // 構建資源所在路徑,此處參數可優化為使用配置文件傳入
        location = "/home/tao/code/resource";
        // 構建404頁面
        String path = location + "/404.html";
        NOT_FOUND = new File(path);
    }


    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
        // 獲取URI
        String uri = request.getUri();
        // 設置不支持favicon.ico文件
        if ("favicon.ico".equals(uri)) {
            return;
        }
        // 根據路徑地址構建文件
        String path = location + uri;
        File html = new File(path);

        // 狀態為1xx的話,繼續請求
        if (HttpHeaders.is100ContinueExpected(request)) {
            send100Continue(ctx);
        }

        // 當文件不存在的時候,將資源指向NOT_FOUND
        if (!html.exists()) {
            html = NOT_FOUND;
        }

        RandomAccessFile file = new RandomAccessFile(html, "r");
        HttpResponse response = new DefaultHttpResponse(request.getProtocolVersion(), HttpResponseStatus.OK);

        // 文件沒有發現設置狀態為404
        if (html == NOT_FOUND) {
            response.setStatus(HttpResponseStatus.NOT_FOUND);
        }

        // 設置文件格式內容
        if (path.endsWith(".html")){
            response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/html; charset=UTF-8");
        }else if(path.endsWith(".js")){
            response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "application/x-javascript");
        }else if(path.endsWith(".css")){
            response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/css; charset=UTF-8");
        }

        boolean keepAlive = HttpHeaders.isKeepAlive(request);

        if (keepAlive) {
            response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, file.length());
            response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
        }
        ctx.write(response);

        if (ctx.pipeline().get(SslHandler.class) == null) {   
            ctx.write(new DefaultFileRegion(file.getChannel(), 0, file.length()));
        } else {
            ctx.write(new ChunkedNioFile(file.getChannel()));
        }
        // 寫入文件尾部
        ChannelFuture future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);  
        if (!keepAlive) {
            future.addListener(ChannelFutureListener.CLOSE);
        }
        file.close();
    }

    private static void send100Continue(ChannelHandlerContext ctx) {
        FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE);
        ctx.writeAndFlush(response);
    }
}

HttpServerInitializer

添加我們剛剛完成的HttpServerHandleAdapter

package com.zhoutao123.simpleChat.html;


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;
import io.netty.handler.stream.ChunkedWriteHandler;


public class HttpServerInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    public void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        //將請求和應答消息編碼或解碼為HTTP消息
        pipeline.addLast(new HttpServerCodec());
        //將HTTP消息的多個部分組合成一條完整的HTTP消息
        pipeline.addLast(new HttpObjectAggregator(64 * 1024));
        pipeline.addLast(new ChunkedWriteHandler());
        pipeline.addLast(new HttpServerHandleAdapter());
    }
}

服務啟動

public class Server {

    private final static int  port = 8080;
    
    public static void main(String[] args) throws InterruptedException {
        NioEventLoopGroup boss = new NioEventLoopGroup();
        NioEventLoopGroup work = new NioEventLoopGroup();

        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(boss, work)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new HttpServerInitializer())
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);
            ChannelFuture future = serverBootstrap.bind(port).sync();
            // 等待服務器  socket 關閉 。
            // 在這個例子中,這不會發生,但你可以優雅地關閉你的服務器。
            future.channel().closeFuture().sync();
        } finally {
            work.shutdownGracefully();
            boss.shutdownGracefully();
        }
    }
}

靜態資源文件

靜態資源文件這里主要測試html以及js和css文件,這里寫了3個html文件(404.html用於當輸入路徑不存在的時候,跳轉到的文件),一個js文件,一個css文件,一個logo圖片,測試js和css在index.html文件中測試

需要注意的是這些資源文件需要放在上面代碼中的location指定的位置,否則可能會出現訪問不到的異常情況

具體的代碼如下:

index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Netty靜態資源服務器</title>
    <link rel="stylesheet" href="css/style.css"/>

</head>
<body>
    <h1 id="title">你好,歡迎來到基於Netty構建的靜態資源服務器主頁</h1>
    <h1 style="color: blue">當前位置Index.html</h1>
    <input type="button" value="啟動問候語" onclick="sayHello()"/>
    <a href="about.html">關於Netty和作者</a>
</body>
    <script src="js/message.js"></script>
</html>

about.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>WebSocket Chat</title>
    <link rel="stylesheet" href="css/style.css">
</head>
<body>
<h1 style="color: blue">這是關於界面</h1>
<img style="width: 250px" src="logo.jpg">
<a href="index.html">點擊我,返回主頁</a>
</body>
</html>

404.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>WebSocket Chat</title>
</head>
<body>
<h1 style="color: red">沒有發現此頁面</h1>
</body>
</html>

js文件

function sayHello() {
    alert("你好,歡迎使用Netty服務器")
}

css文件

#title{
    color: red;
    text-underline: darkcyan;
}

input{
    background-color: darkcyan;

}

文件整體結構

.
├── index.html
├── 404.html
├── about.html
├── logo.jpg
├── css
│   └── style.css
└── js
    └── message.js

2 directories, 5 files

測試效果

瀏覽器輸入URL即可訪問

主頁

可以看到訪問成功,地址是localhost:8080/index.html ,開發者工具中展示狀態均為200

調用JS效果

點擊啟動問候語會執行js腳本彈出窗口

跳轉效果

點擊超鏈接 ,即可跳轉到about.html界面,這里引用了一個jpg圖片

404頁面

當輸入一個不存在的文件的時候,可以看到狀態為404.

總結

基於Netty實現的一個簡單的靜態資源服務器,可以說實現了基本的功能,但是還有其他很多idea可以實現,如負載均衡,設置配置文件(如資源所在路徑以及端口等信息),


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM