0x14-套接字編程-HTTP服務(wù)器(2)
HTTP服務(wù)器的結(jié)構(gòu)
- HTTP服務(wù)器 本質(zhì)上就是一個(gè) TCP的接收端 程序
- 但凡一個(gè)正常的 TCP 接收端程序,都逃不過那幾個(gè)流程:
- 創(chuàng)建監(jiān)聽
socket -> 綁定端口,IP -> 監(jiān)聽socket -> 接受新連接 -> 處理讀寫... -> 關(guān)閉完成的連接
- 其中前三步比較固定,最多對(duì)這個(gè)監(jiān)聽用的
socket,進(jìn)行一些優(yōu)化處理,設(shè)置一些屬性之類的,但那都是固定模式,想想就能明白。硬要說重要的地方,也就是在于是否把socket設(shè)為非阻塞(non-blocking)了。
- 后面幾步,每個(gè)都是很重要的環(huán)節(jié),需要細(xì)細(xì)設(shè)計(jì)才行
所謂非阻塞,我還是不班門弄斧了,請(qǐng)移步 UNIX網(wǎng)絡(luò)編程-卷1-中文·第三版 127頁(yè)(英文版160頁(yè)) 的圖6-6,清楚的對(duì)比了,阻塞,非阻塞,異步,I/O復(fù)用的區(qū)別和含義。十分建議寫網(wǎng)絡(luò)程序之前,去把這本書的某些章節(jié)大致過一遍。
- 對(duì)于這章節(jié)需要寫的這個(gè)服務(wù)器而言,采用的是經(jīng)典且流行的 I/O復(fù)用+非阻塞套接字(socket)+多線程(線程池) 結(jié)構(gòu)。
- 吶,又出現(xiàn)一個(gè)新的知識(shí)點(diǎn),I/O復(fù)用,這是什么鬼。
I/O復(fù)用
- 我給一個(gè)不太嚴(yán)密的解釋,那就是 將你這個(gè)程序需要等待的地方,集中起來
- 打個(gè)比方:
- 假設(shè)有100個(gè)新連接,被你的監(jiān)聽套接字給成功接受(
accept())了
- 這時(shí)候并不是所有新連接都立刻有數(shù)據(jù)可以讀,那此時(shí)你有兩種選擇:阻塞,非阻塞。但不論是哪一種都會(huì)導(dǎo)致同一個(gè)結(jié)果
- 阻塞: 那假設(shè)你只有一個(gè)線程處理這100個(gè)連接,萬一要是正好處理到這個(gè)暫時(shí)沒有數(shù)據(jù)的連接,就要一直等待它的數(shù)據(jù)到來,后面的幾十個(gè)連接都要閑著;假設(shè)有多個(gè)線程同時(shí)處理,理由還是一樣,換湯不換藥,而且難道你還能開100個(gè)線程去處理嗎?那如果更多的連接呢?
- 非阻塞: 比阻塞看起來稍微好一些,因?yàn)槿绻麤]有數(shù)據(jù)到來的話,那就直接跳過這個(gè)連接,直接去處理下一個(gè)連接了,但是你想想,這不就是遍歷了嗎?萬一連接量一大,假設(shè)上萬,而且只有少數(shù)的幾個(gè)連接有數(shù)據(jù)活躍,這無用功做的是不是太多了?多開幾個(gè)線程去平攤壓力?那么要開多少比較合適?
- 這時(shí)候喜歡偷懶的程序員,自然就不愿意了,于是考慮是否可以有一個(gè),讓我們可以在單個(gè)線程的情況下還能夠只處理那些活躍的連接?
- 這時(shí)候出現(xiàn)了所謂的 I/O復(fù)用 技術(shù),說是技術(shù),因?yàn)樗褂玫倪€是同步型的操作(
read, write),只不過套接字設(shè)為非阻塞的了。
- Linux平臺(tái)下的
epoll, Unix(包括Mac)平臺(tái)下的kqueue, Windows平臺(tái)下的IOCP,各平臺(tái)通用的select, poll,還有幾個(gè)歷史實(shí)現(xiàn)就不贅述了。
- 最后這兩個(gè)
select, poll在活躍連接明顯少于總連接數(shù)的情況下,性能比前三個(gè)要差許多,故本章使用的是epoll,(當(dāng)然還有資料比較多的原因啦
-
說說 epoll 的工作
- 首先它幫我們管理著所有的套接字,用來監(jiān)聽這些套接字哪些有了數(shù)據(jù),就返回誰。
- 將所有等待,阻塞都集中在了一個(gè)地方,那就是
epoll_wait()調(diào)用上
- 而且可以針對(duì)不同的事件進(jìn)行不同的監(jiān)聽,這就是事件驅(qū)動(dòng)這種模式的由來
-
事件驅(qū)動(dòng)
- 簡(jiǎn)單來說,就是針對(duì)某種事件進(jìn)行觸發(fā)的一種編程模式
- 具體來說,假設(shè)你在網(wǎng)絡(luò)編程,正在處理一個(gè)套接字,由于TCP是全雙工的,意味著這個(gè)TCP套接字是可讀可寫,問題來了,什么時(shí)候可讀,什么時(shí)候可寫呢?這就延伸出了事件,讀事件,寫事件,錯(cuò)誤事件等
- 可以通過
epoll_clt()來設(shè)置要監(jiān)聽的事件,當(dāng)然也可以同時(shí)監(jiān)聽多個(gè)事件,看你的設(shè)計(jì)了。
- 具體的
epoll接口的詳細(xì)介紹,可以直接在Linux上,使用man epoll進(jìn)行查看手冊(cè),這是基本功。
epoll_create, epoll_ctl, epoll_wait
服務(wù)器結(jié)構(gòu)
- 繼續(xù)回到服務(wù)器結(jié)構(gòu)
- 上面簡(jiǎn)單的講述了一下什么是 I/O復(fù)用,以及將會(huì)用到的具體實(shí)現(xiàn)
epoll。那具體說一下,整個(gè)程序的流程
- 還是老規(guī)矩,寫程序之前要先構(gòu)思,自己在紙上畫一畫,大概的流程是什么
- 問題: 想要完整處理一個(gè)HTTP請(qǐng)求,需要哪些步驟?
- 解析HTTP請(qǐng)求報(bào)文
- 創(chuàng)建HTTP回復(fù)報(bào)文
- 邏輯就這么簡(jiǎn)單啊,但是加上細(xì)節(jié)部分,就會(huì)稍微麻煩一些了:
- 完整地 從套接字中,讀取 HTTP請(qǐng)求報(bào)文
- 解析 HTTP請(qǐng)求報(bào)文,并判斷其有效性
- 生成 HTTP回復(fù)報(bào)文
- 完整地 通過對(duì)應(yīng)套接字,發(fā)送給請(qǐng)求者。
- 在這里我假設(shè),你已經(jīng)對(duì)TCP編程的模型很熟悉了,不熟的可以去頂部看看再回來
- 并發(fā)服務(wù)器的關(guān)鍵點(diǎn)就在于
- 高效且正確地接收盡可能多的連接
- 高效且正確地處理盡可能多的連接
- 以上忽略了安全性
- 該如何設(shè)計(jì)?
- 讓某個(gè)
epoll用來服務(wù)于接收新連接這個(gè)環(huán)節(jié)(accept)
- 讓某些
epoll用來處理這些新連接的事務(wù)。
- 這樣理論上我們既發(fā)揮了單核的極限(epoll),又用上了多核的優(yōu)勢(shì)(多個(gè)
epoll)
- 更具體的呢?
- 在主線程里使用單個(gè)
epoll來處理,監(jiān)聽套接字的讀事件,也就是接受新連接
- 再開幾個(gè)線程
epoll,用來平分處理這些新連接。
- 這樣也就是網(wǎng)絡(luò)編程的一整個(gè)流程,如果看到這里你已經(jīng)大概有了一個(gè)程序思路,實(shí)際上就已經(jīng)達(dá)到目的了,接下來就是直接上手代碼就行
- 還是迷迷糊糊的,就一步一步跟著我,寫出這個(gè)服務(wù)器,會(huì)大有脾益。
小經(jīng)驗(yàn),在編程中,讀往往比寫要復(fù)雜許多。在網(wǎng)絡(luò)編程里面亦是。
-
有圖有真相,希望能夠自己畫。

- 現(xiàn)在大致有了思路,可以整理整理自己接下來該干什么了
環(huán)境準(zhǔn)備
- 99%的中國(guó)大學(xué)學(xué)生的操作系統(tǒng),應(yīng)該都是 Windows或者M(jìn)ax OS(maxOS),那么建議你直接使用虛擬機(jī)進(jìn)行環(huán)境的搭建,可以選擇開源免費(fèi)的
Visual Box,Windows下也可以使用商業(yè)版的VMware,Mac下有一個(gè)更棒的商業(yè)版選擇Paralelle Desktop,但是這都是軟件,算是無關(guān)緊要的。
- 選擇一個(gè)
Linux發(fā)行版,由于我用的是 Debian系列的Ubuntu 16.04 LTS,所以我也推薦這個(gè)發(fā)行版,其他的發(fā)行版也許略有差異,不再多說。
- 裝好之后,直接進(jìn)入開發(fā)階段吧。
- IDE可以選擇
Clion或者Kdevelop。
- 當(dāng)然你要用
Vim我也不會(huì)阻攔,但是請(qǐng)裝好兩個(gè)插件Nerdtree和YouCompleteMe,配合好另一個(gè)軟件tmux(簡(jiǎn)單使用),不然你會(huì)想死。
- 除了
Vim,你也可以選擇 Visual Studio Code加裝一個(gè)C/C++ tools也是不錯(cuò)的。
- 作為時(shí)尚的我,自然選擇
Clion了,簡(jiǎn)單明了,且還是使用CMake作為構(gòu)建工具。
- 想要進(jìn)行這么底層的網(wǎng)絡(luò)編程,請(qǐng)準(zhǔn)備好
Google和Unix網(wǎng)絡(luò)編程卷1,如果你兩個(gè)都沒有的話,不說了,再見。建議準(zhǔn)備一個(gè)那玩意兒去訪問Google。