1. 程式人生 > >網路程式設計知識(5)--用Netty實現的一個簡單的HTTP伺服器

網路程式設計知識(5)--用Netty實現的一個簡單的HTTP伺服器

用Netty實現的一個簡單的HTTP伺服器,可以處理靜態檔案,例子中的註釋也比較全。

public class HttpServer {  
    public static void main(String[] args) {  
        ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(  
                Executors.newCachedThreadPool(), Executors.newCachedThreadPool()));  

        bootstrap.setPipelineFactory(new
HttpServerPipelineFactory()); bootstrap.bind(new InetSocketAddress(8080)); System.out.println("伺服器已經啟動,請訪問http://127.0.0.1:8080/index.html進行測試!\n\n"); } }

2.Pipeline

public class HttpServerPipelineFactory implements ChannelPipelineFactory {  
    public ChannelPipeline getPipeline
() throws Exception { // Create a default pipeline implementation. ChannelPipeline pipeline = pipeline(); // Uncomment the following line if you want HTTPS // SSLEngine engine = // SecureChatSslContextFactory.getServerContext().createSSLEngine(); // engine.setUseClientMode(false);
// pipeline.addLast("ssl", new SslHandler(engine)); pipeline.addLast("decoder", new HttpRequestDecoder()); // Uncomment the following line if you don't want to handle HttpChunks. // pipeline.addLast("aggregator", new HttpChunkAggregator(1048576)); pipeline.addLast("encoder", new HttpResponseEncoder()); // Remove the following line if you don't want automatic content // compression. //pipeline.addLast("aggregator", new HttpChunkAggregator(1048576)); pipeline.addLast("chunkedWriter", new ChunkedWriteHandler()); pipeline.addLast("deflater", new HttpContentCompressor()); pipeline.addLast("handler", new HttpRequestHandler()); return pipeline; } }

3.handler類

import static org.jboss.netty.handler.codec.http.HttpHeaders.is100ContinueExpected;  

import static org.jboss.netty.handler.codec.http.HttpHeaders.isKeepAlive;  
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;  
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.COOKIE;  
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.SET_COOKIE;  
import static org.jboss.netty.handler.codec.http.HttpResponseStatus.CONTINUE;  
import static org.jboss.netty.handler.codec.http.HttpResponseStatus.OK;  
import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1;  

import java.io.File;  
import java.io.RandomAccessFile;  
import java.util.List;  
import java.util.Map;  
import java.util.Map.Entry;  
import java.util.Set;  

import org.jboss.netty.buffer.ChannelBuffer;  
import org.jboss.netty.channel.Channel;  
import org.jboss.netty.channel.ChannelFutureListener;  
import org.jboss.netty.channel.ChannelHandlerContext;  
import org.jboss.netty.channel.ExceptionEvent;  
import org.jboss.netty.channel.MessageEvent;  
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;  
import org.jboss.netty.handler.codec.http.Cookie;  
import org.jboss.netty.handler.codec.http.CookieDecoder;  
import org.jboss.netty.handler.codec.http.CookieEncoder;  
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;  
import org.jboss.netty.handler.codec.http.HttpChunk;  
import org.jboss.netty.handler.codec.http.HttpChunkTrailer;  
import org.jboss.netty.handler.codec.http.HttpHeaders;  
import org.jboss.netty.handler.codec.http.HttpMethod;  
import org.jboss.netty.handler.codec.http.HttpRequest;  
import org.jboss.netty.handler.codec.http.HttpResponse;  
import org.jboss.netty.handler.codec.http.HttpResponseStatus;  
import org.jboss.netty.handler.codec.http.QueryStringDecoder;  
import org.jboss.netty.handler.stream.ChunkedFile;  
import org.jboss.netty.util.CharsetUtil;  

public class HttpRequestHandler extends SimpleChannelUpstreamHandler {  

    private HttpRequest request;  
    private boolean readingChunks;  

    @Override  
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {  
        if (!readingChunks) {  
            HttpRequest request = this.request = (HttpRequest) e.getMessage();  
            String uri = request.getUri();  
            System.out.println("-----------------------------------------------------------------");  
            System.out.println("uri:"+uri);  
            System.out.println("-----------------------------------------------------------------");  
            /** 
             * 100 Continue 
             * 是這樣的一種情況:HTTP客戶端程式有一個實體的主體部分要傳送給伺服器,但希望在傳送之前檢視下伺服器是否會 
             * 接受這個實體,所以在傳送實體之前先發送了一個攜帶100 
             * Continue的Expect請求首部的請求。伺服器在收到這樣的請求後,應該用 100 Continue或一條錯誤碼來進行響應。 
             */  
            if (is100ContinueExpected(request)) {  
                send100Continue(e);  
            }  
            // 解析http頭部  
            for (Map.Entry<String, String> h : request.getHeaders()) {  
                System.out.println("HEADER: " + h.getKey() + " = " + h.getValue() + "\r\n");  
            }  
            // 解析請求引數  
            QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.getUri());  
            Map<String, List<String>> params = queryStringDecoder.getParameters();  
            if (!params.isEmpty()) {  
                for (Entry<String, List<String>> p : params.entrySet()) {  
                    String key = p.getKey();  
                    List<String> vals = p.getValue();  
                    for (String val : vals) {  
                        System.out.println("PARAM: " + key + " = " + val + "\r\n");  
                    }  
                }  
            }  
            if (request.isChunked()) {  
                readingChunks = true;  
            } else {  
                ChannelBuffer content = request.getContent();  
                if (content.readable()) {  
                    System.out.println(content.toString(CharsetUtil.UTF_8));  
                }  
                writeResponse(e, uri);  
            }  
        } else {// 為分塊編碼時  
            HttpChunk chunk = (HttpChunk) e.getMessage();  
            if (chunk.isLast()) {  
                readingChunks = false;  
                // END OF CONTENT\r\n"  
                HttpChunkTrailer trailer = (HttpChunkTrailer) chunk;  
                if (!trailer.getHeaderNames().isEmpty()) {  
                    for (String name : trailer.getHeaderNames()) {  
                        for (String value : trailer.getHeaders(name)) {  
                            System.out.println("TRAILING HEADER: " + name + " = " + value + "\r\n");  
                        }  
                    }  
                }  
                writeResponse(e, "/");  
            } else {  
                System.out.println("CHUNK: " + chunk.getContent().toString(CharsetUtil.UTF_8)  
                        + "\r\n");  
            }  
        }  
    }  

    private void writeResponse(MessageEvent e, String uri) {  
        // 解析Connection首部,判斷是否為持久連線  
        boolean keepAlive = isKeepAlive(request);  

        // Build the response object.  
        HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);  
        response.setStatus(HttpResponseStatus.OK);  
        // 服務端可以通過location首部將客戶端導向某個資源的地址。  
        // response.addHeader("Location", uri);  
        if (keepAlive) {  
            // Add 'Content-Length' header only for a keep-alive connection.  
            response.setHeader(CONTENT_LENGTH, response.getContent().readableBytes());  
        }  
        // 得到客戶端的cookie資訊,並再次寫到客戶端  
        String cookieString = request.getHeader(COOKIE);  
        if (cookieString != null) {  
            CookieDecoder cookieDecoder = new CookieDecoder();  
            Set<Cookie> cookies = cookieDecoder.decode(cookieString);  
            if (!cookies.isEmpty()) {  
                CookieEncoder cookieEncoder = new CookieEncoder(true);  
                for (Cookie cookie : cookies) {  
                    cookieEncoder.addCookie(cookie);  
                }  
                response.addHeader(SET_COOKIE, cookieEncoder.encode());  
            }  
        }  
        final String path = Config.getRealPath(uri);  
        File localFile = new File(path);  
        // 如果檔案隱藏或者不存在  
        if (localFile.isHidden() || !localFile.exists()) {  
            // 邏輯處理  
            return;  
        }  
        // 如果請求路徑為目錄  
        if (localFile.isDirectory()) {  
            // 邏輯處理  
            return;  
        }  
        RandomAccessFile raf = null;  
        try {  
            raf = new RandomAccessFile(localFile, "r");  
            long fileLength = raf.length();  
            response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(fileLength));  
            Channel ch = e.getChannel();  
            ch.write(response);  
            // 這裡又要重新溫習下http的方法,head方法與get方法類似,但是伺服器在響應中只返回首部,不會返回實體的主體部分  
            if (!request.getMethod().equals(HttpMethod.HEAD)) {  
                ch.write(new ChunkedFile(raf, 0, fileLength, 8192));//8kb  
            }  
        } catch (Exception e2) {  
            e2.printStackTrace();  
        } finally {  
            if (keepAlive) {  
                response.setHeader(CONTENT_LENGTH, response.getContent().readableBytes());  
            }  
            if (!keepAlive) {  
                e.getFuture().addListener(ChannelFutureListener.CLOSE);  
            }  
        }  
    }  

    private void send100Continue(MessageEvent e) {  
        HttpResponse response = new DefaultHttpResponse(HTTP_1_1, CONTINUE);  
        e.getChannel().write(response);  
    }  

    @Override  
    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {  
        e.getCause().printStackTrace();  
        e.getChannel().close();  
    }  
}  

