http協議及httpd-2.2基本功能實現

分類:IT技術 時間:2016-10-10

  HTTP,即:HyperText Transfer Protocol的簡稱,超文本傳輸協議。1960年美國人泰德·尼爾森構思了一種通過計算機處理文本信息的方法,並稱之為超文本(Hypertext),這成為了HTTP標準架構的發展根基。1990年,為了方便分布於世界各地的高能物理學家之間的協作,歐洲粒子物理研究所(European Organization for Nuclear Research,簡稱CERN)的計算機科學家蒂姆·伯納斯·李以及與之一同工作的羅伯特·卡裏奧,提出了一個基於"超文本"的分布式信息系統——一種將存儲於計算機上的相關的信息碎片鏈接起來的方法。在他們的設想中,通過將網絡地址隱藏在屏幕上突出顯示的內容背後,從而將多臺計算機之間信息被鏈接在一起。於是,他們就選用了"World-Wide Web"的名字,簡稱"www"。他們同時也開發了萬維網客戶端和服務器端,還定義了URL(統一資源定位符)、HTML(超文件標記語言)等。後來泰德·尼爾森組織協調萬維網協會(World Wide Web Consortium),並和互聯網工程工作小組(Internet Engineering Task Force共同合作研究,最終發布了一系列的RFC,其中著名RFC1945中定義了HTTP/1.0;RFC2616定義了HTTP/1.1以及RFC7540定義的HTTP/2.0。

  下面簡單來說說HTTP的版本:
  HTTP真正公開使用的是其HTTP/0.9版本,其中只提供了一種"GET"方法,並且沒有在通訊中指定版本號,且不支持請求頭。由於該版本不支持POST方法,所以客戶端無法向服務器傳遞太多信息。

  HTTP/1.0
  這是第一個在通訊中指定版本號的 HTTP 協議版本,並首次引入了緩存的概念。而且定義了GET、POST、HEAD、PUT、DELETE、TRACE、OPTIONS等多種方法,而且引入了MEMI,使得通過HTTP提供多媒體資源分發成為現實。

  HTTP/1.1
  當前最常見和常用的版本。默認采用長連接,GET、HEAD、POST、PUT、DELETE、CONNECT、OPTIONS、TRACE、PATCH、MOVE、COPY、LINK、UNLINK、WRAPPED等14種方法,並且還額外提供了Extension-mothed,以期在不改動協議的前提下,可增加另外的方法。更為合理和強大的緩存處理機制、帶寬優化及網絡連接的使用、錯誤通知的管理、消息在網絡中的發送、互聯網地址的維護、安全性及完整性等諸多方面均較HTTP/1.0版本有明顯提升。

  下面是HTTP/1.1所支持的方法及其相應的功能解釋:
   GET      請求指定的頁面信息,並返回資源主體;
   HEAD      類似於GET請求,只不過返回的響應中沒有具體的內容,用於獲取HTTP首部;
   POST      向指定服務器提交表單數據進行處理請求。請求報文主體種包含了要傳遞的

          數據信息內容。POST請求可能會導致新的資源的建立和/或已有資源的修改。
   PUT      從客戶端向服務器上傳可取代指定的文檔的內容的數據資源。
   DELETE     請求服務器刪除指定的資源。
   CONNECT    HTTP/1.1協議中預留給能夠將連接改為管道方式的代理服務器。
   OPTIONS    允許客戶端查看服務器的性能。
   TRACE     回顯服務器收到的請求,主要用於測試或診斷。
   PATCH     資源主體中包含一個表,表中說明與該URI所表示的原內容的區別。
   MOVE     請求服務器將指定的頁面移至另一個網絡地址。
   COPY     請求服務器將指定的頁面拷貝至另一個網絡地址。
   LINK     請求服務器建立鏈接關系。
   UNLINK    斷開鏈接關系。
   WRAPPED    允許客戶端發送經過封裝的請求。
   Extension-mothed   在不改動協議的前提下,可增加另外的方法。

  HTTP/2.0即超文本傳輸協議2.0版本,是下一代HTTP協議。是由互聯網工程任務組的Hypertext Transfer Protocol Bis工作小組進行開發。是自1999年http1.1發布後的首個更新。HTTP/2.0在2013年8月進行首次合作共事性測試。在開放互聯網上HTTP/2.0將只用於https://網址,而http://網址將繼續使用HTTP/1,目的是在開放互聯網上增加使用加密技術,以提供強有力的保護去遏制主動攻擊。

  HTTP的采用了最為通用的C/S架構模式,並且采用Request/Response的工作方式實施客戶端和服務端的通信,即:一次完整的HTTP事務,必須包含一次Request和一次Response。

  HTTP是應用層協議,在傳輸層依靠TCP來承載和封裝/解封裝HTTP報文。對於HTTP服務器來說,默認采用TCP的80端口作為有效的套接字組合而進行監聽。

  一旦有客戶端的請求發送到服務器,那麽服務器完成一次完整的http請求的處理過程大致可以分為以下幾個階段:
   1.建立或處理連接(可能還包含所需的身份驗證階段)
   2.接收請求
   3.處理請求
   4.訪問資源
   5.構建響應報文
   6.發送響應報文
   7.記錄日誌

  任何一次完整的http事務,都必須在客戶端和服務端的配合下才能順利完成。對於客戶端來說,他們希望每次向服務器發出的請求都能在也應該在極短的時間內被處理,更應該以最高的速度最短的時延來為每個客戶端響應;然而對於服務器來講,不只要是能夠響應客戶就好,服務器需要做的事情就像上面說的那樣,其實有很多步驟的。因此服務器就必須盡量合理分配和利用自身的資源,保證自身正常工作的前提下,盡可能多的處理客戶發送的請求。但是每一個客戶端請求都需要一個進程或線程來處理,這樣就必須要綜合考慮內存空間利用率、CPU計算資源的合理分配以及磁盤IO性能,於是就有了下列幾種服務器的並發響應模型:
  單進程I/O模型:利用極短的時間片分別處理所以請求,使得一系列魚貫而入的請求得以在短時間內處理完成。這個模型適用於訪問量較小請求較簡單的場合,這是偽並發處理機制,其實就是串行處理機制。
  多進程I/O模型:同時啟動多個進程,每個進程響應一個客戶端請求;這種模型相對比較重量級,能夠真正同時處理的請求數的峰值並不會太大,對系統資源消耗較大,但勝在管理簡單,進程的創建和銷毀也較容易。
  復用的I/O模型:一個進程響應多個請求;這種情況略微復雜,其實是利用線程完成處理的,但是在linux中使用的線程是偽線程,其實是輕量級進程(LWP),所以與多進程模型比較起來並沒有太大的區別。下面是兩種常見的實現方式:
   多線程模型:只啟動一個進程,該進程可生成多個線程,每個線程響應一個請求;
   事件驅動模型:只啟動一個進程,一個進程直接響應多個請求;這種模型相對來說性能更加優化。
  復用的多進程I/O結構模型:啟動m個進程,每個進程生成n個線程,每個線程響應一個請求;這個模型在性能上沒有明顯的改觀,但是這是多進程I/O模型和復用I/O多線程模型的折中模型,因此兼具兩種模型的優勢。

  那麽,每次的Request到底是請求什麽呢?可能很多粗略接觸或非業內人士會認為,我用自己的計算機瀏覽網頁,不就是想看哪個網頁就請求哪個網頁麽!這個說法不能說完全錯誤,因為最終非常有可能是服務器將整個頁面都傳輸到了客戶端,但其過程並不是一次性傳輸整個頁面。其實每個頁面都是由很多個更小的基本單元構成的,我們可以將這些基本單元稱為資源。也就是說,每個頁面可能由一個或N多個資源共同組成,而HTTP的每個請求只能從服務器上請求一個資源。如果這個頁面只是一個資源組成的,那麽這一次就可以返回整個頁面;但絕大多數情況下,一個頁面包含了不止一個資源,因此,需要請求者連續完成多次HTTP事務,將所有需要的資源全部緩存到本地,再經過客戶端程序組合之後顯示出來,才成為用戶所看到的完整的網頁頁面。

  因為TCP協議的特性,使得在實現每一次完整的HTTP事務時,都是四個階段:
   1.三次握手建立TCP連接
   2.客戶端發生Request報文
   3.服務器響應Response報文
   4.四次揮手斷開TCP連接

  可以看出,TCP的面向連接的特性會大大的降低HTTP的整體的工作效率。於是在HTTP/1.1種開始默認使用長連接來執行HTTP事務,即:如果請求是來自於同一個客戶端地址,那麽在服務器響應之後沒有必要斷開此TCP連接,客戶端仍然可以利用這個TCP連接進行後續的資源請求。這樣一來看起來和諧多了。但另一個問題又隨之出現:服務器的資源和服務能力都是很寶貴的,因為大量的建立長連接而消耗了大量的系統資源(內存資源、CPU計算資源以及IO性能等),因此再強大的服務器也會有一個資源分配的上限。面對數量眾多的客戶端請求,該如何管理那些沒有請求發送的長連接呢?又應該如何控制不讓某個客戶端一直占用服務器的服務能力呢?最終,人們選擇了折中,即:每個長連接最多只能發送一定數量的請求,耗盡之後,服務器必須斷開此次連接;在一定時間內,如果該長連接沒有再次接收到任何的請求,服務器也會斷開此次連接。通過這樣的雙保險設置,可以比較高效的為客戶端進行響應,也可以在一定程度上避免資源的巨大浪費。

http協議的實現:
  簡單的基本http協議服務器,主要提供靜態頁面內容的瀏覽和訪問,一般包括以下幾種:
   httpd (apache)
   nginx
   lighttpd
  當然,除了這些之外還有很多是基於Nginx的二次開發版本,比如淘寶的TEngine等。

  動態服務器技術,一般是指安放在基本http服務器後端的應用程序服務器,包括:
   iis

   tomcat

   jetty

   resin

   weblogic

   websphere

   jboss

   glassfish等。

  接下來我們簡單的就httpd(apache)進行介紹,展開來討論其功能和工作原理。
  首先,我們來說一下httpd的性能特性:
   httpd 2.2版本的特性:
    高度模塊化:core + modules
    DSO: Dynamic shared objects,支持模塊的動態裝載和卸載;
    MPM:multipath processing modules,以靜態的方式裝載到核心,更換後必須重新啟動服務;
     prefork:多進程模型,一個主進程,多個子進程;一個進程響應一個請求;
      主進程:管理子進程;創建套接字;接收用戶請求,並派發給某子進程處理;...
      子進程:處理請求、響應請求;
     worker:多進程多線程模型;一個線程響應一個請求;
      主進程:管理子進程;創建套接字;接收用戶請求,並派發給某子進程處理;...
      子進程:負責管理線程;
      線程:處理並響應請求;
     event(在2.2版本中該模型尚處於測試階段):事件驅動模型,多進程模型,每個進程響應多個請求;
      主進程:管理子進程;創建套接字;接收用戶請求,並派發給某子進程處理;...
      子進程:處理並響應請求;
    CGI:common gateway interface;
    虛擬主機:IP, PORT, HOSTNAME
    反向代理
    負載均衡:bytraffic, bybusiness, byrequest
    路徑別名
    豐富的目錄和文件級別的訪問控制機制
     基於IP和FQDN的訪問控制
     基於用戶認證機制
      basic:明文驗證
      digest:摘要信息比對認證
    支持第三方模塊

  接下來,我們可以使用CentOS官方光盤中提供的安裝包進行httpd程序的安裝。先配置好yum源,然後執行下列命令即可:

$ yum -y install httpd

  註意:在安裝httpd之前,需要先安裝apr以及apr-util,這兩個包是httpd運行時環境。

  接下來我們看一看安裝了哪些httpd的相關軟件包,以及這些軟件包在我們的Linux中安裝了哪些文件?

[root@Centos68A ~]# rpm -qa httpd*
httpd-tools-2.2.15-53.el6.centos.x86_64
httpd-2.2.15-53.el6.centos.x86_64
httpd-devel-2.2.15-53.el6.centos.x86_64
[root@Centos68A ~]# rpm -ql httpd
/etc/httpd
/etc/httpd/conf
/etc/httpd/conf.d
/etc/httpd/conf.d/README
/etc/httpd/conf.d/welcome.conf
/etc/httpd/conf/httpd.conf:httpd默認的主配置文件
/etc/httpd/conf/magic
/etc/httpd/logs
/etc/httpd/modules
/etc/httpd/run
以上這些是服務器根目錄,conf和conf.d中保存了httpd所以的配置文件;logs、modules和run都是符號鏈接,其中logs鏈接到/var/log/httpd,用於存放默認主服務器的日誌文件;modules鏈接到/usr/lib64/httpd/modules,用於存放所有httpd所能裝載的DSO模塊文件;run鏈接到/var/run/httpd,用來存放主進程的PidFile。
/etc/logrotate.d/httpd:日誌滾動的腳本
/etc/rc.d/init.d/htcacheclean
/etc/rc.d/init.d/httpd:httpd服務的SysInit風格的啟動腳本
/etc/sysconfig/htcacheclean
/etc/sysconfig/httpd:httpd啟動腳本的配置文件,其中可以選擇MPM模塊。
/usr/lib64/httpd
/usr/lib64/httpd/modules:真正存放httpd可以裝載的DSO模塊文件的目錄
/usr/sbin/apachectl
/usr/sbin/htcacheclean
/usr/sbin/httpd
/usr/sbin/httpd.event
/usr/sbin/httpd.worker
/usr/sbin/httxt2dbm
/usr/sbin/rotatelogs
/usr/sbin/suexec
/var/cache/mod_proxy
/var/lib/dav
/var/log/httpd:真正存放主服務器日誌文件的目錄
/var/run/httpd:真正存放主服務器PidFile的目錄
/var/www
/var/www/cgi-bin:存放第三方可執行的CGI程序的目錄
/var/www/error:存放錯誤頁面的目錄
/var/www/html:默認的主服務器的文檔根目錄
/var/www/icons:存放了所有默認圖標文件的目錄
(省略了具體的模塊內容、幫助文檔和圖標文件等)
[root@Centos68A ~]# rpm -ql httpd-tools
/usr/bin/ab:httpd的壓力測試工具
/usr/bin/htdbm
/usr/bin/htdigest
/usr/bin/htpasswd:用於生成用戶文件和添加用戶信息到用戶文件的工具
/usr/bin/logresolve
(省略了幫助文檔)

  最後,我們來簡單的了解一下httpd的主配置文件的格式和常用的配置指令。
  httpd的主配置文件是/etc/httpd/conf/httpd.conf文件,其中有大量的註釋信息,所以可以用來進行服務功能配置和定義的內容,通常稱為指令(Directive)。原則上來講,Directive和Value都是大小寫不敏感的,但是如果Value是位於某個大小寫敏感的文件系統中的文件的路徑,則也就變得大小寫敏感了。每個指令的格式是:Directive Value。其中,Directive部分習慣上寫出駝峰的形式,即:每個單詞的首字母大小,其余字母為小寫。如:DocumentRoot、ServerRoot、DirectoryIndex等。

常用的配置指令:
  1、修改監聽的地址和端口;
   Listen [IP:]PORT :該指令可以在配置文件中多次定義,註意,如果省略IP,表示0.0.0.0/0;
   Listen 80
   Listen 172.16.109.224:80
   Listen 8080

  2、長連接
  tcp連接建立後,資源獲取完成不會斷開此TCP的連接,而是繼續等待請求其它資源,但並不是真正的永不斷開。在一個TCP連接發送來的請求數量達到某個限制或者空閑時間超出了時間限制,長連接也會被斷開,進程也有可能被清理。
  相關指令:
   KeepAlive On|Off  如果該值為OFF,則後面的兩個指令不再生效。
   MaxKeepAliveRequests 100
   KeepAliveTimeout 15
  默認情況下,KeepAlive指令的值為OFF,利用telnet進行請求測試結果如下:

[root@Centos68A ~]# telnet SERVER_IP PORT
GET /test1.html HTTP/1.1     
Host: SERVER_IP

HTTP/1.1 200 OK
Date: Tue, 12 Jul 2016 15:04:36 GMT
Server: Apache/2.2.15 (CentOS)
Last-Modified: Tue, 12 Jul 2016 09:34:43 GMT
ETag: "60102-10-53534af955806"
Accept-Ranges: bytes
Content-Length: 20
Connection: close
Content-Type: text/html; charset=UTF-8

This is a test page
Connection closed by foreign host.

  如果將KeepAlive指令的值修改為On,則不會立即出現最後一句提示,我們依然可以繼續進行資源請求。有興趣的小夥伴請自行嘗試。

  3、MPM
  多路處理模塊;
  httpd-2.2的MPM不支持DSO機制,均需以靜態的方式裝載至核心中,而且event為測試模塊,在主配置文件中並沒有為其提供配置指令。在安裝了httpd包之後,系統中在/usr/sbin目錄中提供了下述三個文件:
   httpd:prefork模塊
   httpd.worker: worker模塊
   httpd.event: event模塊,在2.2版本中為測試模塊,不建議使用
  如果想要修改MPM模式的模塊,可以修改下述文件中的特定行
  /etc/sysconfig/httpd
   HTTPD=/usr/sbin/httpd|httpd.worker|httpd.event
  修改之後,必須重啟服務才能讓配置生效。可以利用下列命令查看httpd程序的模塊列表:
   查看靜態編譯的模塊:
    httpd -l
   查看編譯的所有模塊:
    httpd -M

  在主配置文件中為prefork模塊提供的指令如下:

<IfModule prefork.c>
StartServers      8
MinSpareServers    5
MaxSpareServers     20
ServerLimit      256
MaxClients       256
MaxRequestsPerChild  4000
</IfModule>

  在主配置文件中為worker模塊提供的指令如下:

<IfModule worker.c>
StartServers       4
MaxClients        300
MinSpareThreads     25
MaxSpareThreads     75
ThreadsPerChild     25
MaxRequestsPerChild    0
</IfModule>

  4、DSO——LoadModule指令
   LoadModule  Mod_Name  modules/Module_File.so
  模塊的路徑為相對路徑,是相對於ServerRoot指令所定義的路徑而言,即:/etc/httpd目錄中的modules目錄

  5、'Main' Server
  定義一個主機的基本指令:
   ServerName  FQDN:PORT
   DocumentRoot /var/www/html

  6、站點資源訪問控制
  基於目錄和文件進行訪問控制
   <Directory "/PATH/TO/SOME_DIR">
   </Directory>

   <File "/PATH/TO/SOME_FILE">
   </File>

   <FileMatch "PATTERN">
   </FileMatch>

  基於url路徑進行訪問控制
   <Location "/PATH/TO/SOME_URL">
   </Location>

   <LocationMatch "URL_PATTERN">
   </LocationMatch>

  以上這些指令,因為必須成對出現,將所有的其他指令包含在其內部方能生效,我們便將這些指令形象的稱為"容器指令"。
  在Directory容器指令中的常用指令:
   1) Options:用於定義資源的展示方式;後跟以空白字符分隔的“選項”列表;
    Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews None All
    Indexes:允許索引;
    FollowSymLinks:允許跟蹤符號鏈接;
    SymLinksifOwnerMatch
    ExecCGI:允許執行CGI腳本;
    InCludes:服務器端包含
    None:全部都不選擇
    All:全部都有效

   2)AllowOverride
    httpd允許在網頁文檔的各目錄下使用.htaccess文件實現單目錄資源的訪問控制;表示哪些指令可以存放於.htaccess文件中。該指令的默認值為None,一旦將設置為其他的非"None"值,則每次檢索資源的時候,都要逐級向上查找.htaccess文件,因此會消耗大量的資源來做這樣的無用功,從而大大降低服務器的響應效率。因此這個選項非常不建議進行修改。

   3)order和allow/deny from
    基於IP地址的訪問控制;
    order用於定義allow和deny的生效次序;
    allow from IP/Network/FQDN
    deny from IP/NETWORK/FQDN
    來源請求遵循最佳匹配法則機制

  7、定義站點主頁面:
   DirectoryIndex file1 file2 ...

  8、定義路徑別名:
   Alias  /URL/  "/PATH/TO/SOME_DIR/"
   alias指定的URL右側的“/”相當於後面的路徑右側的“/”

  9、日誌設定
   錯誤日誌:
    ErrorLog logs/error_log
    LogLevel warn
   訪問日誌:
    LogFormat:定義日誌信息格式;

     LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined

    CustomLog:指明日誌文件路徑及日誌格式;

    格式:
     %h      遠程客戶端主機地址
     %l      遠程客戶端用戶登錄名
     %u      遠程客戶端用戶名
     %t      收到請求的時間,標準英文格式時間
     %r      請求報文的首行
     %s      狀態信息
     %b      響應報文中除了首部之外的報文大小,以字節為單位
     %{Foobar}i  在請求報文首部中屬性名為Foobar的對應的值

  10、httpd-manual

