使用Netty編寫一個(gè)極簡(jiǎn)的Http服務(wù)器

Netty是一個(gè)高性能的網(wǎng)絡(luò)編程框架,有著簡(jiǎn)單易于使用的抽象模型。利用Netty自帶的Http協(xié)議編解碼器扳抽,我們可以快速地以較少的代碼編寫一個(gè)簡(jiǎn)單的Http服務(wù)器艺糜。

本文主要介紹如何使用Netty實(shí)現(xiàn)一個(gè)極簡(jiǎn)的Http服務(wù)器异袄,主要功能是通過(guò)接收一個(gè)瀏覽器Http請(qǐng)求,Netty服務(wù)器返回一個(gè)靜態(tài)資源頁(yè)面。

為了實(shí)現(xiàn)Http服務(wù)器,我們只需要用到Netty自帶的兩個(gè)編解碼器:

  • HttpServerCodec:用于Netty服務(wù)端旭寿,該類其實(shí)是HttpRequestDecoderHttpResponseEncoder的封裝,因此我們?cè)?code>ChannelPipeline中加入HttpServerCodec即可實(shí)現(xiàn)Http請(qǐng)求的解碼和Http響應(yīng)的編碼崇败;
  • HttpObjectAggregator:Http請(qǐng)求經(jīng)過(guò)HttpServerCodec解碼之后是HttpRequestHttpContents對(duì)象盅称,HttpObjectAggregator會(huì)將多個(gè)HttpRequestHttpContents對(duì)象再拼裝成一個(gè)FullHttpRequest,再將其傳遞到下個(gè)Handler

首先缩膝,我們首先創(chuàng)建一個(gè)maven項(xiàng)目搭幻,在pom.xml文件中添加netty-all依賴即可:

<dependency>
  <groupId>io.netty</groupId>
  <artifactId>netty-all</artifactId>
  <version>4.1.32.Final</version>
</dependency>

第二步,編寫HttpServer類逞盆,作為Http服務(wù)啟動(dòng)的入口。此處我們通過(guò)命令行傳入Http服務(wù)啟動(dòng)的端口號(hào)松申。HttpServer類代碼如下:

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
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.HttpServerCodec;

import java.net.InetSocketAddress;

public class HttpServer {

    private final int port;

    public HttpServer(int port) {
        this.port = port;
    }

    public static void main(String[] args) throws Exception {
        if (args.length != 1) {
            System.err.println("Usage: " + HttpServer.class.getSimpleName() + " <port>");
            return;
        }
        int port = Integer.parseInt(args[0]);
        new HttpServer(port).start();
    }

    public void start() throws Exception {
        NioEventLoopGroup group = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(group)
                    .channel(NioServerSocketChannel.class)
                    .localAddress(new InetSocketAddress(port))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) {
                            ch.pipeline().addLast("codec", new HttpServerCodec())
                                    .addLast("aggregator", new HttpObjectAggregator(512 * 1024))
                                    .addLast(new HttpRequestHandler());
                        }
                    });

            ChannelFuture f = b.bind().sync();
            System.out.println(HttpServer.class.getName() + " started and listen on " + f.channel().localAddress());
            f.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully().sync();
        }
    }
}

接下來(lái)需要編寫我們自己的HttpRequestHandler云芦,來(lái)處理Http請(qǐng)求相關(guān)的業(yè)務(wù)需求,即通過(guò)接收一個(gè)Http請(qǐng)求贸桶,返回一個(gè)靜態(tài)資源頁(yè)面的功能舅逸。

我們先創(chuàng)建一個(gè)index.html,并把它放到/src/main/resources目錄下皇筛,index文件如下所示:

<html>
<head>
    <title>Hello Netty</title>
    <meta charset="UTF-8">
</head>
<body>
<h1>Hello Netty</h1>
<p>Netty Http Server測(cè)試</p>
</body>
</html>

HttpRequestHandler類如下:

import io.netty.channel.*;
import io.netty.handler.codec.http.*;

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

public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
        String url = this.getClass().getResource("/").getPath() + "index.html";
        File file  = new File(url);
        RandomAccessFile raf = new RandomAccessFile(file, "r");
        HttpResponse response = new DefaultHttpResponse(msg.protocolVersion(), HttpResponseStatus.OK);
        ctx.write(response);
        ctx.write(new DefaultFileRegion(raf.getChannel(),0, raf.length()));
        ChannelFuture future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
        future.addListener(ChannelFutureListener.CLOSE);
    }
}

代碼只有很少的幾行琉历。首先我們獲取index.html所在的路徑, 創(chuàng)建RandomAccessFile對(duì)象指向index.html文件水醋。通過(guò)調(diào)用ChannelHandlerContextwrite方法寫入一個(gè)HttpResponse旗笔,其中包含了Http協(xié)議版本和服務(wù)端200的響應(yīng)碼。接著再寫入index.html文件內(nèi)容拄踪,最后再寫入LastHttpContent并同時(shí)調(diào)用flush蝇恶。最后在ChannelFuture上增加監(jiān)聽器以便在Http響應(yīng)完成后關(guān)閉channel

我們通過(guò)命令行啟動(dòng)Http服務(wù)器惶桐,同時(shí)指定相應(yīng)端口撮弧。打開瀏覽器訪問(wèn)后效果如下:

https://i.loli.net/2021/01/10/hUAzm5JHaWvqfB2.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市姚糊,隨后出現(xiàn)的幾起案子贿衍,更是在濱河造成了極大的恐慌,老刑警劉巖救恨,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贸辈,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡忿薇,警方通過(guò)查閱死者的電腦和手機(jī)裙椭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)署浩,“玉大人揉燃,你說(shuō)我怎么就攤上這事〗疃埃” “怎么了炊汤?”我有些...
    開封第一講書人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我抢腐,道長(zhǎng)姑曙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任迈倍,我火速辦了婚禮伤靠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘啼染。我一直安慰自己宴合,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開白布迹鹅。 她就那樣靜靜地躺著卦洽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪斜棚。 梳的紋絲不亂的頭發(fā)上阀蒂,一...
    開封第一講書人閱讀 49,784評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音弟蚀,去河邊找鬼蚤霞。 笑死,一個(gè)胖子當(dāng)著我的面吹牛义钉,可吹牛的內(nèi)容都是我干的争便。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼断医,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼滞乙!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起鉴嗤,我...
    開封第一講書人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤斩启,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后醉锅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體兔簇,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年硬耍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了垄琐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡经柴,死狀恐怖狸窘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情坯认,我是刑警寧澤翻擒,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布氓涣,位于F島的核電站,受9級(jí)特大地震影響陋气,放射性物質(zhì)發(fā)生泄漏劳吠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一巩趁、第九天 我趴在偏房一處隱蔽的房頂上張望痒玩。 院中可真熱鬧,春花似錦议慰、人聲如沸凰荚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至缆毁,卻和暖如春番川,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背脊框。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工颁督, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人浇雹。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓沉御,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親昭灵。 傳聞我的和親對(duì)象是個(gè)殘疾皇子吠裆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容