整合營銷服務商

          電腦端+手機端+微信端=數(shù)據同步管理

          免費咨詢熱線:

          Tomcat進階學習上篇

          、Tomcat原理理論介紹

          1.1 Tomcat簡介

          Tomcat 服務器是一個免費的開放源代碼的Web 應用服務器,屬于輕量級應用服務器,在中小型系統(tǒng)和并發(fā)訪問用戶不是很多的場合下被普遍使用,是開發(fā)和調試JSP 程序的首選。對于一個初學者來說,可以這樣認為,當在一臺機器上配置好Apache 服務器,可利用它響應HTML(標準通用標記語言下的一個應用)頁面的訪問請求。實際上Tomcat是Apache 服務器的擴展,但運行時它是獨立運行的,所以當你運行tomcat 時,它實際上作為一個與Apache 獨立的進程單獨運行的。

          1.1.1Tomcat目錄結構

          目錄說明bin主要存放編譯后的代碼的地方,startup.bat、shutdown.bat分別對應windows版本的啟動和關閉conf主要存放配置文件目錄,context.xml 存放上下文的配置信息,logging.properties存放日志配置信息,server.xml指定服務端的一些配置信息比如端口號,web.xml指定一些Servlet的配置文件lib存儲一些依賴的jar包(tomcat也是java開發(fā)的)Logs存放日志相關的包temp存放一些臨時文件的包webapps主要存放解壓之后的war的項目信息Work工作相關的信息

          1.2 請求處理流程

          1.2.1計算機的網絡通信模型



          應用層

          不同計算機上應用之間的通信,來解決通信雙方數(shù)據傳輸?shù)膯栴}。或者說不同計算機上對應的應用進程之間的通信。支持的協(xié)議有:HTTP FTP TFTP SMTP SNMP DNS TELNET HTTPS DHCP等。(協(xié)議)

          表示層

          用來規(guī)定傳輸?shù)臄?shù)據格式以及加解密,格式有,JPEG、ASCll、DECOIC、加密格式等。【在四層模型里面已經合并到了應用層】

          會話層

          用來規(guī)定開始、控制和終止一個會話。對應主機的進程,指本地主機與遠程主機正在進行的會話。【在四層模型里面已經合并到了應用層】

          傳輸層

          規(guī)定傳輸數(shù)據的協(xié)議端口號,以及流控和差錯校驗。支持的協(xié)議有:TCP UDP等,數(shù)據包一旦離開網卡即進入網絡傳輸層。指定IO模型 BIO NIO AIO等

          網絡層

          進行邏輯地址尋址,實現(xiàn)不同網絡之間的路徑選擇。(路由選擇) 協(xié)議有:ICMP IGMP IP(IPV4 IPV6) ARP RARP。

          數(shù)據鏈路層

          建立邏輯連接、進行硬件地址尋址、差錯校驗等功能。(由底層網絡定義協(xié)議,ATM,F(xiàn)DDI等)將比特組合成字節(jié)進而組合成幀,用MAC地址訪問介質,能錯誤發(fā)現(xiàn)但不能糾正。

          物理層

          建立、維護、斷開物理連接。(由底層網絡定義協(xié)議,RJ45,802.3等)【在四層模型里面已經合并到了數(shù)據鏈路層】

          1.2.2瀏覽器訪問服務器流程

          http請求處理過程



          第2步是先建立連接,進行3次握手。

          1.3 Tomcat 系統(tǒng)總體架構

          1.3.1Tomcat處理請求大致流程

          因為Tomcat可以處理Http請求,因此稱Tomcat為Http服務器。

          如果我們直接從瀏覽器發(fā)送請求,Tomcat直接接收請求,并將請求轉發(fā)到對應的java程序的話,也可以實現(xiàn)請求處理,但是耦合度非常高,因此Tomcat的做法是在接收到http請求之后,將請求轉發(fā)給servlet容器,由servlet容器在進行處理并轉發(fā)到對應的java程序。

          因此在Tomcat內部也實現(xiàn)了Servlet規(guī)范。(Servlet接?和Servlet容器這?整套內容叫作Servlet規(guī)范。

          因此Tomcat有兩個身份:

          1、Http服務器

          2、Servlet容器



          1.3.2Tomcat Servlet容器處理過程


          步驟說明:

          1)Tomcat中Http服務器模塊會接收到原始的請求參數(shù)Request,封裝ServletRequest準備請求Servlet容器

          2)將請求轉發(fā)到Servlet容器中定位Servlet(URL和Servlet的映射關系,找到相應的Servlet)

          3)如果Servlet還未加載,利用反射機制創(chuàng)建Servlet,并調用Servlet的init初始化方法

          4)獲取到具體的Servlet實例之后直接調用對應的業(yè)務處理方法執(zhí)行業(yè)務

          5)將處理好的響應結果封裝成ServletResponse

          6)Http服務器在將ServletResponse對象封裝成原生的Response相應到瀏覽器

          1.3.3Tomcat系統(tǒng)總體架構總結


          Tomcat中包括兩大核心組件:

          連接器(Connector)組件,容器(Container)組件。(還包括其他組件)

          連接器(Connector)組件主要作用:處理Socket連接,負責?絡字節(jié)流與Request和Response對象的轉化。【與客戶端交互】

          容器(Container)組件主要作用:加載和管理Servlet,處理Request請求;

          1.4Tomcat組件

          1.4.1連接器組件 Coyote

          1)Coyote簡介

          Coyote是Tomcat中連接器的組件名稱,是對應的接口。客戶端通過Coyote與服務器建立連接、發(fā)送請求并接受響應。

          2)主要職責

          1)Coyote封裝了底層網絡通信(Socket請求及相應處理)

          2)Coyote使Container容器組件與具體的請求協(xié)議及IO操作?式完全解耦

          3)Coyote將Socket輸入轉換封裝為Request對象,并進一步封裝成ServletRequest交給Container容器進行處理,處理完成后返回ServletResponse給Coyote,Coyote將對象轉換成Response對象將結果寫入輸出流。

          4)Coyote負責的是具體協(xié)議(應?層)和IO(傳輸層)相關內容。

          3)Tomcat Coyote支持的IO模型與協(xié)議

          支持的協(xié)議

          應用層應用層描述描述HTTP/1.1大部分Web應用采用的協(xié)議【Tomcat8.x默認協(xié)議】AJPAJP定向包協(xié)議,實現(xiàn)對靜態(tài)資源的優(yōu)化以及集群的部署。HTTP/2HTTP2.0大幅度提升了web性能,屬于下一代的HTTP協(xié)議但是用的很少。

          支持的IO

          傳輸層IO模型描述NIO同步非阻塞I/O、采用javaNIO類庫實現(xiàn)【Tomcat8默認IO模型】NIO2異步非阻塞I/O、采用jdk7的NIO類庫實現(xiàn)APR采用Apache可移植運行庫實現(xiàn),是C/C++編寫的本地庫,如果使用需要單獨安裝APR庫。

          Tomcat在8.0之前默認使用的是BIO。如果使用APR性能可能達到Apache Http Server的性能。

          4)Coyote內部組件



          Coyote組件其中包括EndPoint組件、Processor組件、Adapter組件。

          EndPoint:EndPoint 是 Coyote 通信端點,即通信監(jiān)聽的接?,是具體Socket接收和發(fā)送處理器,是對傳輸層的抽象,因此EndPoint?來實現(xiàn)TCP/IP協(xié)議的。

          Processor:Processor 是Coyote 協(xié)議處理接?,如果說EndPoint是?來實現(xiàn)TCP/IP協(xié)議的,那么Processor?來實現(xiàn)HTTP協(xié)議,Processor接收來?EndPoint的Socket,讀取字節(jié)流解析成Tomcat的 Request和Response原生對象。

          Adapter:Tomcat Request對象不是標準的ServletRequest,不能?Tomcat Request作為參數(shù)來調?容器。Tomcat設計者的解決?案是引?CoyoteAdapter,將參數(shù)轉換成ServlerRequest對象。


          ProtocolHandler:由Endpoint 和 Processor組成,Tomcat 按照協(xié)議和I/O 提供了6個實現(xiàn)類 : AjpNioProtocol,AjpAprProtocol,AjpNio2Protocol,Http11NioProtocol,Http11Nio2Protocol,Http11AprProtocol。

          1.4.2Tomcat Catalina組件


          1)Catalina組件地位

          本來Catalina組件只是Servlet容器的一個組件,而Tomcat是由一些列組件組成,組件可以在conf/server.xml文件中配置。Catalina在Tomcat中的地位非常的核心,因此經常把tomcat的一個實例當作一個Catalina實例。



          2)Catalina結構



          整個Tomcat就相當于一個Catalina實例,Tomcat啟動的時候會先初始化這個實例Catalina,Catalina實例對象通過加載server.xml完成其他實例的創(chuàng)建,創(chuàng)建并管理?個Server,Server創(chuàng)建并管理多個服務,每個服務?可以有多個Connector和?個Container。


          對應關系:一個Tomcat對應一個Catalina,對應一個Server,對應多個Service。每一個Service實例有多個Connector和一個Container實例。

          Catalina:負責解析Tomcat的配置?件(server.xml) , 以此來創(chuàng)建服務器Server組件并進?管理

          Server:Server是整個Catalina Servlet容器以及其它組件,作用負責組裝并啟動Servlaet引擎,Tomcat連接器。Server通過實現(xiàn)Lifecycle接?,提供了?種優(yōu)雅的啟動和關閉整個系統(tǒng)的?式。

          Service:Service是Server內部的組件,?個Server包含多個Service。它將若?個Connector組件綁定到?個Container。

          Container:容器,負責處理?戶的servlet請求,并返回對象給web?戶的模塊。


          3)Container 組件介紹

          Container組件其中包括Engine、Host、Context、Wrapper4種組件,他們之間是父子關系。Tomcat通過分層的架構,讓Servlet容器具有很好的靈活性。



          Engine:表示整個Servlet容器的引擎,用來管理多個虛擬站點,一個Service只能有一個Engine。

          Host:代表一個虛擬主機,或者說一個站點,可以配置多個虛擬主機地址,一個Engine可以有多個Host。

          Context:表示一個Web應用服務器,一個Host下可以包含多個Context。

          Wrapper:表示一個Servlet, 一個Context中可以包括多個Wrapper。

          2、Tomcat核心配置

          思考:

          去哪兒配置?->conf/server.xml中

          怎么配置?

          2.1server.xml配置詳解

          2.1.1server配置文件概述

          server.xml中包括Server根標簽,Listener,GlobalNamingResources,Service

          Server標簽:主要用來創(chuàng)建一個server實體對象

          Listener標簽:定義監(jiān)聽器

          GlobalNamingResources 標簽:定義服務器的全局JNDI(Java Naming and Directory Interface 標準的Java命名系統(tǒng)接口)資源。

          Service標簽:定義?個Service服務,?個Server標簽可以有多個Service服務實例


          Server.xml整體結構

          <?xml version="1.0" encoding="UTF-8"?>
          <!--
          port:關閉服務器的監(jiān)聽端?
          shutdown:關閉服務器的指令字符串
          -->
          <Server port="8005" shutdown="SHUTDOWN">
              <!-- 以?志形式輸出服務器 、操作系統(tǒng)、JVM的版本信息 -->
              <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
              <!-- Security listener. Documentation at /docs/config/listeners.html
              <Listener className="org.apache.catalina.security.SecurityListener" />
              -->
              <!--APR library loader. Documentation at /docs/apr.html -->
              <!-- 加載(服務器啟動) 和 銷毀 (服務器停?) APR。 如果找不到APR庫, 則會輸出?志, 并
              不影響 Tomcat啟動 -->
              <Listener className="org.apache.catalina.core.AprLifecycleListener"
                        SSLEngine="on" />
              <!-- Prevent memory leaks due to use of particular java/javax APIs-->
              <!-- 避免JRE內存泄漏問題 -->
              <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
              <!-- 加載(服務器啟動) 和 銷毀(服務器停?) 全局命名服務 -->
              <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
              <!-- 在Context停?時重建 Executor 池中的線程, 以避免ThreadLocal 相關的內存泄漏 -->
              <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
              <!-- Global JNDI resources
              Documentation at /docs/jndi-resources-howto.html
              GlobalNamingResources 中定義了全局命名服務
              -->
              <GlobalNamingResources>
                  <!-- Editable user database that can also be used by
                  UserDatabaseRealm to authenticate users
                  -->
                  <Resource name="UserDatabase" auth="Container"
                            type="org.apache.catalina.UserDatabase"
                            description="User database that can be updated and saved"
                            factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
                            pathname="conf/tomcat-users.xml" />
              </GlobalNamingResources>
              <!-- A "Service" is a collection of one or more "Connectors" that share
              a single "Container" Note: A "Service" is not itself a "Container",
              so you may not define subcomponents such as "Valves" at this level.
              Documentation at /docs/config/service.html
              -->
              <Service name="Catalina">
                  ...
              </Service>
          </Server>


          2.1.2Service標簽

          <Server port="8005" shutdown="SHUTDOWN">
             ....
              <Service name="Catalina">
                  <!--為Connector創(chuàng)建一個線程池-->
                  <!--
                  <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
                      maxThreads="150" minSpareThreads="4"/>
                  -->
                  <!--創(chuàng)建一個監(jiān)聽8080的連接器組件 -->
                  <Connector port="8080" protocol="HTTP/1.1"
                             connectionTimeout="20000"
                             redirectPort="8443" />
                  <!-- 創(chuàng)建連接池的連接器 -->
                  <!--
                  <Connector executor="tomcatThreadPool"
                             port="8080" protocol="HTTP/1.1"
                             connectionTimeout="20000"
                             redirectPort="8443" />
                  -->
          
                  <!-- 創(chuàng)建一個監(jiān)聽8009的ajp的connector -->
                  <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
          
                  <!--創(chuàng)建引擎 管理 多個Host-->
                  <Engine name="Catalina" defaultHost="localhost">
                      <!--配置集群-->
                      <!--
                      <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
                      -->
                      <Realm className="org.apache.catalina.realm.LockOutRealm">
                          <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
                                 resourceName="UserDatabase"/>
                      </Realm>
          
                      <!--host來管理各個Servlet-->
                      <Host name="localhost"  appBase="webapps"
                            unpackWARs="true" autoDeploy="true">
                          <!--servlet-->
                          <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
                                 prefix="localhost_access_log" suffix=".txt"
                                 pattern="%h %l %u %t "%r" %s %b" />
          
                      </Host>
                  </Engine>
              </Service>
          </Server>

          Service標簽包括Executor 、Connector、Engine、Host等子標簽。


          Executor標簽

          	<Executor 
                    name="commonThreadPool"  <!--指定當前線程的名字 相當于Id-->
                    namePrefix="thread-exec-" <!-- namePrefix 給線程指定前綴名字 -->
                    maxThreads="200"  <!--指定最大線程數(shù) -->
                    minSpareThreads="100" <!-- 指定核心線程數(shù) -->
                    maxIdleTime="60000" <!-- 指定線程空閑時間 超過該時間線程被自動銷毀單位是毫秒 -->
                    maxQueueSize="Integer.MAX_VALUE"  <!-- 阻塞隊列的大小最大值 -->
                    prestartminSpareThreads="false" <!--確定線程池時是否啟動核心線程-->
                    threadPriority="5" <!--線程中的優(yōu)先級默認是5 取值范圍是1到10 -->
                    className="org.apache.catalina.core.StandardThreadExecutor" <!-- 自定義線程池類路徑 -->
                   />

          Executor主要作用是來創(chuàng)建一個線程池,用于連接器Connector使用。

          Connector標簽

          <Connector 
                  port="8080" <!--監(jiān)聽的端口號--> 
                  protocol="HTTP/1.1"  
                  connectionTimeout="20000" <!--連接超時時間單位毫秒-->
                  redirectPort="8443" />

          protocol="HTTP/1.1": 當前Connector?持的訪問協(xié)議。 默認為 HTTP/1.1 ,并采??動切換機制選擇?個基于 JAVANIO 的鏈接器或者基于本地APR的鏈接器(根據本地是否含有Tomcat的本地庫判定)

          redirectPort="8443":當前Connector 不?持SSL請求, 接收到了?個請求, 并且也符合security-constraint 約束,需要SSL傳輸,Catalina?動將請求重定向到指定的端?。

          URIEncoding="UTF-8":?于指定編碼URI的字符編碼, Tomcat8.x版本默認的編碼為 UTF-8 , Tomcat7.x版本默認為ISO-8859-1。

          executor="commonThreadPool":指定共享線程池的名稱,也可以通過maxThreads、minSpareThreads 等屬性配置內部線程池。

          內部線程Demo

          	<Connector port="8080"
                     protocol="HTTP/1.1"
                     executor="commonThreadPool"
                     maxThreads="1000"
                     minSpareThreads="100"
                     acceptCount="1000"
                     maxConnections="1000"
                     connectionTimeout="20000"
                     compression="on"  <!--是否壓縮數(shù)據, on是開啟壓縮 off關閉-->
                     compressionMinSize="2048" <!--壓縮的最小容量-->
                     disableUploadTimeout="true" <!--是否允許Servlet容器,正在執(zhí)行使用一個較長的連接超時-->
                     redirectPort="8443"
                     URIEncoding="UTF-8" />

          Engine 標簽

          	<Engine name="Catalina" defaultHost="localhost">
              ...
          	</Engine>

          name屬性: ?于指定Engine 的名稱, 默認為Catalina

          defaultHost:默認使?的虛擬主機名稱, 當客戶端請求指向的主機?效時, 將交由默認的虛擬主機處

          理, 默認為localhost。

          Host標簽

          <Host name="localhost2"  appBase="webapps2"
                unpackWARs="true" autoDeploy="true">
          
               <!--用來指定日志配置文件地址 valve-->
              <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
                     prefix="localhost_access_log" suffix=".txt"
                     pattern="%h %l %u %t "%r" %s %b" />
          
          </Host>

          Host標簽屬于engine的子標簽,主要配置虛擬主機的。默認可以配置多個,appBase假如不配置context默認訪問ROOT文件下的應用。

          name:用于指定虛擬主機。

          appBase:對應的配置路徑

          unpackWARs:解壓war包默認為true

          autoDeploye:自動提交


          Context 標簽

          <Context docBase="/Users/demo/web_demo" path="/web3"></Context>

          Context屬于Host的子標簽,主要用來映射應用所在的文件,比如上邊的訪問uri是web3映射到 /Users/demo/web_demo文件夾下。

          docBase:Web應??錄或者War包的部署路徑。可以是絕對路徑,也可以是相對于Host appBase的相對路徑。

          path:Web應?的Context 路徑。如果我們Host名為localhost, 則該web應?訪問的根路徑為:localhost:8080/web3。

          3、手寫簡易版本Tomcat

          3.1分析

          手寫一個Tomcat名稱叫做Minicat,需求:可以接收瀏覽器的http請求,并進行請求處理,處理之后將結果返回給瀏覽器客戶端。

          1)提供服務,接收請求(Socket通信)

          2)請求信息封裝成Request對象(Response對象)

          3)客戶端請求資源,資源分為靜態(tài)資源和動態(tài)資源

          4)將資源返回給客戶端

          3.2 V1版本Code

          public class Bootstrap {
          
              //自定義端口號
              public static final int port = 8080;
          
              //啟動方法
              private void start() throws IOException {
                  ServerSocket serverSocket = new ServerSocket(port);
                  System.out.println("啟動成功,端口號:" + port);
                  while (true) {
                      Socket accept = serverSocket.accept();
          
                      OutputStream outputStream = accept.getOutputStream();
                      String content = "Hello Word !";
                      String retResult = HttpProtocolUtil.getHeader200(content.getBytes().length) + content;
                      outputStream.write(retResult.getBytes());
          
                      outputStream.close();
                  }
          
              }
          
              public static void main(String[] args) {
                  Bootstrap bootstrap = new Bootstrap();
                  try {
                      bootstrap.start();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }


          封裝相應頭工具類

          public class HttpProtocolUtil {
           /**
               * 獲取200響應結果
               */
           public static String getHeader200(int size){
                  return "HTTP/1.1 200 OK \n" +
                          "Content-Type: text/html \n" +
                          "Content-Length: " + size + " \n" +
                          "\r\n";
              }
          
           /**
               * 獲取404響應結果
               */
           public static String getHeader404(){
                  String str404 = "<p1>404 Not Found</p1>";
                  return "HTTP/1.1 404 Not Found \n" +
                          "Content-Type: text/html \n" +
                          "Content-Length: " + str404.length() + " \n" +
                          "\r\n" + str404;
              }
          }

          總結:V1版本的代碼比較簡單,就是簡單在頁面上寫輸入一個8080返回一個Hello Minicat但是需要注意在返回結果中必須添加上html響應頭信息瀏覽器才能顯示

          3.3 V2版本Code

          要求:能夠輸出靜態(tài)資源文件。

          3.3.1 觀察請求參數(shù)

          private void start() throws IOException {
              ServerSocket serverSocket = new ServerSocket(port);
              while (true){
                  Socket socket = serverSocket.accept();
                  InputStream inputStream = socket.getInputStream();
          
                  //防止網絡波動讀取不到參數(shù)
                  int count = 0;
                  while (count == 0) {
                      count = inputStream.available();
                  }
          
                  byte[] bytes = new byte[count];
                  inputStream.read(bytes);
                  System.out.println(new String(bytes));
          
                  socket.close();
              }
          }

          打印的參數(shù)

          GET / HTTP/1.1

          Host: localhost:8080

          Connection: keep-alive

          Cache-Control: max-age=0

          Upgrade-Insecure-Requests: 1

          User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36

          Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9

          Sec-Fetch-Site: none

          Sec-Fetch-Mode: navigate

          Sec-Fetch-User: ?1

          Sec-Fetch-Dest: document

          Accept-Encoding: gzip, deflate, br

          Accept-Language: zh-CN,zh;q=0.9

          只提取一些有用的請求信息,比如GET、/ 路徑信息、參數(shù)信息。來封裝成Request對象和Response對象。

          3.3.2完成V2版本的Code

          Bootstrap啟動類

          public class Bootstrap {
              private static final int port = 8080;
          
              public static void main(String[] args) {
                  Bootstrap bootstrap = new Bootstrap();
                  try {
                      bootstrap.start();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          
           /**
               * V2.0版本
               */
           private void start() throws IOException {
                  ServerSocket serverSocket = new ServerSocket(port);
                  System.out.println("啟動成功");
          
                  while (true){
                      Socket socket = serverSocket.accept();
                      InputStream inputStream = socket.getInputStream();
                      OutputStream outputStream = socket.getOutputStream();
          
                      //獲取到輸入輸出對象
                      Request request = new Request(inputStream);
                      Response response = new Response(outputStream);
          
                      //將結果寫到輸出流中
                      response.outputHtml(request.getUrl());
          
                      //一定要把socket關閉
                      socket.close();
                  }
              }
          }

          Request對象

          @Data
          @NoArgsConstructor
          public class Request {
              //請求方式GET、POST
              private String methodType;
              //請求URL
              private String url;
              //輸入流
              private InputStream inputStream;
          
          
           /**
               * 構造器
               */
           public Request(InputStream inputStream) throws IOException {
                  this.inputStream = inputStream;
                  parseInputParam(inputStream);
              }
          
              //解析參數(shù)
              private void parseInputParam(InputStream inputStream) throws IOException {
                  //只提取第一行
                  int length = 0;
                  while (length == 0){
                      length = inputStream.available();
                  }
                  byte[] bytes = new byte[length];
                  inputStream.read(bytes);
                  String inputStr = new String(bytes);
                  //截取出來 GET / HTTP/1.1
                  String[] split = inputStr.split("\\n");
                  String[] infoArr = split[0].split(" ");
                  this.methodType = infoArr[0];
                  this.url = infoArr[1];
          
                  System.out.println("=====>>method:" + methodType);
                  System.out.println("=====>>url:" + url);
              }
          }

          Response響應對象

          @Data
          @AllArgsConstructor
          @NoArgsConstructor
          public class Response {
              private OutputStream outputStream;
          
              //V2版本只輸出靜態(tài)資源文件
              public void outputHtml(String path) throws IOException {
                  //獲取到文件的絕對路徑
                  String absolutePath = StaticResourceUtil.getAbsolutePath(path);
                  File file = new File(absolutePath);
                  if (file.exists() && file.isFile()) {
          
                      //調用工具類輸出文件
                      StaticResourceUtil.outputStaticResource(new FileInputStream(file), outputStream);
                  }else{
                      //404 Not Found
                      output(HttpProtocolUtil.getHeader404());
                  }
              }
          
              //輸出文件
              private void output(String path) throws IOException {
                  outputStream.write(path.getBytes());
              }
          }


          靜態(tài)資源處理工具

          public class StaticResourceUtil {
          
           /**
               * 獲取到文件的絕對路徑并且替換\\為/方便linux識別
               */
           public static String getAbsolutePath(String path) {
                  //獲取到當前的絕對路徑
                  String absolutePath = StaticResourceUtil.class.getResource("/").getPath();
                  return (absolutePath + path).replaceAll("\\\\", "/");
              }
          
           /**
               * 根據輸入流 對文件進行輸出
               *
               * @param inputStream  輸入流
               * @param outputStream 輸出流
               */
           public static void outputStaticResource(InputStream inputStream, 
                                                         OutputStream outputStream) throws IOException {
                  //緩沖區(qū)
                  int buffer = 1024;
                  int actualOutSize = 0;
          
                  //獲取需要輸出文件流的長度
                  int outputSize = 0;
                  while (outputSize == 0){
                      outputSize = inputStream.available();
                  }
          
                  //輸出請求頭
                  outputStream.write(HttpProtocolUtil.getHeader200(outputSize).getBytes());
          
                  byte[] bytes = new byte[buffer];
                  while (actualOutSize < outputSize){
                      //如果最后不夠一個緩沖區(qū)的話需要獲取到最后的數(shù)據length
                      if (actualOutSize + buffer > outputSize) {
                          buffer = outputSize - actualOutSize;
                          bytes = new byte[buffer];
                      }
                      //從輸入流中讀取
                      inputStream.read(bytes);
                      //寫出到輸出流
                      outputStream.write(bytes);
                      //刷新輸出流
                      outputStream.flush();
                      actualOutSize += buffer;
                  }
              }
          }

          總結: 底層使用的是JavaSocket編程,就是對應輸入和輸出流進行了封裝,截取了需要的信息。

          3.4 V3版本Code

          需求,能夠接收靜態(tài)和動態(tài)資源,使用線程池接收。

          基于V2版本Request和Response進行開發(fā)。

          添加Pom Dom4j用來配置servlet

          <dependencies>
              <dependency>
                  <groupId>org.projectlombok</groupId>
                  <artifactId>lombok</artifactId>
                  <version>1.18.12</version>
              </dependency>
          
              <dependency>
                  <groupId>dom4j</groupId>
                  <artifactId>dom4j</artifactId>
                  <version>1.6.1</version>
              </dependency>
              <dependency>
                  <groupId>jaxen</groupId>
                  <artifactId>jaxen</artifactId>
                  <version>1.1.6</version>
              </dependency>
          </dependencies>


          web.xml配置文件

          <?xml version="1.0" encoding="UTF-8"?>
          <web-app>
          
              <servlet>
                  <servlet-name>myServlet</servlet-name>
                  <servlet-class>com.tomcat.demo.servlet.MyServlet</servlet-class>
              </servlet>
          
              <servlet-mapping>
                  <servlet-name>myServlet</servlet-name>
                  <url-pattern>/hello</url-pattern>
              </servlet-mapping>
          
          </web-app>

          Servlet

          public interface Servlet {
             //初始化
             void init() throws Exception;
             //銷毀
             void destroy() throws Exception;
             //執(zhí)行請求
             void service(Request request, Response response) throws Exception;
          }
          public abstract class HttpServlet implements Servlet {
          
              public abstract void doGet(Request request, Response response)throws Exception;
              public abstract void doPost(Request request, Response response)throws Exception;
          
              @Override
              public void service(Request request, Response response) throws Exception {
                  if ("GET".equalsIgnoreCase(request.getMethodType())) {
                      doGet(request,response);
                  }else {
                      doPost(request,response);
                  }
              }
          }
          
          public class MyServlet extends HttpServlet {
          
              @Override
              public void doGet(Request request, Response response) throws Exception {
                  doPost(request,response);
              }
          
              @Override
              public void doPost(Request request, Response response) throws Exception {
                  String content = "<H1>Hello Servlet</H1>";
                  String header200 = HttpProtocolUtil.getHeader200(content.getBytes().length);
                  response.output(header200 + content);
              }
          
              @Override
              public void init() throws Exception {
                  System.out.println("初始化方法...");
              }
          
              @Override
              public void destroy() throws Exception {
                  System.out.println("銷毀方法...");
          
              }
          }

          BootStrap啟動類

          public class Bootstrap {
          
              private static final int port = 8080;
              private Map<String, Servlet> servletMap = new HashMap<>();
          
          
              public static void main(String[] args) {
                  Bootstrap bootstrap = new Bootstrap();
                  try {
                      bootstrap.start();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          
           /**
               * 啟動tomcat
               */
           private void start() throws IOException {
                  ServerSocket serverSocket = new ServerSocket(port);
          
                  //解析web.xml
                  parseWebXml();
          
                  //創(chuàng)建一個線程池
                  ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                          20,//指定核心線程數(shù)
                          100,//指定最大線程數(shù)
                          100L,//指定存活時間
                          TimeUnit.MILLISECONDS,//指定時間格式
                          new LinkedBlockingDeque<>(1000));//設置阻塞隊列大小
          
                  while (true){
                      //獲取到socket
                      Socket socket = serverSocket.accept();
          
                      //通過線程池去執(zhí)行
                      threadPool.execute(new RequestProcessor(socket, servletMap));
                  }
              }
          
              private void parseWebXml() {
                  try {
                      InputStream resourceAsStream = this.getClass().getResourceAsStream("/web-apps/WEB-INF/web.xml");
                      Document document = new SAXReader().read(resourceAsStream);
                      Element rootElement = document.getRootElement();
                      Element servletElement = (Element) rootElement.selectSingleNode("servlet");
                      String servletName = servletElement.selectSingleNode("servlet-name").getStringValue();
                      String servletClass = servletElement.selectSingleNode("servlet-class").getStringValue();
          
                      Element servletMapping = (Element) rootElement.selectSingleNode("servlet-mapping[servlet-name = '" + servletName + "']");
                      String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();
          
                      servletMap.put(urlPattern, (Servlet) Class.forName(servletClass).newInstance());
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              }
          }

          RequestProcessor請求處理類

          @Data
          @AllArgsConstructor
          @NoArgsConstructor
          public class RequestProcessor implements Runnable {
              private Socket socket;
              private Map<String, Servlet> servletMap;
          
              @Override
              public void run() {
                  try {
                      InputStream inputStream = socket.getInputStream();
                      OutputStream outputStream = socket.getOutputStream();
          
                      Request request = new Request(inputStream);
                      Response response = new Response(outputStream);
          
                      //動態(tài)資源
                      if (servletMap.containsKey(request.getUrl())) {
                          servletMap.get(request.getUrl()).service(request, response);
                      }else {
                          //靜態(tài)資源
                          response.outputHtml(request.getUrl());
                      }
          
                      //關閉當前socket
           socket.close();
                  }catch (Exception e){
                      e.printStackTrace();
                  }
              }
          }


          總結:書寫Mini版本的Tomcat感受一下Tomcat整體的處理思路。

          多數(shù)PHP程序都使用HTML表單從用戶那里獲取數(shù)據并計算結果。

          HTML表單的一些基本原則

          • 選擇適合于收集的數(shù)據類型和提供交互方式的控件。
          • 清楚標記每一個控件,這樣用戶就可以理解其功能。
          • 盡可能將標簽對齊。將控件的左邊緣對齊。
          • 將相關的標簽分組,并且通過設計中使用空白將每一個分組分開。
          • 表單上的控件順序應該類似于用戶操作他們的順序。

          創(chuàng)建基本HTML表單

          首先創(chuàng)造一個基本的HTML大綱,包含表單控件;然后將控件進行合并(HTML表單必須包括一個提交按鈕,用戶單擊它可以將表單數(shù)據發(fā)送到服務器。)一個單獨的HTML頁面可以包含多個表單。

          • 創(chuàng)建HTML結構

          包含表單的HTML結構和和普通的HTML結構一樣。

          <HTML>
            <HEAD>
            <TITLE>標題放在這</TITLE>
            </HEAD>
          <BODY>
            表單頁面放在這
            </BODY>
            </HTML>

          在包含表單的HTML頁面中可以使用任何HTML標簽。基本的表單使用FROM標簽來說明。該標簽中METHOD屬性接收GET或POST兩個值中的一個。ACTION屬性子明PHP腳本的url,該腳本可以收集通過表單收集的數(shù)據,可以是絕對路徑或者相對路徑

          <FORM METHOD="method" ACTION="url"> 
            中間可以放置表單控件
            </FORM>
          • 合并控件

          兩個常用的基本控件:文本框和提交按鈕。

          文本框:允許用戶鍵入信息以發(fā)送給PHP腳本。NAME屬性為文本提供名稱,PHP腳本可以通過名稱準確訪問其內容,因此它應該是唯一的且符合PHP變量命名規(guī)則(但不需要$符號),單標簽。VALUE屬性指明出現(xiàn)在提交按鈕上面的標題。創(chuàng)建方式如下:

          <INPUT TYPE = "TEXT" NAME="text">

          提交按鈕:允許用戶將一個表單的內容發(fā)送到服務器,一個HTML表單對應應該有一個提交按鈕。

          示例:一個完整的HTML表單。

          <HTML>
            <HEAD>
            <TITLE>標題</TITLE>
            </HEAD>
          <BODY>
          <FORM METHOD="POST" ACTION="phpinfo.php">
          <INPUT TYPE="TEXT" NAME="user_name">
          <BR/>
          <BR/>
          <INPUT TYPE="TEXT" NAME="user_email">
          <BR/>
          <BR/>
          <INPUT TYPE="SUBMIT" VALUE="Send the Data">
          </FORM>
            </BODY>
            </HTML>


          • 使用多個表單

          可以在一個HTML頁面中包含多個表單,注意下一個表單的FORM開始之前需要結束前一個FORM表單。

          <HTML>
            <HEAD>
            <TITLE>標題</TITLE>
            </HEAD>
          <BODY>
            
          <FORM METHOD="POST" ACTION="phpinfo.php">
          <INPUT TYPE="TEXT" NAME="user_name">
          <BR/>
          <BR/>
          <INPUT TYPE="TEXT" NAME="user_email">
          <BR/>
          <BR/>
          <INPUT TYPE="SUBMIT" VALUE="Send the Data">
          <BR/>
          <BR/>
          </FORM>
          
          <FORM METHOD="POST" NAME="phpinfo.php">
          <INPUT TYPE="TEXT" NAME="user_name1">
          <BR/>
          <BR/>
          <INPUT TYPE="TEXT" NAME="user_email1">
          <BR/>
          <BR/>
          <INPUT TYPE="SUBMIT" VALUE="Send the Data1">
          </FORM>
          
            </BODY>
            </HTML>


          創(chuàng)建表單控件

          • 創(chuàng)建自定義的文本框

          文本框的屬性中,TYPE和NAME是必須的,其余是可選屬性。SIZE屬性用于設置文本框的可視大小;MAXLENGTH指明用戶鍵入字符的最大長度;VALUE給出了一個最初顯示在文本框中的值。

          <input type="text" name="" size="" maxlength="" value="">
          • 創(chuàng)建文本區(qū)域

          文本區(qū)域可以輸入多行文本。NAME和ROWS屬性是必須的。ROWS屬性表明了文本區(qū)域內可以看到的文本行數(shù),充滿時會滾動。COLS屬性指明可見文本列數(shù)與行數(shù)類似。WRAP屬性指明文本區(qū)域內單詞換行的方式,可以指定如下值。該標簽為雙標簽。

          說明

          off

          禁止單詞換行但用戶可以輸入換行符強制換行

          virtual/soft

          各行顯示為換行,但是換行并沒有被發(fā)送到服務器

          physica/hard

          啟用了單詞換行

          <inputarea name="" rows="" cols="" wrap="">
          • 創(chuàng)建密碼框

          創(chuàng)建密碼框的語法與文本框相同,但要將TYPE屬性指定為PASSWORD而不是TYPE。

          <input type="password" name="" size="" maxlength="" value="">
          • 創(chuàng)建復選框

          取兩個值中的一個,即二選一。TYPE屬性是必須的,checked屬性出現(xiàn),該復選框默認情況會被選定。value屬性指定復選框被選定情況下被發(fā)送到服務器的值,默認發(fā)送on值。法如下:

          <input type="checkbox" name="" checked value="">
          • 創(chuàng)建單選按鈕

          語法與復選框屬性含義相同,但是TYPE屬性的值必須是RADIO,NAME屬性是必須的。

          <input type="radio" name="" checked value="">
          • 創(chuàng)建列表框

          用戶可以選擇一個或者多個選項,它是一個滾動菜單。

          <select name="" multipile size="">options go here</select>

          name屬性是必須的,multipile屬性指明用戶可以通過按下crtl鍵并單擊多個選項來選擇它們

          列表框的單選行為可作為單選按鈕。

          <option selected value="text"></options>
          • 創(chuàng)建隱藏域
          <input type="hidden" name="text"value="">
          • 實現(xiàn)上傳文件的HTML表單
          <input type="FILE" name="name" accept="time" value="text">

          其中type屬性是必須的。格式通過使用MIME碼指定。常用的格式如下:


          超文本標記語言文本 .html,.html text/html

            普通文本 :txt text/plain

            word文檔:application/msword

            RTF文本 :rtf application/rtf

            GIF圖形 :gif image/gif

            JPEG圖形 :jpeg,

            jpg: image/jpeg

            au聲音文件:au audio/basic

            MIDI音樂文件 :mid,.midi audio/midi,audio/x-midi

            RealAudio音樂文件 .ra, .ram audio/x-pn-realaudio

            MPEG文件 .mpg,.mpeg video/mpeg

            AVI文件 .avi video/x-msvideo

            GZIP文件 .gz application/x-gzip

            壓縮文件.rar application/octet-stream

            壓縮文件.zip application/x-zip-compressed

            TAR文件 .tar application/x-tar


          更多提交表單的信息

          • 使用圖像提交數(shù)據
          <input type="image" src="url" name="text" align="align">
          • 創(chuàng)建重置按鈕
          <input type="reset" value="text">

          olang 簡介

          Go(又稱Golang)是Google開發(fā)的一種靜態(tài)強類型、編譯型、并發(fā)型,并具有垃圾回收功能的編程語言。

          golang發(fā)展前景

          適合云計算、微服務、基礎后端軟件的功能實現(xiàn)。

          go語言優(yōu)點

          go是非常年輕的一門語言,“兼具Python 等動態(tài)語言的開發(fā)速度和C/C++等編譯型語言的性能與安全性”,具備強大的標準庫,內置垃圾回收機制,屬于靜態(tài)類型語言,因此具備編譯檢查和編碼規(guī)范,特別是并發(fā)功能實現(xiàn)簡單,但功能強大,可使用goroutine和channel輕松實現(xiàn)高并發(fā)。

          golang web 優(yōu)勢

          語法簡單:語法簡潔,新手入門簡單,開發(fā)應用性能高。

          便于維護:強類型靜態(tài)語言,便于項目的重構和維護。

          部署方便:編譯的可執(zhí)行二進制文件,在執(zhí)行時,不需要部署環(huán)境,把編譯文件上傳,就完成了項目的部署。

          1 - 構建Web服務

          2 - 查看GET請求報文

          GET /index HTTP/1.1 //請求行信息 , 方法是GET , 請求地址是/index , 請求版本HTTP/1.1

          Host: 127.0.0.1 //以下是請求頭信息

          Connection: keep-alive

          Cache-Control: max-age=0

          Upgrade-Insecure-Requests: 1

          User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36

          Sec-Fetch-User: ?1

          Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9

          Sec-Fetch-Site: none

          Sec-Fetch-Mode: navigate

          Accept-Encoding: gzip, deflate, br

          Accept-Language: zh-CN,zh;q=0.9

          3 - 查看POST請求報文

          構建請求頁面

          構建請求服務端

          查看請求行 , 請求頭 , 請求體

          4 - 查看相應

          5 - 響應狀態(tài)碼

          1xx :(臨時響應),代表請求已被接受,需要繼續(xù)處理。

          2xx :(成功),代表請求已成功被服務器接收、理解、并接受。

          3xx :(重定向),代表需要客戶端采取進一步的操作才能完成請求。

          4xx :(請求錯誤),代表客戶端看起來可能發(fā)生了錯誤,妨礙了服務器的處理。

          5xx :(服務器錯誤),代表服務器在處理請求的過程中有錯誤或者異常狀態(tài)發(fā)生。

          常見的狀態(tài)碼

          200 : 服務器成功返回網頁

          404 : 請求的網頁不存在

          503 : 服務不可用


          主站蜘蛛池模板: 国产精品无码一区二区在线观一 | 麻豆国产一区二区在线观看| 日韩一区二区三区视频| 亚洲色无码专区一区| 一区二区3区免费视频| 无码毛片一区二区三区视频免费播放 | 国产美女一区二区三区| 成人精品一区二区三区校园激情 | 免费一区二区无码视频在线播放| 成人午夜视频精品一区| 国产成人亚洲综合一区| 久久精品一区二区东京热| 国产一区二区在线观看麻豆| 人妻无码视频一区二区三区| 国产精品美女一区二区| 国产伦精品一区二区三区女| 中文字幕不卡一区| 国产一区二区三区在线免费| 无码人妻精品一区二区三区久久久| 亚洲熟妇av一区二区三区| 国产免费一区二区三区免费视频 | 亚洲日韩国产一区二区三区| 精品一区二区三人妻视频| 无码人妻AV免费一区二区三区| 精品一区二区三区水蜜桃| 亚洲AV网一区二区三区| 亚洲一区二区三区丝袜| 亚洲av乱码一区二区三区| 日韩精品一区二区三区老鸭窝| 免费无码一区二区三区蜜桃| 一区二区视频免费观看| 无码人妻一区二区三区在线水卜樱| 国产一区二区三区视频在线观看| 国产品无码一区二区三区在线| 精品国产一区二区三区香蕉事| 亚洲AV网一区二区三区| 一区五十路在线中出| 亚洲电影一区二区三区| 国产一区二区三区小说| 日本一区二区三区久久| 精品福利一区二区三|