<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          手寫一個(gè)HTTP圖片資源服務(wù)器,太容易了叭!

          共 30652字,需瀏覽 62分鐘

           ·

          2021-09-02 20:20

          閱讀本文大概需要 8 分鐘。

          來自:binhao.blog.csdn.net/article/details/112059406

          摘要

          web開發(fā)一直是行業(yè)熱門技術(shù),而要做web程序就離不開http服務(wù)器,現(xiàn)在主流的http服務(wù)器用的最廣的如tomcat,apache。還有商用版本的各式各樣的http服務(wù)器,而再行業(yè)類各種微服務(wù),各種web開發(fā)技術(shù)層出不窮,都是基于這些服務(wù)器上的架構(gòu)上的使用,并沒有從本質(zhì)上提高服務(wù)器運(yùn)行的效率,筆者在研究http服務(wù)的過程中,就花了一早上來寫了這樣一個(gè)http服務(wù)器展示http服務(wù)器工作的流程。

          一、什么是http服務(wù)器

          HTTP服務(wù)器一般指網(wǎng)站服務(wù)器,是指駐留于因特網(wǎng)上某種類型計(jì)算機(jī)的程序,可以處理瀏覽器等Web客戶端的請求并返回相應(yīng)響應(yīng),也可以放置網(wǎng)站文件,讓全世界瀏覽;可以放置數(shù)據(jù)文件,讓全世界下載。1目前最主流的三個(gè)Web服務(wù)器有Tomcat、Apache、 Nginx 。

          http服務(wù)器用來干什么:

          如下圖所示,網(wǎng)絡(luò)通過一個(gè)如tomcat一樣的http服務(wù)器來獲取一臺計(jì)算機(jī)上的資源。計(jì)算機(jī)上的資源可以是計(jì)算機(jī)能提供的任何服務(wù)(包括文件,存儲,計(jì)算能力等等)。
          下圖是tomcat中一個(gè)http請求的傳遞過程你,該圖在tomcat官網(wǎng)可以找到。
          一個(gè)請求到響應(yīng)主要經(jīng)過了tomcat的Engine->Host->Pipeline->執(zhí)行servece方法響應(yīng)http請求。

          二、如何自己簡單實(shí)現(xiàn)一個(gè)http服務(wù)器

          一個(gè)簡單的實(shí)現(xiàn)思路如下,最簡單的過程通俗的說就是,請求進(jìn)來,請求處理器將響應(yīng)的結(jié)果生產(chǎn)出來,再有服務(wù)器發(fā)出響應(yīng)。
          思路上一定是很簡單的,實(shí)現(xiàn)一個(gè)良好可靠的http服務(wù)器確實(shí)一件很難的事。有很多復(fù)制的問題需要解決,如下:
          • 什么樣的請求能進(jìn)來或者說是有效的?(第一層請求攔截)
          • 請求的解析和響應(yīng)結(jié)果的生成?(需要精通http協(xié)議)
          • io模型如何選擇,怎樣充分利用發(fā)揮服務(wù)器性能?(涉及多線程,復(fù)雜的io模型)
          • 可插拔要怎么實(shí)現(xiàn)?如tomcat可以通過一個(gè)servlet讓開發(fā)人員進(jìn)行處理。
          • …等等,實(shí)現(xiàn)一個(gè)http服務(wù)器絕對是一件很困難的事,所有網(wǎng)絡(luò)應(yīng)用層都是基于這個(gè)向上設(shè)計(jì)的。

          三、自己實(shí)現(xiàn)的http服務(wù)器

          說干就干,今早花了一個(gè)小時(shí)寫了一個(gè)簡單的demo來展示,先看下做出來的效果是什么樣的。
          先在我項(xiàng)目配置的文件夾下放兩張圖片
          通過瀏覽器加文件名的方式來訪問試試結(jié)果:
          兩張圖片現(xiàn)在就可以通過網(wǎng)絡(luò)來瀏覽了,怎么樣,很神奇對吧,接下來就看下代碼是怎么實(shí)現(xiàn)的?

          四、Http服務(wù)器實(shí)現(xiàn)(Java)

          HttpServer類:具體的server實(shí)現(xiàn),并且將解析方法也在該類下:
          import java.io.*;
          import java.net.ServerSocket;
          import java.net.Socket;
          import java.nio.ByteBuffer;
          import java.nio.channels.FileChannel;
          import java.util.Properties;

          /**
           * Copyright(c)[email protected]
           * @author liubinhao
           * @date 2021/1/1
           * ++++ ______                           ______             ______
           * +++/     /|                         /     /|           /     /|
           * +/_____/  |                       /_____/  |         /_____/  |
           * |     |   |                      |     |   |        |     |   |
           * |     |   |                      |     |   |________|     |   |
           * |     |   |                      |     |  /         |     |   |
           * |     |   |                      |     |/___________|     |   |
           * |     |   |___________________   |     |____________|     |   |
           * |     |  /                  / |  |     |   |        |     |   |
           * |     |/ _________________/  /   |     |  /         |     |  /
           * |_________________________|/b    |_____|/           |_____|/
           */


          public class HttpServer {
              public static final String RESOURCE_PATH = "D:/image";
              public static void main(String[] args) throws IOException {
                  ServerSocket server = new ServerSocket(80);
                  while (true){
                      Socket client = server.accept();
                      InputStream inputStream = client.getInputStream();
                      String s = handleNewClientMessage(inputStream);
                      Request request = Request.parse(s);
                      System.out.println(request);
                      //假設(shè)請求的都是文件
                      String uri = request.getUri();
                      File file = new File(RESOURCE_PATH + uri);
                      FileInputStream fileInputStream = new FileInputStream(file);
                      FileChannel channel = fileInputStream.getChannel();
                      ByteBuffer buffer = ByteBuffer.allocate((int) file.length());
                      channel.read(buffer);
                      buffer.rewind();
                      Response response = new Response();
                      response.setTopRow("HTTP/1.1 200 OK");
                      response.getHeaders().put("Content-Type",readProperties(getType(request.getUri())));
                      response.getHeaders().put("Content-Length",String.valueOf(file.length()));
                      response.getHeaders().put("Server","httpserver-lbh");
                      response.setBody(buffer.array());
                      response.setOutputStream(client.getOutputStream());
                      response.setClient(client);
                      System.out.println(response);
                      System.out.println(client.isClosed());
                      //響應(yīng)客戶端
                      response.service();
                      inputStream.close();
                      client.close();
                  }
              }


              private static String handleNewClientMessage(InputStream inputStream) throws IOException {
                  byte[] bytes = new byte[1024];
                  int read = inputStream.read(bytes);
                  StringBuilder buffer = new StringBuilder();
                  if (read != -1) {
                      for (byte b:bytes) {
                          buffer.append((char)b);
                      }
                  }
                  return buffer.toString();
              }

              /**
               * 我個(gè)人放mimetype文件類型的文件,需要單獨(dú)修改
               */

              private static final String MIME_PROPERTIES_LOCATION =
                      "D:\\individual\\springcloudDemo\\src\\main\\java\\contentype.properties";

              public static String readProperties(String key) {
                  System.out.println(System.getProperty("user.dir"));
                  Properties prop = new Properties();
                  try {prop.load(new FileInputStream(MIME_PROPERTIES_LOCATION));
                      return prop.get(key).toString();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
                  return null;
              }
              public static String getType(String uri) {
                  System.out.println("uri:" + uri);
                  String stringDot = ".";
                  String dot = "\\.";
                  String[] fileName;
                  if (uri.contains(stringDot)) {
                      fileName = uri.split(dot);
                      return fileName[fileName.length - 1];
                  }
                  return null;
              }

          }
          Request類,請求進(jìn)來的形式:
          import java.util.Arrays;
          import java.util.HashMap;
          import java.util.Map;

          /**
           * Copyright(c)[email protected]
           * @author liubinhao
           * @date 2021/1/1
           * ++++ ______                           ______             ______
           * +++/     /|                         /     /|           /     /|
           * +/_____/  |                       /_____/  |         /_____/  |
           * |     |   |                      |     |   |        |     |   |
           * |     |   |                      |     |   |________|     |   |
           * |     |   |                      |     |  /         |     |   |
           * |     |   |                      |     |/___________|     |   |
           * |     |   |___________________   |     |____________|     |   |
           * |     |  /                  / |  |     |   |        |     |   |
           * |     |/ _________________/  /   |     |  /         |     |  /
           * |_________________________|/b    |_____|/           |_____|/
           */


          public class Request {

              private String method;

              private String uri;

              private String version;

              private byte[] body;

              private Map<String, String> headers;

              public String getMethod() {
                  return method;
              }

              public String getUri() {
                  return uri;
              }

              public String getVersion() {
                  return version;
              }

              public byte[] getBody() {
                  return body;
              }

              public Map<String, String> getHeaders() {
                  return headers;
              }

              public void setMethod(String method) {
                  this.method = method;
              }

              public void setUri(String uri) {
                  this.uri = uri;
              }

              public void setVersion(String version) {
                  this.version = version;
              }

              public void setHeaders(Map<String, String> headers) {
                  this.headers = headers;
              }

              public void setBody(byte[] body) {
                  this.body = body;
              }
              @Override
              public String toString() {
                  return "Request{" +
                          "method='" + method + '\'' +
                          ", uri='" + uri + '\'' +
                          ", version='" + version + '\'' +
                          ", body=" + Arrays.toString(body) +
                          ", headers=" + headers +
                          '}';
              }


              private static final String CONTENT_SEPARATE_SIGN = "\r\n\r\n";

              private static final String LINE_SEPARATE_SIGN = "\r\n";

              private static final String SPACE_SEPARATE_SIGN = "\\s+";

              private static final String KV_SEPARATE_SIGN = ":";

              //**********************************解析請求*************************************
              private static void parseTopReq(Request req, String[] topReq){
                  req.setMethod (topReq[0]);
                  req.setUri    (topReq[1]);
                  req.setVersion(topReq[2]);
              }

              private static void parseHeaders(Request req, String[] headerStr){
                  HashMap<String, String> headers = new HashMap<>(12);
                  for (String s : headerStr) {
                      String[] keyValue = s.split(KV_SEPARATE_SIGN);
                      headers.put(keyValue[0], keyValue[1]);
                  }
                  req.setHeaders(headers);
              }


              public static Request parse(String reqStr) {
                  Request req = new Request();
                  String[] httpInfo = reqStr.split(CONTENT_SEPARATE_SIGN);
                  if (httpInfo.length==0){
                      return null;
                  }
                  if (httpInfo.length > 1){
                      req.setBody(httpInfo[1].getBytes());
                  }
                  String[] content  = httpInfo[0].split(LINE_SEPARATE_SIGN);
                  // http first row of a http request
                  String[] firstRow = content[0].split(SPACE_SEPARATE_SIGN);
                  // http / get demo_path.type
                  parseTopReq(req, firstRow);
                  parseHeaders(req, Arrays.copyOfRange(content,1,content.length));
                  return req;
              }
          }
          Response類,具體響應(yīng)的結(jié)果:
          import java.io.IOException;
          import java.io.OutputStream;
          import java.net.Socket;
          import java.util.HashMap;
          import java.util.Map;

          /**
           * Copyright..@lbhbinhao@163.com
           * Author:liubinhao
           * Date:2020/12/28
           * ++++ ______                           ______             ______
           * +++/     /|                         /     /|           /     /|
           * +/_____/  |                       /_____/  |         /_____/  |
           * |     |   |                      |     |   |        |     |   |
           * |     |   |                      |     |   |________|     |   |
           * |     |   |                      |     |  /         |     |   |
           * |     |   |                      |     |/___________|     |   |
           * |     |   |___________________   |     |____________|     |   |
           * |     |  /                  / |  |     |   |        |     |   |
           * |     |/ _________________/  /   |     |  /         |     |  /
           * |_________________________|/b    |_____|/           |_____|/
           */


          public class Response{

              private String topRow;

              private Map<String, String> headers = new HashMap<>(12);

              private byte[] body;

              private int responseCode;

              private OutputStream outputStream;

              public String getTopRow() {
                  return topRow;
              }

              public void setTopRow(String topRow) {
                  this.topRow = topRow;
              }

              public Map<String, String> getHeaders() {
                  return headers;
              }

              public void setHeaders(Map<String, String> headers) {
                  this.headers = headers;
              }

              public byte[] getBody() {
                  return body;
              }

              public void setBody(byte[] body) {
                  this.body = body;
              }

              public OutputStream getOutputStream() {
                  return outputStream;
              }

              public void setOutputStream(OutputStream outputStream) {
                  this.outputStream = outputStream;
              }

              public int getResponseCode() {
                  return responseCode;
              }

              public void setResponseCode(int responseCode) {
                  this.responseCode = responseCode;
              }

              public static final byte[] LINE_SEPARATE_SIGN_BYTES    = "\r\n".getBytes();
              public static final byte[] KV_SEPARATE_SIGN_BYTES      = ":".getBytes();

              private Socket client;

              public Socket getClient() {
                  return client;
              }

              public void setClient(Socket client) {
                  this.client = client;
              }

              //    如果頭數(shù)據(jù)里不包含Content-Length: x 類型為Transfer-Encoding:
          //    chunked 說明響應(yīng)數(shù)據(jù)的長度不固定,結(jié)束符已"\r\n0\r\n"這5個(gè)字節(jié)為結(jié)束符
              public void service() throws IOException {
                  outputStream.write(this.getTopRow().getBytes());
                  outputStream.write(LINE_SEPARATE_SIGN_BYTES);
                  for (Map.Entry<String, String> entry : this.getHeaders().entrySet()){
                      outputStream.write(entry.getKey().getBytes());
                      outputStream.write(KV_SEPARATE_SIGN_BYTES);
                      outputStream.write(entry.getValue().getBytes());
                      outputStream.write(LINE_SEPARATE_SIGN_BYTES);
                  }
                  outputStream.write(LINE_SEPARATE_SIGN_BYTES);
                  outputStream.write(this.getBody());
                  outputStream.flush();
              }


              @Override
              public String toString() {
                  return "Response{" +
                          "topRow='" + topRow + '\'' +
                          ", headers=" + headers +
          //                ", body=" + Arrays.toString(body) +
                          ", responseCode=" + responseCode +
                          ", client=" + client +
                          '}';
              }
          }
          mimetype文件,用來判斷請求的類型,由于文件太大,只復(fù)制一部分,格式都一樣:
          contentype.properties
          #mdb=application/x-mdb
          mfp=application/x-shockwave-flash
          mht=message/rfc822
          mhtml=message/rfc822
          mi=application/x-mi
          mid=audio/mid
          midi=audio/mid
          mil=application/x-mil
          mml=text/xml
          mnd=audio/x-musicnet-download
          mns=audio/x-musicnet-stream
          mocha=application/x-javascript
          movie=video/x-sgi-movie
          mp1=audio/mp1
          mp2=audio/mp2
          mp2v=video/mpeg
          mp3=audio/mp3
          mp4=video/mp4
          mpa=video/x-mpg
          mpd=application/vnd
          #ms-project
          mpe=video/x-mpeg
          mpeg=video/mpg
          mpg=video/mpg
          mpga=audio/rn-mpeg
          mpp=application/vnd
          #ms-project
          mps=video/x-mpeg
          mpt=application/vnd

          五、總結(jié)

          實(shí)現(xiàn)一個(gè)http服務(wù)什么是核心呢,就在名字里,就是http協(xié)議,http協(xié)議本質(zhì)上就是一個(gè)基于文本解析表示特定的語義規(guī)則,只要讀懂了這些規(guī)則就能夠理解一個(gè)http服務(wù)器。
          但是要開發(fā)一個(gè)http服務(wù)器這個(gè)是遠(yuǎn)遠(yuǎn)不夠的,還需要高超的編程技藝,個(gè)人上述只是一個(gè)小demo,看上去好些很簡單的樣子。在個(gè)人github上有一個(gè)更加完整的實(shí)現(xiàn),基于nio和多線程,效率更高,實(shí)現(xiàn)的更加完善,并且支持?jǐn)帱c(diǎn)續(xù)傳的方式
          github地址:
          https://github.com/haobinliu/app-server/tree/server-x-test/server-x/src/com/lbh
          <END>


          瀏覽 42
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲专区免费 | 日韩黄页网站大全免费在线观看 | 伊人大香蕉在线视频网 | 在线免费A片| 欧美日韩一级二级 |