4.配置類

public class Config {  

    public static String getRealPath(String uri) {  
        StringBuilder sb=new StringBuilder("/home/guolei/workspace/Test/web");  
        sb.append(uri);  
        if (!uri.endsWith("/")) {  
            sb.append('/');  
        }  
        return sb.toString();  
    }  
}  

5.頁面
在專案中新建一個資料夾,名稱為web(可以在配置中配置),在資料夾中放入靜態頁面index.html。
6.啟動伺服器,測試

相關推薦

網路程式設計知識(5)--Netty實現一個簡單HTTP伺服器

用Netty實現的一個簡單的HTTP伺服器,可以處理靜態檔案,例子中的註釋也比較全。 public class HttpServer { public static void main(String[] args) {

自己-Netty-實現一個簡單的-RPC

  轉自:http://thinkinjava.cn/2018/03/%E8%87%AA%E5%B7%B1%E7%94%A8-Netty-%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E7%9A%84-RPC/ 目錄: 需求

Netty學習——Netty實現一個簡單Http伺服器

package study.netty; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.C

Windows 上靜態編譯 Libevent 2.0.10 並實現一個簡單 HTTP 伺服器

      假設 Visual Studio 2005 的安裝路徑為“D:\Program Files\Microsoft Visual Studio 8\”,Libevent 2.0.10 解壓後的路徑為“D:\libevent-2.0.10-stable”。 編譯生成L

Numpy實現一個簡單的神經網路

本示例來自於PyTorch的官網上的一個warm-up小示例, 覺得很有代表性, 所有這裡單獨記錄一下. 對於numpy來說, 它對計算圖, 深度學習, 梯度等等概念幾乎是不知道的, 但是, 如果我們瞭

java實現一個簡單的單戶登陸功能的思路

get 單用戶 這樣的 簡單的 lock ref 數據庫 清除 一個 引用 所謂“單用戶單賬戶登錄”是指:在同一系統中,一個用戶名不能在兩個地方同時登錄。 我們參照 QQ 實現效果:當某賬號在 A 處登錄後,在未退出的情況下,如果再到 B 處登錄,那麽,系統會擠下 A 處

C# 實現一個簡單的 Rest Service 供外部調

message [] operation rem adk www span method title 用 C# 實現一個簡單的 Restful Service 供外部調用,大體總結為4點: The service contract (the methods it o

Vue實現一個簡單的輪播效果

Vue實現簡單的輪播效果,用的的一些常用系統指令: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" conte

類方法實現python實現一個簡單的單詞本,添加/查找/刪除單詞。

end code div keys style 成功 move print utf 1.實現一個簡單的單詞本,功能: ①添加單詞,當所添加的單詞已存在時,讓用戶知道 ②查找單詞,當查找的單詞不存在時,讓用戶知道 ③刪除單詞,當刪除的單詞不存在時,讓用戶知道 以上

【人工智慧】Python實現一個簡單的人臉識別,原來我和這個明星如此相似

近幾年來,興起了一股人工智慧熱潮,讓人們見到了AI的能力和強大,比如影象識別,語音識別,機器翻譯,無人駕駛等等。總體來說,AI的門檻還是比較高,不僅要學會使用框架實現,更重要的是,需要有一定的數學基礎,如線性代數,矩陣,微積分等。 幸慶的是,國內外許多大神都已經給我們造好“輪子”,我們可以直接來使用某些模型

【人工智能】Python實現一個簡單的人臉識別,原來我和這個明星如此相似

數值 但是 智能 深度學習 lib python 數學 三方 python實現 近幾年來,興起了一股人工智能熱潮,讓人們見到了AI的能力和強大,比如圖像識別,語音識別,機器翻譯,無人駕駛等等。總體來說,AI的門檻還是比較高,不僅要學會使用框架實現,更重要的是,需要有一定的數

js實現一個簡單的mvvm

這裡利用的object.defineproperty() 方法; <input     id='input'><p id='p'><p/>js: const data={}; const input=documen

TextView實現一個簡單的Android資訊顯示工具

本文用 TextView 實現一個在手機上顯示 Android 資訊的工具類。比如涉及到訊號的傳遞時,那種類似日誌記錄的功能。先看圖: 先看佈局檔案的程式碼,注意 TextView 裡面的幾個屬性就可以了。 <?xml version="1.0" encoding="utf-8"

Python實現一個簡單的——人臉相似度對比

近幾年來,興起了一股人工智慧熱潮,讓人們見到了AI的能力和強大,比如影象識別,語音識別,機器翻譯,無人駕駛等等。總體來說,AI的門檻還是比較高,不僅要學會使用框架實現,更重要的是,需要有一定的數學基礎,如線性代數,矩陣,微積分等。 幸慶的是,國內外許多大神都已經給我們造好“輪子”,我們可以直

【很有趣】Python實現一個簡單的人臉識別,原來我和這個明星如此相似

近幾年來,興起了一股人工智慧熱潮,讓人們見到了AI的能力和強大,比如影象識別,語音識別,機器翻譯,無人駕駛等等。總體來說,AI的門檻還是比較高,不僅要學會使用框架實現,更重要的是,需要有一定的數學基礎,如線性代數,矩陣,微積分等。 幸慶的是,國內外許多大神都已經給我們造好“輪子”,我們可

echarts實現一個簡單的生成圖表的功能

說實話一直想做一個可以生成圖表的檔案,但是一直研究不明白,曾經也看過很多的類似技術的檔案,D3.js,Hcharts,Echarts都看過,但是看不下去,一個是api寫的很死板,一個是自己事情比較多,今天不是很忙,簡單的看了一下,寫一個簡單的生成圖表,很簡單,沒有什麼技術含量

Nodejs實現一個簡單的爬蟲功能。(ES6標準)

Nodejs版本:v10.11.0 依賴模組:express,superagent,cheerio 程式碼: const express = require('express'); const superagent = require('superagent'); co

mpvue實現一個簡單的demo

序言 上一篇從騰訊後臺搭建以及搭建本地開發環境這兩個方面進行總結。在進行編碼時,這兩種搭建方式也能提供更好的開發環境,提高實際的開發效率。 這一節主要分享的便是如果用mpvue實現一個基礎的demo,這個demo主要會從mpvue的特性、結構,以及生命週期來實現,期間遇到

java實現一個簡單的ArrayList

重複造輪子雖然不可取,但是溫習一下資料結構,光看不做總是少了什麼,所以也來實現一下List,希望多多包涵。 既然要實現一個List,先來簡單說一下List的定義 線性表是最基本、最簡單、也是最常用的一種資料結構。 線性表中資料元素之間的關係是一對一的關係

Python3實現一個簡單的爬蟲。

import urllib import urllib.request def loadPage(url,filename): """ 作用:根據url傳送請求,獲取html資料;