Jack Jiang

          我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
          posts - 499, comments - 13, trackbacks - 0, articles - 1

          1、引言

          本文接上篇《腦殘式網(wǎng)絡(luò)編程入門(一):跟著動畫來學(xué)TCP三次握手和四次揮手》,繼續(xù)腦殘式的網(wǎng)絡(luò)編程知識學(xué)習(xí) ^_^。

          套接字socket是大多數(shù)程序員都非常熟悉的概念,它是計算機(jī)網(wǎng)絡(luò)編程的基礎(chǔ),TCP/UDP收發(fā)消息都靠它。我們熟悉的web服務(wù)器底層依賴它,我們用到的MySQL關(guān)系數(shù)據(jù)庫、Redis內(nèi)存數(shù)據(jù)庫底層依賴它。我們用微信和別人聊天也依賴它,我們玩網(wǎng)絡(luò)游戲時依賴它,讀者們能夠閱讀這篇文章也是因為有它在背后默默地支持著網(wǎng)絡(luò)通信。

          本篇文章依然嘗試使用動畫圖片的方式,來對這個知識點進(jìn)行“腦殘式”講解(哈哈),期望讀者們可以更加簡單、直觀地理解Socket通信的數(shù)據(jù)讀寫本質(zhì)。

          友情提示:如果您的網(wǎng)速較慢,加載gif動畫可能較慢,請耐心等候哦。

          學(xué)習(xí)交流:

          - 即時通訊開發(fā)交流3群:185926912[推薦]

          - 移動端IM開發(fā)入門文章:《新手入門一篇就夠:從零開發(fā)移動端IM

          (本文同步發(fā)布于:http://www.52im.net/thread-1732-1-1.html

          2、關(guān)于作者

          錢文品(老錢):畢業(yè)于華中科技大學(xué)計算機(jī)科學(xué)與技術(shù)專業(yè),互聯(lián)網(wǎng)分布式高并發(fā)技術(shù)十年老兵,目前任掌閱科技資深后端工程師。熟練使用 Java、Python、Golang 等多種計算機(jī)語言,開發(fā)過游戲,制作過網(wǎng)站,寫過消息推送系統(tǒng)和MySQL 中間件,實現(xiàn)過開源的 ORM 框架、Web 框架、RPC 框架等。

          作者的Github: https://github.com/pyloque

          3、系列文章

          本文是系列文章中的第2篇,本系列大綱如下:

          腦殘式網(wǎng)絡(luò)編程入門(一):跟著動畫來學(xué)TCP三次握手和四次揮手

          腦殘式網(wǎng)絡(luò)編程入門(二):我們在讀寫Socket時,究竟在讀寫什么?》(本文)

          4、Socket讀寫的簡單過程理解

          當(dāng)客戶端和服務(wù)器使用TCP協(xié)議進(jìn)行通信時,客戶端封裝一個請求對象req,將請求對象req序列化成字節(jié)數(shù)組,然后通過套接字socket將字節(jié)數(shù)組發(fā)送到服務(wù)器,服務(wù)器通過套接字socket讀取到字節(jié)數(shù)組,再反序列化成請求對象req,進(jìn)行處理,處理完畢后,生成一個響應(yīng)對應(yīng)res,將響應(yīng)對象res序列化成字節(jié)數(shù)組,然后通過套接字將自己數(shù)組發(fā)送給客戶端,客戶端通過套接字socket讀取到自己數(shù)組,再反序列化成響應(yīng)對象。

          通信框架往往可以將序列化的過程隱藏起來,我們所看到的現(xiàn)象就是上圖所示,請求對象req和響應(yīng)對象res在客戶端和服務(wù)器之間跑來跑去。

          也許你覺得這個過程還是挺簡單的,很好理解,但是實際上背后發(fā)生的一系列事件超出了你們中大多數(shù)人的想象。通信的真實過程要比上面的這張圖復(fù)雜太多。你也許會問,我們需要了解的那么深入么,直接拿來用不就可以了么?

          在互聯(lián)網(wǎng)技術(shù)服務(wù)行業(yè)工作多年的經(jīng)驗告訴我,如果你對底層機(jī)制不了解,你就會不明白為什么對套接字socket的讀寫會出現(xiàn)各種奇奇乖乖的問題,為什么有時會阻塞,有時又不阻塞,有時候還報錯,為什么會有粘包半包問題,NIO具體又是什么,它是什么特別新鮮的技術(shù)么?對于這些問題的理解都需要你了解底層機(jī)制。

          5、Socket讀寫的細(xì)節(jié)過程分析

          為了方便大家對通信底層的理解,我花了些時間做了下面這個動畫,它并不能完全覆蓋底層細(xì)節(jié)的全貌,但是對于理解套接字的工作機(jī)制已經(jīng)足夠了。請讀者仔細(xì)觀察這個動畫,后面的講解將圍繞著這個動畫展開。

          我們平時用到的套接字其實只是一個引用(一個對象ID),這個套接字對象實際上是放在操作系統(tǒng)內(nèi)核中。這個套接字對象內(nèi)部有兩個重要的緩沖結(jié)構(gòu),一個是讀緩沖(read buffer),一個是寫緩沖(write buffer),它們都是有限大小的數(shù)組結(jié)構(gòu)。

          當(dāng)我們對客戶端的socket寫入字節(jié)數(shù)組時(序列化后的請求消息對象req),是將字節(jié)數(shù)組拷貝到內(nèi)核區(qū)套接字對象的write buffer中,內(nèi)核網(wǎng)絡(luò)模塊會有單獨(dú)的線程負(fù)責(zé)不停地將write buffer的數(shù)據(jù)拷貝到網(wǎng)卡硬件,網(wǎng)卡硬件再將數(shù)據(jù)送到網(wǎng)線,經(jīng)過一些列路由器交換機(jī),最終送達(dá)服務(wù)器的網(wǎng)卡硬件中。

          同樣,服務(wù)器內(nèi)核的網(wǎng)絡(luò)模塊也會有單獨(dú)的線程不停地將收到的數(shù)據(jù)拷貝到套接字的read buffer中等待用戶層來讀取。最終服務(wù)器的用戶進(jìn)程通過socket引用的read方法將read buffer中的數(shù)據(jù)拷貝到用戶程序內(nèi)存中進(jìn)行反序列化成請求對象進(jìn)行處理。然后服務(wù)器將處理后的響應(yīng)對象走一個相反的流程發(fā)送給客戶端,這里就不再具體描述。

          5.1 細(xì)節(jié)過程:阻塞

          我們注意到write buffer空間都是有限的,所以如果應(yīng)用程序往套接字里寫的太快,這個空間是會滿的。一旦滿了,寫操作就會阻塞,直到這個空間有足夠的位置騰出來。不過有了NIO(非阻塞IO),寫操作也可以不阻塞,能寫多少是多少,通過返回值來確定到底寫進(jìn)去多少,那些沒有寫進(jìn)去的內(nèi)容用戶程序會緩存起來,后續(xù)會繼續(xù)重試寫入。

          同樣我們也注意到read buffer的內(nèi)容可能會是空的。這樣套接字的讀操作(一般是讀一個定長的字節(jié)數(shù)組)也會阻塞,直到read buffer中有了足夠的內(nèi)容(填充滿字節(jié)數(shù)組)才會返回。有了NIO,就可以有多少讀多少,無須阻塞了。讀不夠的,后續(xù)會繼續(xù)嘗試讀取。

          5.2 細(xì)節(jié)過程:ack

          那上面這張圖就展現(xiàn)了套接字的全部過程么?顯然不是,數(shù)據(jù)的確認(rèn)過程(ack)就完全沒有展現(xiàn)。比如當(dāng)寫緩沖的內(nèi)容拷貝到網(wǎng)卡后,是不會立即從寫緩沖中將這些拷貝的內(nèi)容移除的,而要等待對方的ack過來之后才會移除。如果網(wǎng)絡(luò)狀況不好,ack遲遲不過來,寫緩沖很快就會滿的。

          5.3 細(xì)節(jié)過程:包頭

          細(xì)心的同學(xué)可能注意到圖中的消息req被拷貝到網(wǎng)卡的時候變成了大寫的REQ,這是為什么呢?因為這兩個東西已經(jīng)不是完全一樣的了。內(nèi)核的網(wǎng)絡(luò)模塊會將緩沖區(qū)的消息進(jìn)行分塊傳輸,如果緩沖區(qū)的內(nèi)容太大,是會被拆分成多個獨(dú)立的小消息包的。并且還要在每個消息包上附加上一些額外的頭信息,比如源網(wǎng)卡地址和目標(biāo)網(wǎng)卡地址、消息的序號等信息,到了接收端需要對這些消息包進(jìn)行重新排序組裝去頭后才會扔進(jìn)讀緩沖中。這些復(fù)雜的細(xì)節(jié)過程就非常難以在動畫上予以呈現(xiàn)了。

          5.4 細(xì)節(jié)過程:速率

          還有個問題那就是如果讀緩沖滿了怎么辦,網(wǎng)卡收到了對方的消息要怎么處理?一般的做法就是丟棄掉不給對方ack,對方如果發(fā)現(xiàn)ack遲遲沒有來,就會重發(fā)消息。那緩沖為什么會滿?是因為消息接收方處理的慢而發(fā)送方生產(chǎn)的消息太快了,這時候tcp協(xié)議就會有個動態(tài)窗口調(diào)整算法來限制發(fā)送方的發(fā)送速率,使得收發(fā)效率趨于匹配。如果是udp協(xié)議的話,消息一丟那就徹底丟了。

          網(wǎng)絡(luò)協(xié)議內(nèi)部實現(xiàn)還有更多復(fù)雜的細(xì)節(jié)有待繼續(xù)挖掘,留著以后繼續(xù)分析吧。

          附錄1:同類文章精選

          如果您覺得本系列文章過于基礎(chǔ),您可直接閱讀以下系列:

          網(wǎng)絡(luò)編程懶人入門(一):快速理解網(wǎng)絡(luò)通信協(xié)議(上篇)

          網(wǎng)絡(luò)編程懶人入門(二):快速理解網(wǎng)絡(luò)通信協(xié)議(下篇)

          網(wǎng)絡(luò)編程懶人入門(三):快速理解TCP協(xié)議一篇就夠

          網(wǎng)絡(luò)編程懶人入門(四):快速理解TCP和UDP的差異

          網(wǎng)絡(luò)編程懶人入門(五):快速理解為什么說UDP有時比TCP更有優(yōu)勢

          網(wǎng)絡(luò)編程懶人入門(六):史上最通俗的集線器、交換機(jī)、路由器功能原理入門

          網(wǎng)絡(luò)編程懶人入門(七):深入淺出,全面理解HTTP協(xié)議

          《不為人知的網(wǎng)絡(luò)編程》系列文章為高階必讀,該系列目錄如下:

          不為人知的網(wǎng)絡(luò)編程(一):淺析TCP協(xié)議中的疑難雜癥(上篇)

          不為人知的網(wǎng)絡(luò)編程(二):淺析TCP協(xié)議中的疑難雜癥(下篇)

          不為人知的網(wǎng)絡(luò)編程(三):關(guān)閉TCP連接時為什么會TIME_WAIT、CLOSE_WAIT

          不為人知的網(wǎng)絡(luò)編程(四):深入研究分析TCP的異常關(guān)閉

          不為人知的網(wǎng)絡(luò)編程(五):UDP的連接性和負(fù)載均衡

          不為人知的網(wǎng)絡(luò)編程(六):深入地理解UDP協(xié)議并用好它

          關(guān)于移動端網(wǎng)絡(luò)特性及優(yōu)化手段的總結(jié)性文章請見:

          現(xiàn)代移動端網(wǎng)絡(luò)短連接的優(yōu)化手段總結(jié):請求速度、弱網(wǎng)適應(yīng)、安全保障

          移動端IM開發(fā)者必讀(一):通俗易懂,理解移動網(wǎng)絡(luò)的“弱”和“慢”

          移動端IM開發(fā)者必讀(二):史上最全移動弱網(wǎng)絡(luò)優(yōu)化方法總結(jié)

          附錄2:參考資料

          TCP/IP詳解 - 第11章·UDP:用戶數(shù)據(jù)報協(xié)議

          TCP/IP詳解 - 第17章·TCP:傳輸控制協(xié)議

          TCP/IP詳解 - 第18章·TCP連接的建立與終止

          TCP/IP詳解 - 第21章·TCP的超時與重傳

          通俗易懂-深入理解TCP協(xié)議(上):理論基礎(chǔ)

          通俗易懂-深入理解TCP協(xié)議(下):RTT、滑動窗口、擁塞處理

          理論經(jīng)典:TCP協(xié)議的3次握手與4次揮手過程詳解

          理論聯(lián)系實際:Wireshark抓包分析TCP 3次握手、4次揮手過程

          計算機(jī)網(wǎng)絡(luò)通訊協(xié)議關(guān)系圖(中文珍藏版)

          高性能網(wǎng)絡(luò)編程(一):單臺服務(wù)器并發(fā)TCP連接數(shù)到底可以有多少

          高性能網(wǎng)絡(luò)編程(二):上一個10年,著名的C10K并發(fā)連接問題

          高性能網(wǎng)絡(luò)編程(三):下一個10年,是時候考慮C10M并發(fā)問題了

          高性能網(wǎng)絡(luò)編程(四):從C10K到C10M高性能網(wǎng)絡(luò)應(yīng)用的理論探索

          簡述傳輸層協(xié)議TCP和UDP的區(qū)別

          為什么QQ用的是UDP協(xié)議而不是TCP協(xié)議?

          移動端即時通訊協(xié)議選擇:UDP還是TCP?

          技術(shù)往事:改變世界的TCP/IP協(xié)議(珍貴多圖、手機(jī)慎點)

          UDP中一個包的大小最大能多大?

          Java新一代網(wǎng)絡(luò)編程模型AIO原理及Linux系統(tǒng)AIO介紹

          NIO框架入門(一):服務(wù)端基于Netty4的UDP雙向通信Demo演示

          NIO框架入門(二):服務(wù)端基于MINA2的UDP雙向通信Demo演示

          NIO框架入門(三):iOS與MINA2、Netty4的跨平臺UDP雙向通信實戰(zhàn)

          NIO框架入門(四):Android與MINA2、Netty4的跨平臺UDP雙向通信實戰(zhàn)

          P2P技術(shù)詳解(一):NAT詳解——詳細(xì)原理、P2P簡介

          P2P技術(shù)詳解(二):P2P中的NAT穿越(打洞)方案詳解

          P2P技術(shù)詳解(三):P2P技術(shù)之STUN、TURN、ICE詳解

          通俗易懂:快速理解P2P技術(shù)中的NAT穿透原理

          >> 更多同類文章 ……

          (本文同步發(fā)布于:http://www.52im.net/thread-1732-1-1.html



          作者:Jack Jiang (點擊作者姓名進(jìn)入Github)
          出處:http://www.52im.net/space-uid-1.html
          交流:歡迎加入即時通訊開發(fā)交流群 215891622
          討論:http://www.52im.net/
          Jack Jiang同時是【原創(chuàng)Java Swing外觀工程BeautyEye】【輕量級移動端即時通訊框架MobileIMSDK】的作者,可前往下載交流。
          本博文 歡迎轉(zhuǎn)載,轉(zhuǎn)載請注明出處(也可前往 我的52im.net 找到我)。


          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          Jack Jiang的 Mail: jb2011@163.com, 聯(lián)系QQ: 413980957, 微信: hellojackjiang
          主站蜘蛛池模板: 澎湖县| 泾源县| 克什克腾旗| 合阳县| 东至县| 民丰县| 平邑县| 方山县| 潮州市| 闻喜县| 高安市| 佛冈县| 万源市| 虞城县| 永善县| 德保县| 高青县| 新余市| 延吉市| 连江县| 祁门县| 兴化市| 江达县| 河池市| 张家港市| 黄大仙区| 青神县| 长葛市| 抚顺市| 绿春县| 灌阳县| 南开区| 武陟县| 疏勒县| 锡林浩特市| 收藏| 聂拉木县| 太仓市| 同江市| 乐平市| 汤阴县|