1、引言
本文接上篇《腦殘式網絡編程入門(一):跟著動畫來學TCP三次握手和四次揮手》,繼續腦殘式的網絡編程知識學習 ^_^。
套接字socket是大多數程序員都非常熟悉的概念,它是計算機網絡編程的基礎,TCP/UDP收發消息都靠它。我們熟悉的web服務器底層依賴它,我們用到的MySQL關系數據庫、Redis內存數據庫底層依賴它。我們用微信和別人聊天也依賴它,我們玩網絡游戲時依賴它,讀者們能夠閱讀這篇文章也是因為有它在背后默默地支持著網絡通信。
本篇文章依然嘗試使用動畫圖片的方式,來對這個知識點進行“腦殘式”講解(哈哈),期望讀者們可以更加簡單、直觀地理解Socket通信的數據讀寫本質。
友情提示:如果您的網速較慢,加載gif動畫可能較慢,請耐心等候哦。
學習交流:
- 即時通訊開發交流3群:185926912[推薦]
- 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM》
(本文同步發布于:http://www.52im.net/thread-1732-1-1.html)
2、關于作者
錢文品(老錢):畢業于華中科技大學計算機科學與技術專業,互聯網分布式高并發技術十年老兵,目前任掌閱科技資深后端工程師。熟練使用 Java、Python、Golang 等多種計算機語言,開發過游戲,制作過網站,寫過消息推送系統和MySQL 中間件,實現過開源的 ORM 框架、Web 框架、RPC 框架等。
作者的Github: https://github.com/pyloque
3、系列文章
本文是系列文章中的第2篇,本系列大綱如下:
《腦殘式網絡編程入門(一):跟著動畫來學TCP三次握手和四次揮手》
《腦殘式網絡編程入門(二):我們在讀寫Socket時,究竟在讀寫什么?》(本文)
4、Socket讀寫的簡單過程理解
當客戶端和服務器使用TCP協議進行通信時,客戶端封裝一個請求對象req,將請求對象req序列化成字節數組,然后通過套接字socket將字節數組發送到服務器,服務器通過套接字socket讀取到字節數組,再反序列化成請求對象req,進行處理,處理完畢后,生成一個響應對應res,將響應對象res序列化成字節數組,然后通過套接字將自己數組發送給客戶端,客戶端通過套接字socket讀取到自己數組,再反序列化成響應對象。
通信框架往往可以將序列化的過程隱藏起來,我們所看到的現象就是上圖所示,請求對象req和響應對象res在客戶端和服務器之間跑來跑去。
也許你覺得這個過程還是挺簡單的,很好理解,但是實際上背后發生的一系列事件超出了你們中大多數人的想象。通信的真實過程要比上面的這張圖復雜太多。你也許會問,我們需要了解的那么深入么,直接拿來用不就可以了么?
在互聯網技術服務行業工作多年的經驗告訴我,如果你對底層機制不了解,你就會不明白為什么對套接字socket的讀寫會出現各種奇奇乖乖的問題,為什么有時會阻塞,有時又不阻塞,有時候還報錯,為什么會有粘包半包問題,NIO具體又是什么,它是什么特別新鮮的技術么?對于這些問題的理解都需要你了解底層機制。
5、Socket讀寫的細節過程分析
為了方便大家對通信底層的理解,我花了些時間做了下面這個動畫,它并不能完全覆蓋底層細節的全貌,但是對于理解套接字的工作機制已經足夠了。請讀者仔細觀察這個動畫,后面的講解將圍繞著這個動畫展開。
我們平時用到的套接字其實只是一個引用(一個對象ID),這個套接字對象實際上是放在操作系統內核中。這個套接字對象內部有兩個重要的緩沖結構,一個是讀緩沖(read buffer),一個是寫緩沖(write buffer),它們都是有限大小的數組結構。
當我們對客戶端的socket寫入字節數組時(序列化后的請求消息對象req),是將字節數組拷貝到內核區套接字對象的write buffer中,內核網絡模塊會有單獨的線程負責不停地將write buffer的數據拷貝到網卡硬件,網卡硬件再將數據送到網線,經過一些列路由器交換機,最終送達服務器的網卡硬件中。
同樣,服務器內核的網絡模塊也會有單獨的線程不停地將收到的數據拷貝到套接字的read buffer中等待用戶層來讀取。最終服務器的用戶進程通過socket引用的read方法將read buffer中的數據拷貝到用戶程序內存中進行反序列化成請求對象進行處理。然后服務器將處理后的響應對象走一個相反的流程發送給客戶端,這里就不再具體描述。
5.1 細節過程:阻塞
我們注意到write buffer空間都是有限的,所以如果應用程序往套接字里寫的太快,這個空間是會滿的。一旦滿了,寫操作就會阻塞,直到這個空間有足夠的位置騰出來。不過有了NIO(非阻塞IO),寫操作也可以不阻塞,能寫多少是多少,通過返回值來確定到底寫進去多少,那些沒有寫進去的內容用戶程序會緩存起來,后續會繼續重試寫入。
同樣我們也注意到read buffer的內容可能會是空的。這樣套接字的讀操作(一般是讀一個定長的字節數組)也會阻塞,直到read buffer中有了足夠的內容(填充滿字節數組)才會返回。有了NIO,就可以有多少讀多少,無須阻塞了。讀不夠的,后續會繼續嘗試讀取。
5.2 細節過程:ack
那上面這張圖就展現了套接字的全部過程么?顯然不是,數據的確認過程(ack)就完全沒有展現。比如當寫緩沖的內容拷貝到網卡后,是不會立即從寫緩沖中將這些拷貝的內容移除的,而要等待對方的ack過來之后才會移除。如果網絡狀況不好,ack遲遲不過來,寫緩沖很快就會滿的。
5.3 細節過程:包頭
細心的同學可能注意到圖中的消息req被拷貝到網卡的時候變成了大寫的REQ,這是為什么呢?因為這兩個東西已經不是完全一樣的了。內核的網絡模塊會將緩沖區的消息進行分塊傳輸,如果緩沖區的內容太大,是會被拆分成多個獨立的小消息包的。并且還要在每個消息包上附加上一些額外的頭信息,比如源網卡地址和目標網卡地址、消息的序號等信息,到了接收端需要對這些消息包進行重新排序組裝去頭后才會扔進讀緩沖中。這些復雜的細節過程就非常難以在動畫上予以呈現了。
5.4 細節過程:速率
還有個問題那就是如果讀緩沖滿了怎么辦,網卡收到了對方的消息要怎么處理?一般的做法就是丟棄掉不給對方ack,對方如果發現ack遲遲沒有來,就會重發消息。那緩沖為什么會滿?是因為消息接收方處理的慢而發送方生產的消息太快了,這時候tcp協議就會有個動態窗口調整算法來限制發送方的發送速率,使得收發效率趨于匹配。如果是udp協議的話,消息一丟那就徹底丟了。
網絡協議內部實現還有更多復雜的細節有待繼續挖掘,留著以后繼續分析吧。
附錄1:同類文章精選
如果您覺得本系列文章過于基礎,您可直接閱讀以下系列:
《網絡編程懶人入門(一):快速理解網絡通信協議(上篇)》
《網絡編程懶人入門(二):快速理解網絡通信協議(下篇)》
《網絡編程懶人入門(三):快速理解TCP協議一篇就夠》
《網絡編程懶人入門(四):快速理解TCP和UDP的差異》
《網絡編程懶人入門(五):快速理解為什么說UDP有時比TCP更有優勢》
《網絡編程懶人入門(六):史上最通俗的集線器、交換機、路由器功能原理入門》
《網絡編程懶人入門(七):深入淺出,全面理解HTTP協議》
《不為人知的網絡編程》系列文章為高階必讀,該系列目錄如下:
《不為人知的網絡編程(一):淺析TCP協議中的疑難雜癥(上篇)》
《不為人知的網絡編程(二):淺析TCP協議中的疑難雜癥(下篇)》
《不為人知的網絡編程(三):關閉TCP連接時為什么會TIME_WAIT、CLOSE_WAIT》
《不為人知的網絡編程(四):深入研究分析TCP的異常關閉》
《不為人知的網絡編程(五):UDP的連接性和負載均衡》
《不為人知的網絡編程(六):深入地理解UDP協議并用好它》
關于移動端網絡特性及優化手段的總結性文章請見:
《現代移動端網絡短連接的優化手段總結:請求速度、弱網適應、安全保障》
《移動端IM開發者必讀(一):通俗易懂,理解移動網絡的“弱”和“慢”》
《移動端IM開發者必讀(二):史上最全移動弱網絡優化方法總結》
附錄2:參考資料
《TCP/IP詳解 - 第11章·UDP:用戶數據報協議》
《TCP/IP詳解 - 第17章·TCP:傳輸控制協議》
《TCP/IP詳解 - 第18章·TCP連接的建立與終止》
《TCP/IP詳解 - 第21章·TCP的超時與重傳》
《通俗易懂-深入理解TCP協議(上):理論基礎》
《通俗易懂-深入理解TCP協議(下):RTT、滑動窗口、擁塞處理》
《理論經典:TCP協議的3次握手與4次揮手過程詳解》
《理論聯系實際:Wireshark抓包分析TCP 3次握手、4次揮手過程》
《計算機網絡通訊協議關系圖(中文珍藏版)》
《高性能網絡編程(一):單臺服務器并發TCP連接數到底可以有多少》
《高性能網絡編程(二):上一個10年,著名的C10K并發連接問題》
《高性能網絡編程(三):下一個10年,是時候考慮C10M并發問題了》
《高性能網絡編程(四):從C10K到C10M高性能網絡應用的理論探索》
《簡述傳輸層協議TCP和UDP的區別》
《為什么QQ用的是UDP協議而不是TCP協議?》
《移動端即時通訊協議選擇:UDP還是TCP?》
《技術往事:改變世界的TCP/IP協議(珍貴多圖、手機慎點)》
《UDP中一個包的大小最大能多大?》
《Java新一代網絡編程模型AIO原理及Linux系統AIO介紹》
《NIO框架入門(一):服務端基于Netty4的UDP雙向通信Demo演示》
《NIO框架入門(二):服務端基于MINA2的UDP雙向通信Demo演示》
《NIO框架入門(三):iOS與MINA2、Netty4的跨平臺UDP雙向通信實戰》
《NIO框架入門(四):Android與MINA2、Netty4的跨平臺UDP雙向通信實戰》
《P2P技術詳解(一):NAT詳解——詳細原理、P2P簡介》
《P2P技術詳解(二):P2P中的NAT穿越(打洞)方案詳解》
《P2P技術詳解(三):P2P技術之STUN、TURN、ICE詳解》
《通俗易懂:快速理解P2P技術中的NAT穿透原理》
>> 更多同類文章 ……
(本文同步發布于:http://www.52im.net/thread-1732-1-1.html)