Java創(chuàng)建非阻塞的HTTP服務(wù)器的實(shí)現(xiàn)
HTTP(Hypertext Transfer Protocol,超級(jí)文本傳輸協(xié)議)是網(wǎng)絡(luò)應(yīng)用層的協(xié)議,建立在TCP/IP基礎(chǔ)上。HTTP使用可靠的TCP連接,默認(rèn)端口是80端口。HTTP的第1個(gè)版本是HTTP/0.9,后來發(fā)展到了HTTP/1,現(xiàn)在最新的版本是HTTP/2。值得注意的是,在目前的實(shí)際運(yùn)用中,HTTP/2并沒有完全取代HTTP/1,而是這兩種協(xié)議在網(wǎng)絡(luò)上并存,也就是說,許多Web服務(wù)器和瀏覽器之間既可以通過HTTP/1通信,也可以通過HTTP/2通信。
HTTP/1.1對(duì)應(yīng)的RFC文檔為RFC2616,它對(duì)HTTP/1.1做了詳細(xì)的闡述。HTTP/2對(duì)應(yīng)的RFC文檔為RFC7540,它對(duì)HTTP/2協(xié)議做了詳細(xì)的闡述。
HTTP基于客戶/服務(wù)器模式,客戶端主動(dòng)發(fā)出HTTP請(qǐng)求,服務(wù)器接收HTTP請(qǐng)求,返回HTTP響應(yīng)結(jié)果。HTTP對(duì)HTTP請(qǐng)求以及響應(yīng)的格式做了明確的規(guī)定。
1、Htpp簡(jiǎn)介
當(dāng)用戶打開瀏覽器,輸入一個(gè)URL地址,就能接收到遠(yuǎn)程HTTP服務(wù)器發(fā)送過來的網(wǎng)頁。瀏覽器就是最常見的HTTP客戶程序。如下圖所示,HTTP客戶程序必須先發(fā)出一個(gè)HTTP請(qǐng)求,然后才能接收到來自HTTP服務(wù)器的響應(yīng)。
HTTP客戶程序和HTTP服務(wù)器分別由不同的軟件開發(fā)商提供,它們都可以用任意的編程語言編寫。用VC編寫的HTTP客戶程序能否與用Java編寫的HTTP服務(wù)器順利通信呢?答案是肯定的。HTTP嚴(yán)格規(guī)定了HTTP請(qǐng)求和HTTP響應(yīng)的數(shù)據(jù)格式,只要HTTP服務(wù)器與客戶程序都遵守HTTP,就能彼此看得懂對(duì)方發(fā)送的消息。
1.1、HTTP請(qǐng)求格式
HTTP規(guī)定,HTTP請(qǐng)求由3部分構(gòu)成,分別是:
- 請(qǐng)求方法、URI、HTTP的版本
- 請(qǐng)求頭(Request Header)
- 請(qǐng)求正文(Request Content)
下面是一個(gè)HTTP請(qǐng)求的例子:
1.請(qǐng)求方式、URI、HTTP的版本
HTTP請(qǐng)求的第1行包括請(qǐng)求方式、URI和協(xié)議版本這3項(xiàng)內(nèi)容,以空格分開:
在以上代碼中,“POST” 表示請(qǐng)求方式,“/hello”表示URI,“HTTP/1.1”表示HTTP的版本。
根據(jù)HTTP,HTTP請(qǐng)求可以使用多種請(qǐng)求方式,主要包括以下幾種:
- GET:這種請(qǐng)求方式最為常見,客戶程序通過這種請(qǐng)求方式訪問服務(wù)器上的一個(gè)文檔,服務(wù)器把文檔發(fā)送給客戶程序。
- POST:客戶程序可通過這種方式發(fā)送大量信息給服務(wù)器。在HTTP請(qǐng)求中除了包含要訪問的文檔的URI,還包括大量的請(qǐng)求正文,這些請(qǐng)求正文中通常會(huì)包含大量HTML表單數(shù)據(jù)。
- HEAD:客戶程序和服務(wù)器之間交流一些內(nèi)部數(shù)據(jù),服務(wù)器不會(huì)返回具體的文檔。當(dāng)使用GET和POST方法時(shí),服務(wù)器最后都將特定的文檔返回給客戶程序。而HEAD請(qǐng)求方式則不同,它僅僅交流一些內(nèi)部數(shù)據(jù),這些數(shù)據(jù)不會(huì)影響用戶瀏覽網(wǎng)頁的過程,可以說對(duì)用戶是透明的。HEAD請(qǐng)求方式通常不單獨(dú)使用,而是為其他請(qǐng)求方式起輔助作用。一些搜索引擎使用HEAD請(qǐng)求方式來獲得網(wǎng)頁的標(biāo)志信息,還有一些HTTP服務(wù)器在進(jìn)行安全認(rèn)證時(shí),用這個(gè)方式來傳遞認(rèn)證信息。
- PUT:客戶程序通過這種方式把文檔上傳給服務(wù)器。
- DELETE:客戶程序通過這種方式來刪除遠(yuǎn)程服務(wù)器上的某個(gè)文檔??蛻舫绦蚩梢岳肞UT和DELETE請(qǐng)求方式來管理遠(yuǎn)程服務(wù)器上的文檔。
GET和POST請(qǐng)求方式最常用,而PUT和DELETE請(qǐng)求方式并不常用,因而不少HTTP服務(wù)器并不支持PUT和DELETE請(qǐng)求方式。
統(tǒng)一資源定位符(Universal Resource Identifier,URI)用于標(biāo)識(shí)要訪問的網(wǎng)絡(luò)資源。在HTTP請(qǐng)求中,通常只要給出相對(duì)于服務(wù)器的根目錄的相對(duì)目錄即可,因此以“/”開頭。
HTTP請(qǐng)求的第1行的最后一部分內(nèi)容為客戶程序使用的HTTP的版本。
2.請(qǐng)求頭(Request Header)
請(qǐng)求頭包含許多有關(guān)客戶端環(huán)境和請(qǐng)求正文的有用信息。例如,請(qǐng)求頭可以聲明瀏覽器的類型、所用的語言、請(qǐng)求正文的類型,以及請(qǐng)求正文的長(zhǎng)度等。
3.請(qǐng)求正文(Request Content)
HTTP規(guī)定,請(qǐng)求頭和請(qǐng)求正文之間必須以空行分割(即只有CRLF符號(hào)的行),這個(gè)空行非常重要,它表示請(qǐng)求頭已經(jīng)結(jié)束,接下來是請(qǐng)求正文。請(qǐng)求正文中可以包含客戶以POST方式提交的表單數(shù)據(jù)。
在以上HTTP請(qǐng)求例子中,請(qǐng)求正文只有一行內(nèi)容。在實(shí)際應(yīng)用中,HTTP請(qǐng)求的正文可以包含更多的內(nèi)容。
下面是一個(gè)簡(jiǎn)單的HTTP客戶程序,它發(fā)送的HTTP請(qǐng)求信息就嚴(yán)格遵守上述規(guī)范。
1.2、HTTP響應(yīng)格式
和HTTP請(qǐng)求相似,HTTP響應(yīng)也由3部分構(gòu)成,分別是:
- HTTP的版本、狀態(tài)代碼、描述
- 響應(yīng)頭(Response Header)
- 響應(yīng)正文(Response Content)
下面是一個(gè)HTTP響應(yīng)的例子:
1.HTTP的版本、狀態(tài)代碼、描述
HTTP響應(yīng)的第1行包括服務(wù)器使用的HTTP的版本、狀態(tài)代碼,以及對(duì)狀態(tài)代碼的描述,這3項(xiàng)內(nèi)容之間以空格分割。在本例中,使用HTTP1.1,狀態(tài)代碼為200,該狀態(tài)代碼表示服務(wù)器已經(jīng)成功地處理了客戶端發(fā)出的請(qǐng)求。
狀態(tài)代碼是一個(gè)3位整數(shù),以1、2、3、4或5開頭:
- 1xx:信息提示,表示臨時(shí)的響應(yīng)。
- 2xx:響應(yīng)成功,表明服務(wù)器成功的接收了客戶端請(qǐng)求。
- 3xx:重定向。
- 4xx:客戶端錯(cuò)誤,表明客戶端可能有問題。
- 5xx:服務(wù)器錯(cuò)誤,表明服務(wù)器由于遇到某種錯(cuò)誤而不能響應(yīng)客戶請(qǐng)求。
以下是一些常見的狀態(tài)代碼:
- 200:響應(yīng)成功。
- 400:錯(cuò)誤的請(qǐng)求??蛻舭l(fā)送的HTTP請(qǐng)求不正確。
- 404:文件不存在。在服務(wù)器上沒有客戶要求訪問的文檔。
- 405:服務(wù)器不支持客戶的請(qǐng)求方式。
- 500:服務(wù)器內(nèi)部錯(cuò)誤。
2.響應(yīng)頭(Response Header)
響應(yīng)頭也和請(qǐng)求頭一樣包含許多有用的信息,例如服務(wù)器類型、正文類型和正文長(zhǎng)度等。
3.響應(yīng)正文(Response Content)
響應(yīng)正文就是服務(wù)器返回的具體的文檔,最常見的是HTML網(wǎng)頁。
HTTP請(qǐng)求頭與請(qǐng)求正文之間必須用空行分割,同樣,HTTP響應(yīng)頭與響應(yīng)正文之間也必須用空行分隔。
2、創(chuàng)建非阻塞的HTTP服務(wù)器
HTTP服務(wù)器的主要任務(wù)就是接收HTTP請(qǐng)求,然后發(fā)送HTTP響應(yīng)。下圖是我們要介紹的非阻塞的HTTP服務(wù)器范例的模型。
在這個(gè)對(duì)象模型中,HttpServer類是服務(wù)器主程序,由它啟動(dòng)服務(wù)器。AcceptHandler負(fù)責(zé)接收客戶連接,RequestHandler負(fù)責(zé)接收客戶的HTTP請(qǐng)求,對(duì)其解析,然后生成相應(yīng)的HTTP響應(yīng),再把它發(fā)送給客戶。Request類表示HTTP請(qǐng)求,Response類表示HTTP響應(yīng),Content類表示HTTP響應(yīng)的正文。
2.1、服務(wù)器主程序:HttpServer類
HttpServer類是服務(wù)器的主程序,僅啟用了單個(gè)主線程,采用非阻塞模式來接收客戶連接,以及收發(fā)數(shù)據(jù)。下面是HttpServer類的源程序。
在HttpServer類的service()方法中,當(dāng)ServerSocketChannel向Selector注冊(cè)接收連接就緒事件時(shí),設(shè)置了一個(gè)AcceptHandler附件:
AcceptHandler類的handle()方法負(fù)責(zé)處理接收連接就緒事件。當(dāng)某種事件發(fā)生時(shí),HttpServer類的service()方法從SelectionKey中獲得Handler附件,然后調(diào)用它的handle()方法:
2.2、具有自動(dòng)增長(zhǎng)的緩沖區(qū)的ChannelIO類
自定義的ChannelIO類對(duì)SocketChannel進(jìn)行了包裝,增加了自動(dòng)增長(zhǎng)緩沖區(qū)容量的功能。當(dāng)調(diào)用socketChannel.read(ByteBuffer buffer)方法時(shí),如果buffer已滿(即position=limit),那么即使通道中還有未接收的數(shù)據(jù),read方法也不會(huì)讀取任何數(shù)據(jù),而是直接返回0,表示讀到了零字節(jié)。
為了能讀取通道中的所有數(shù)據(jù),必須保證緩沖區(qū)的容量足夠大。在ChannelIO類中,有一個(gè)requestBuffer變量,它用來存放客戶的HTTP請(qǐng)求數(shù)據(jù),當(dāng)requestBuffer剩余容量已經(jīng)不足5%,并且還有HTTP請(qǐng)求數(shù)據(jù)未接收時(shí),ChannelIO會(huì)自動(dòng)擴(kuò)充requestBuffer的容量,該功能由resizeRequestBuffer()方法完成。
下面是ChannelIO類的源程序,它的read()方法和write()方法利用SocketChannel來接收和發(fā)送數(shù)據(jù),并且它還提供了實(shí)用方法transferTo(),該方法能把文件中的數(shù)據(jù)發(fā)送到SocketChannel中。
2.3、負(fù)責(zé)處理各種事件的Handler接口
Handler接口負(fù)責(zé)處理各種事件,它的定義如下:
Handler接口有AcceptHandler和RequestHandler兩個(gè)實(shí)現(xiàn)類。AcceptHandler負(fù)責(zé)處理接收連接就緒事件,RequestHandler負(fù)責(zé)處理讀就緒和寫就緒事件。更確切地說,RequestHandler負(fù)責(zé)接收客戶的HTTP請(qǐng)求,以及發(fā)送HTTP響應(yīng)。
2.4、負(fù)責(zé)處理接收連接就緒事件的AcceptHandler類
AcceptHandler負(fù)責(zé)處理接收連接就緒事件。它獲得與客戶連接的SocketChannel,然后向Selector注冊(cè)讀就緒事件,并且創(chuàng)建了一個(gè)RequestHandler,把它作為SelectionKey的附件。當(dāng)讀就緒事件發(fā)生時(shí),將由這個(gè)RequestHandler來處理該事件。
在以上AcceptHandler的handle()方法中,還創(chuàng)建了一個(gè)ChannelIO,RequestHandler與它關(guān)聯(lián)。RequestHandler會(huì)利用ChannelIO來接收和發(fā)送數(shù)據(jù)。
2.5、負(fù)責(zé)接收HTTP請(qǐng)求和發(fā)送HTTP響應(yīng)的RequestHandler類
RequestHandler先通過ChannelIO來接收HTTP請(qǐng)求,當(dāng)接收到了HTTP請(qǐng)求的所有數(shù)據(jù)后,就對(duì)HTTP請(qǐng)求數(shù)據(jù)進(jìn)行解析,創(chuàng)建相應(yīng)的Request對(duì)象,然后依據(jù)客戶的請(qǐng)求內(nèi)容,創(chuàng)建相應(yīng)的Response對(duì)象,最后發(fā)送Response對(duì)象中包含的HTTP響應(yīng)數(shù)據(jù)。為了簡(jiǎn)化程序,RequestHandler僅僅支持GET和HEAD這兩種請(qǐng)求方式。
2.6、代表HTTP請(qǐng)求的Request類
RequestHandler通過ChannelIO讀取HTTP請(qǐng)求數(shù)據(jù)時(shí),這些數(shù)據(jù)被放在requestByteBuffer中。當(dāng)HTTP請(qǐng)求的所有數(shù)據(jù)接收完畢,就要對(duì)requestByteBuffer中的數(shù)據(jù)進(jìn)行解析,然后創(chuàng)建相應(yīng)的Request對(duì)象。Request對(duì)象就表示特定的HTTP請(qǐng)求。
本范例僅支持GET和HEAD請(qǐng)求方式,在這兩種方式下,HTTP請(qǐng)求沒有正文部分,并且以“\r\n\r\n”結(jié)尾。Request類有3個(gè)成員變量:action、uri和version,它們分別表示HTTP請(qǐng)求中的請(qǐng)求方式、URI和HTTP的版本。下面是Request類的源程序。
2.7、代表HTTP響應(yīng)的Response類
Response類表示HTTP響應(yīng)。它有3個(gè)成員變量:code、headerBuffer和content,它們分別表示HTTP響應(yīng)中的狀態(tài)代碼、響應(yīng)頭和正文。Response類的prepare()方法負(fù)責(zé)準(zhǔn)備HTTP響應(yīng)的響應(yīng)頭和正文內(nèi)容,send()方法負(fù)責(zé)發(fā)送HTTP響應(yīng)的所有數(shù)據(jù)。下面是Response類的源程序:
2.8、代表響應(yīng)正文的Content接口及其實(shí)現(xiàn)類
Response類有一個(gè)成員變量content,表示響應(yīng)正文,它被定義為Content類型。
Content接口表示響應(yīng)正文,它的定義如下:
Content接口繼承了Sendable接口,Sendable接口表示服務(wù)器端可發(fā)送給客戶的內(nèi)容,它的定義如下:
Content接口有StringContent和FileContent兩個(gè)實(shí)現(xiàn)類。StringContent表示字符串形式的正文,F(xiàn)ileContent表示文件形式的正文。例如在RequestHandler類的build()方法中,如果HTTP請(qǐng)求方式不是GET和HEAD,就創(chuàng)建一個(gè)包含StringContent的Response對(duì)象,否則就創(chuàng)建一個(gè)包含F(xiàn)ileContent的Response對(duì)象。
下面主要介紹FileContent類的實(shí)現(xiàn)。FileContent類有一個(gè)成員變量fileChannel,它表示讀文件的通道。FileContent類的send()方法把fileChannel中的數(shù)據(jù)發(fā)送到ChannelIO的SocketChannel中,如果文件中的所有數(shù)據(jù)發(fā)送完畢,send()方法就返回false。下面是FileContent類的源程序。
2.9、運(yùn)行HTTP服務(wù)器
運(yùn)行命令“java HttpServer”,就啟動(dòng)了HTTP服務(wù)器。在本范例的root目錄下存放了各種供瀏覽器訪問的文檔,比如login.htm、hello.htm和data.rar文件等。打開IE瀏覽器,輸入U(xiǎn)RL:http://localhost/login.htm或者h(yuǎn)ttp://localhost/data.rar,就能接收到服務(wù)器發(fā)送過來的相應(yīng)文檔。如果瀏覽器按照POST方式訪問hello.htm,服務(wù)器就會(huì)返回HTTP405錯(cuò)誤,因?yàn)楸痉?wù)器不支持POST方式。
3、總結(jié)
HTTP是目前使用非常廣泛的應(yīng)用層協(xié)議,它規(guī)定了在網(wǎng)絡(luò)上傳輸文檔(主要是HTML格式的網(wǎng)頁)的規(guī)則。HTTP的客戶程序主要是瀏覽器。瀏覽器訪問一個(gè)遠(yuǎn)程HTTP服務(wù)器上的網(wǎng)頁的步驟如下:
- (1)建立與遠(yuǎn)程服務(wù)器的連接。
- (2)發(fā)送HTTP請(qǐng)求。
- (3)接收HTTP響應(yīng),斷開與遠(yuǎn)程服務(wù)器的連接。
- (4)展示HTTP響應(yīng)中的網(wǎng)頁內(nèi)容。
HTTP服務(wù)器必須接收HTTP請(qǐng)求,對(duì)它進(jìn)行解析,然后返回相應(yīng)的HTTP響應(yīng)結(jié)果。本章創(chuàng)建了一個(gè)非阻塞的HTTP服務(wù)器,它首先讀取HTTP請(qǐng)求,把它們存放在字節(jié)緩沖區(qū)內(nèi),當(dāng)緩沖區(qū)的容量不夠時(shí),會(huì)擴(kuò)充它的容量,以保證容納HTTP請(qǐng)求的所有數(shù)據(jù)。接著,程序把字節(jié)緩沖區(qū)內(nèi)的字節(jié)轉(zhuǎn)換為字符串,對(duì)其進(jìn)行解析,獲得HTTP請(qǐng)求中的請(qǐng)求方式、URI和協(xié)議版本等信息,然后創(chuàng)建相應(yīng)的HTTP響應(yīng),把它發(fā)送給客戶程序。
到此這篇關(guān)于Java創(chuàng)建非阻塞的HTTP服務(wù)器的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Java 非阻塞HTTP服務(wù)器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- java編寫Http服務(wù)器下載工具
- Java使用NioSocket手動(dòng)實(shí)現(xiàn)HTTP服務(wù)器
- java Socket實(shí)現(xiàn)簡(jiǎn)單模擬HTTP服務(wù)器
- Java/Android 實(shí)現(xiàn)簡(jiǎn)單的HTTP服務(wù)器
- Java 如何實(shí)現(xiàn)一個(gè)http服務(wù)器
- Java模擬實(shí)現(xiàn)HTTP服務(wù)器項(xiàng)目實(shí)戰(zhàn)
- Intellij?IDEA?的maven項(xiàng)目通過Java代碼實(shí)現(xiàn)Jetty的Http服務(wù)器(推薦)
相關(guān)文章
java如何自定義List中的sort()排序,用于日期排序
這篇文章主要介紹了java如何自定義List中的sort()排序,用于日期排序,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11Java實(shí)現(xiàn)解析.xlsb文件的示例代碼
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)解析.xlsb文件的相關(guān)方法,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的可以了解一下2023-01-01詳解基于Spring Cloud幾行配置完成單點(diǎn)登錄開發(fā)
這篇文章主要介紹了詳解基于Spring Cloud幾行配置完成單點(diǎn)登錄開發(fā),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-02-02Java?超詳細(xì)講解數(shù)據(jù)結(jié)構(gòu)中的堆的應(yīng)用
堆首先是一個(gè)完全二叉樹,堆分為小根堆和大根堆。小根堆,所有結(jié)點(diǎn)的左右子節(jié)點(diǎn)都不小于根節(jié)點(diǎn);大根堆,所有結(jié)點(diǎn)的左右子節(jié)點(diǎn)都不大于根節(jié)點(diǎn)。優(yōu)先級(jí)隊(duì)列(priorityQueue)底層就是一個(gè)小根堆2022-04-04Spring Boot 參數(shù)校驗(yàn)的具體實(shí)現(xiàn)方式
這篇文章主要介紹了Spring Boot 參數(shù)校驗(yàn)的具體實(shí)現(xiàn)方式,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-06-06Java中BigInteger類的使用方法詳解(全網(wǎng)最新)
這篇文章主要介紹了Java中BigInteger類的使用方法詳解,常用最全系列,本章作為筆記使用,內(nèi)容比較全面,但常用的只有:構(gòu)造函數(shù),基本運(yùn)算以及compareTo(),intValue(),setBit(),testBit()方法,需要的朋友可以參考下2023-05-05ssm框架controller層返回json格式數(shù)據(jù)到頁面的實(shí)現(xiàn)
這篇文章主要介紹了ssm框架controller層返回json格式數(shù)據(jù)到頁面的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09常用Maven庫,鏡像庫及maven/gradle配置(小結(jié))
這篇文章主要介紹了常用Maven庫,鏡像庫及maven/gradle配置(小結(jié)),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12