[root@Centos68A ~]# yum install httpd-manual

   配置文件:/etc/httpd/conf.d/manual.conf

[root@Centos68A ~]# service httpd reload

   訪問路徑:
    http://SERVER_IP/manual/

  11、基於用戶的訪問控制機制
   http協議的認證功能:
   虛擬賬號:僅用於訪問某服務的賬號和密碼;可將虛擬用戶賬戶存儲於文本文件、SQL數據庫或ldap目錄數據庫中,但httpd要有相應的適配模塊。

   認證質詢:WWW-Authenticate:響應碼為401,拒絕客戶端請求,並說明要求客戶端提供賬號和密碼;
   認證:Authorization:客戶端填入賬號和密碼後再次發送請求報文;認證通過後,服務端發送響應資源
   認證的方式有兩種:
    basic:明文
    digest:消息摘要認證

   安全域:需要用戶認證後方能訪問的資源集合;通常基於名稱對其進行標識;
   basic認證的配置示例:
    1) 定義安全域

<Directory "/PATH/TO/SOME_DIR">
Options  None
AllowOverride None
AuthType Basic
AuthName "SOME_STRING_HERE"
AutuUserFile "/PATH/TO/HT_PASSWD_FILE"
AuthGroupFile "/PATH/TO/HT_GROUP_FILE"
Require user user1 user2 ...
Require group group1 group2 ...
Require valid-user
此處三行Require指令在配置文件中,建議讓一行是生效的,這樣控制的更加精確,減少不確定因素。
</Directory>

  2) 創建賬號文件
   htpasswd [options]  /PATH/TO/HT_PASSWD_FILE  USERNAME
    -c:創建文件;
    -m:md5加密密碼;
    -s: SHA加密密碼;
    -D:刪除指定用戶

  3) 如果需要,還得在指定的組文件中定義組以及組中所包含的用戶賬戶,每行定義一個組,用戶賬戶之間使用空白字符隔開即可
   GROUP_NAME: username1 username2 ...

  12、虛擬主機
   虛擬主機與“主服務器”不能夠同時使用。默認情況下,虛擬主機的配置指令是註釋掉的,所以主服務器可以正常使用;一旦打開了虛擬主機的配置指令,主服務器默認自動失效。
   建立虛擬主機的目的是:可以將多個訪問量不大或者資源消耗較小的站點部署在一臺服務器上,進而可以節省成本,提供服務器資源的利用率。
   在構成虛擬主機標識的三個元素中:IP地址、端口號、FQDN(主機頭)至少有一個能唯一標識該虛擬主機即可。
   於是,常見的虛擬主機就有了下列三種實現方式:
    基於IP的虛擬主機:每個虛擬主機使用一獨有的IP地址;
    基於PORT的虛擬主機:每個虛擬主機使用一個獨有的PORT;
    基於FQDN的虛擬主機:每個虛擬主機使用一個獨有的FQDN;
                        
   虛擬主機的配置方法:
    <VirtualHost IP:PORT>
    ServerName
    DocumentRoot
    </VirtualHost>
                        
   其它常用指令:
    Errorlog
    CusomLog
    Alias
    ServerAlias
    ScriptsAlias
    <Directory "/PATH/TO/SOME_DIR">
    </Directory>
    ...
                            
  httpd2.2版的虛擬主機示例:

NameVirtualHost 172.16.100.71:80

<VirtualHost 172.16.100.71:80>
ServerName www1.myweb.com
DocumentRoot /myseb/vhosts/www1
</VirtualHost>

<VirtualHost 172.16.100.71:8080>
ServerName www1.myweb.com
DocumentRoot /myweb/vhosts/www2
</VirtualHost>

NameVirtualHost 172.16.100.72:80

<VirtualHost 172.16.100.72:80>
ServerName www2.myweb.com
DocumentRoot /myweb/vhosts/www2
</VirtualHost>

<VirtualHost 172.16.100.72:80>
ServerName www3.myweb.com
DocumentRoot /myweb/vhosts/www2
</VirtualHost>


本文出自 “運維者的家” 博客,請務必保留此出處http://zhaotianyu.blog.51cto.com/132212/1825825


Tags: Internet 物理學家 互聯網 計算機 尼爾森

文章來源:


ads
ads

相關文章
ads

相關文章

ad