摘要:文件描述符是一個整數索引值,是這個數組的下標。基于協議的基于的編程過程和有些不同。通過名字可以看出,這是在父進程的基礎上完全拷貝一個子進程。因此父進程剛才因為創建的已連接也是一個文件描述符,同樣也會被子進程獲得。
系列文章傳送門:
網絡協議 1 - 概述
網絡協議 2 - IP 是怎么來,又是怎么沒的?
網絡協議 3 - 從物理層到 MAC 層
網絡協議 4 - 交換機與 VLAN:辦公室太復雜,我要回學校
網絡協議 5 - ICMP 與 ping:投石問路的偵察兵
網絡協議 6 - 路由協議:敢問路在何方?
網絡協議 7 - UDP 協議:性善碰到城會玩
網絡協議 8 - TCP 協議(上):性惡就要套路深
網絡協議 9 - TCP協議(下):聰明反被聰明誤
????前面一直在說各種協議,偏理論方面的知識,這次咱們就來認識下基于 TCP 和 UDP 協議這些理論知識的 Socket 編程。
????說 TCP 和 UDP 的時候,我們是分成客戶端和服務端來認識的,那在寫 Socket 的時候,我們也這樣分。
????Socket 這個名字很有意思,可以作插口或者插槽講。我們寫程序時,就可以將 Socket 想象為,一頭插在客戶端,一頭插在服務端,然后進行通信。
????在建立 Socket 的時候,應該設置什么參數呢?Socket 編程進行的是端到端的通信,往往意識不到中間經過多少局域網,多少路由器,因而能夠設置的參數,也只能是端到端協議之上網絡層和傳輸層的。
????對于網絡層和傳輸層,有以下參數需要設置:
IP協議:IPv4 對應 AF_INEF,IPv6 對應 AF_INET6;
傳輸層協議:TCP 與 UDP。TCP 協議基于數據流,其對應值是 SOCKET_STREAM,而 UDP 是基于數據報的,其對應值是 SOCKET_DGRAM。
????兩端創建了 Socket 之后,而后面的過程中,TCP 和 UDP 稍有不同,我們先來看看 TCP。
基于 TCP 協議的 Socket????對于 TCP 創建 Socket 的過程,有以下幾步走:
????1)TCP 調用 bind 函數賦予 Socket IP 地址和端口。
????為什么需要 IP 地址?還記得嗎?咱們之前了解過,一臺機器會有多個網卡,而每個網卡就有一個 IP 地址,我們可以選擇監聽所有的網卡,也可以選擇監聽一個網卡,只有,發給指定網卡的包才會發給你。
????為什么需要端口?要知道,咱們寫的是一個應用程序,當一個網絡包來的時候,內核就是要通過 TCP 里面的端口號來找到對應的應用程序,把包給你。
????2)調用 listen 函數監聽端口。 在 TCP 的狀態圖了,有一個 listen 狀態,當調用這個函數之后,服務端就進入了這個狀態,這個時候客戶端就可以發起連接了。
????在內核中,為每個 Socket 維護兩個隊列。一個是已經建立了連接的隊列,這里面的連接已經完成三次握手,處于 established 狀態;另一個是還沒有完全建立連接的隊列,這里面的連接還沒有完成三次握手,處于 syn_rcvd 狀態。
????3)服務端調用 accept 函數。 這時候服務端會拿出一個已經完成的連接進行處理,如果還沒有已經完成的連接,就要等著。
????在服務端等待的時候,客戶端可以通過 connect 函數發起連接。客戶端先在參數中指明要連接的 IP 地址和端口號,然后開始發起三次握手。內核會給客戶端分配一個臨時的端口,一旦握手成功,服務端的 accept 就會返回另一個 Socket。
????注意,從上面的過程中可以看出,監聽的 Socket 和真正用來傳數據的 Socket 是不同的兩個。 一個叫做監聽 Socket,一個叫做已連接 Socket。
????下圖就是基于 TCP 協議的 Socket 函數調用過程:
????連接建立成功之后,雙方開始通過 read 和write 函數來讀寫數據,就像往一個文件流里寫東西一樣。
????這里說 TCP 的 Socket 是一個文件流,是非常準確的。因為 Socket 在 linux 中就是以文件的形式存在的。除此之外,還存在文件描述符。寫入和讀出,也是通過文件描述符。
????每一個進程都有一個數據結構 task_struct,里面指向一個文件描述符數組,來列出這個進程打開的所有文件的文件描述符。文件描述符是一個整數索引值,是這個數組的下標。
????這個數組中的內容是一個指針,指向內核中所有打開的文件列表。而每個文件也會有一個 inode(索引節點)。
????對于 Socke 而言,它是一個文件,也就有對于的文件描述符。與真正的文件系統不一樣的是,Socket 對于的 inode 并不是保存在硬盤上,而是在內存中。在這個 inode 中,指向了 Socket 在內核中的 Socket 結構。
????在這個機構里面,主要有兩個隊列。一個發送隊列,一個接收隊列。這兩個隊列里面,保存的是一個緩存 sk_buff。這個緩存里能夠看到完整的包結構。說到這里,你應該就會發現,數據結構以及和前面了解的收發包的場景聯系起來了。
????上面整個過程說起來稍顯混亂,可對比下圖加深理解。
基于 UDP 協議的 Socket????基于 UDP 的 Socket 編程過程和 TCP 有些不同。UDP 是沒有連接狀態的,所以不需要三次握手,也就不需要調用 listen 和 connect。沒有連接狀態,也就不需要維護連接狀態,因而不需要對每個連接建立一組 Socket,只要建立一組 Socket,就能和多個客戶端通信。也正是因為沒有連接狀態,每次通信的時候,都可以調用 sendto 和 recvfrom 傳入 IP 地址和端口。
????下圖是基于 UDP 的 Socket 函數調用過程:
服務器最大并發量????了解了基本的 Socket 函數后,就可以寫出一個網絡交互的程序了。就像上面的過程一樣,在建立連接后,進行一個 while 循環,客戶端發了收,服務端收了發。
????很明顯,這種一臺服務器服務一個客戶的方式和我們的實際需要相差甚遠。這就相當于老板成立了一個公司,只有自己一個人,自己親自服務客戶,只能干完一家再干下一家。這種方式肯定賺不了錢,這時候,就要想,我最多能接多少項目呢?
????我們可以先來算下理論最大值,也就是理論最大連接數。系統會用一個四元組來標識一個 TCP 連接:
{本機 IP,本機端口,對端 IP,對端端口}
????服務器通常固定監聽某個本地端口,等待客戶端連接請求。因此,上面四元組中,可變的項只有對端 IP 和對端端口,也就是客戶端 IP 和客戶端端口。不難得出:
最大 TCP 連接數 = 客戶端 IP 數 x 客戶端端口數。
????對于 IPv4:
客戶端最大 IP 數 = 2 的 32 次方
????對于端口數:
客戶端最大端口數 = 2 的 16 次方
????因此:
最大 TCP 連接數 = 2 的 48 次方(估算值)
????當然,服務端最大并發 TCP 連接數遠不能達到理論最大值。主要有以下原因:
文件描述符限制。按照上面的原理,Socket 都是文件,所以首先要通過 ulimit 配置文件描述符的數目;
內存限制。按上面的數據結構,每個 TCP 連接都要占用一定的內存,而系統內存是有限的。
????所以,作為老板,在資源有限的情況下,要想接更多的項目,賺更多的錢,就要降低每個項目消耗的資源數目。
????本著這個原則,我們可以找到以下幾種方式來最可能的降低消耗項目消耗資源。
1)將項目外包給其他公司(多進程方式)????這就相當于你是一個代理,監聽來的請求,一旦建立一個連接,就會有一個已連接的 Socket,這時候你可以創建一個紫禁城,然后將基于已連接的 Socket 交互交給這個新的子進程來做。就像來了一個新項目,你可以注冊一家子公司,招人,然后把項目轉包給這就公司做,這樣你就又可以去接新的項目了。
????這里有個問題是,如何創建子公司,并將項目移交給子公司?
????在 Linux 下,創建子進程使用 fork 函數。通過名字可以看出,這是在父進程的基礎上完全拷貝一個子進程。在 Linux 內核中,會復制文件描述符的列表,也會復制內存空間,還會復制一條記錄當前執行到了哪一行程序的進程。
????這樣,復制完成后,父進程和子進程都會記錄當前剛剛執行完 fork。這兩個進程剛復制完的時候,幾乎一模一樣,只是根據 fork 的返回值來區分是父進程還是子進程。如果返回值是 0,則是子進程,如果返回值是其他的整數,就是父進程,這里返回的整數,就是子進程的 ID。
????進程復制過程如下圖:
????因為復制了文件描述符列表,而文件描述符都是指向整個內核統一的打開文件列表的。因此父進程剛才因為 accept 創建的已連接 Socket 也是一個文件描述符,同樣也會被子進程獲得。
????接下來,子進程就可以通過這個已連接 Socket 和客戶端進行通信了。當通信完成后,就可以退出進程。那父進程如何知道子進程干完了項目要退出呢?父進程中 fork 函數返回的整數就是子進程的 ID,父進程可以通過這個 ID 查看子進程是否完成項目,是否需要退出。
2)將項目轉包給獨立的項目組(多線程方式)????上面這種方式你應該能發現問題,如果每接一個項目,都申請一個新公司,然后干完了,就注銷掉,實在是太麻煩了。而且新公司要有新公司的資產、辦公家具,每次都買了再賣,不劃算。
????這時候,我們應該已經想到了線程。相比于進程來講,線程更加輕量級。如果創建進程相當于成立新公司,而創建線程,就相當于在同一個公司成立新的項目組。一個項目做完了,就解散項目組,成立新的項目組,辦公家具還可以共用。
????在 Linux 下,通過 pthread_create 創建一個線程,也是調用 do_fork。不同的是,雖然新的線程在 task 列表會新創建一項,但是很多資源,例如文件描述符列表、進程空間,這些還是共享的,只不過多了一個引用而已。
????下圖是線程復制過程:
????新的線程也可以通過已連接 Socket 處理請求,從而達到并發處理的目的。
????上面兩種方式,無論是基于進程還是線程模型的,其實還是有問題的。新到來一個 TCP 連接,就需要分配一個進程或者線程。一臺機器能創建的進程和線程數是有限的,并不能很好的發揮服務器的性能。著名的C10K問題,就是說一臺機器如何維護 1 萬了連接。按我們上面的方式,系統就要創建 1 萬個進程或者線程,這是操作系統無法承受的。
????那既然一個線程負責一個 TCP 連接不行,能不能一個進程或線程負責多個 TCP 連接呢?這就引出了下面兩種方式。
3)一個項目組支撐多個項目(IO 多路復用,一個線程維護多個 Socket)????當一個項目組負責多個項目時,就要有個項目進度墻來把控每個項目的進度,除此之外,還得有個人專門盯著進度墻。
????上面說過,Socket 是文件描述符,因此某個線程盯的所有的 Socket,都放在一個文件描述符集合 fd_set 中,這就是項目進度墻。然后調用 select 函數來監聽文件描述符集合是否有變化,一旦有變化,就會依次查看每個文件描述符。那些發生變化的文件描述符在 fd_set 對應的位都設為 1,表示 Socket 可讀或者可寫,從而可以進行讀寫操作,然后再調用 select,接著盯著下一輪的變化。
4)一個項目組支撐多個項目(IO 多路復用,從“派人盯著”到“有事通知”)????上面 select 函數還是有問題的,因為每次 Socket 所在的文件描述符集合中有發生變化的時候,都需要通過輪詢的方式將所有的 Socket 查看一遍,這大大影響了一個進程或者線程能夠支撐的最大連接數量。使用 select,能夠同時監聽的數量由 FD_SETSIZE 限制。
????如果改成事件通知的方式,情況就會好很多。項目組不需要通過輪詢挨個盯著所有項目,而是當項目進度發生變化的時候,主動通知項目組,然后項目組再根據項目進展情況做相應的操作。
????而 epoll 函數就能完成事件通知。它在內核中的實現不是通過輪詢的方式,而是通過注冊 callback 函數的方式,當某個文件描述符發生變化的時候,主動通知。
????如上圖所示,假設進程打開了 Socket m、n、x 等多個文件描述符,現在需要通過 epoll 來監聽這些 Socket 是否有事件發生。其中 epoll_create 創建一個 epoll 對象,也是一個文件,對應一個文件描述符,同樣也對應著打開文件列表中的一項。在這項里面有一個紅黑樹,在紅黑樹里,要保存這個 epoll 監聽的所有的 Socket。
????當 epoll_ctl 添加一個 Scoket 的時候,其實就是加入這個紅黑樹中。同時,紅黑樹里面的節點指向一個結構,將這個結構掛在被監聽的 Socket 的事件列表中。當一個 Socket 發生某個事件時,可以從這個列表中得到 epoll 對象,并調用 call_back 通知它。
????這種事件通知的方式使得監聽的 Socket 數量增加的同時,效率也不會大幅度降低。因此,能夠同時監聽的 Socket 的數量就非常的多了。上限為系統定義的,進程打開的最大文件描述符個數。因而,epoll 被稱為解決 C10K 問題的利器。
小結牢記基于 TCP 和 UDP 的 Socket 編程中,客戶端和服務端需要調用的函數;
epoll 機制能夠解決 C10K 問題。
參考:
The TCP/IP Guide;
百度百科 - Socket 詞條;
劉超 - 趣談網絡協議系列課;
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/29762.html
摘要:有興趣的博友可以按各編程語言進行相關改寫,然后拿著我們的分析系統真實的看看網絡通信過程。本機請求轉發到網關代碼中的是內網另一臺服務器,樓主的是。主要是下面的分析過程。分析系統介紹上面用到的分析系統叫科來網絡分析系統,點我下載。 系列文章傳送門: 網絡協議 1 - 概述 網絡協議 2 - IP 是怎么來,又是怎么沒的? 網絡協議 3 - 從物理層到 MAC 層 網絡協議 4 - 交換機...
摘要:有興趣的博友可以按各編程語言進行相關改寫,然后拿著我們的分析系統真實的看看網絡通信過程。本機請求轉發到網關代碼中的是內網另一臺服務器,樓主的是。主要是下面的分析過程。分析系統介紹上面用到的分析系統叫科來網絡分析系統,點我下載。 系列文章傳送門: 網絡協議 1 - 概述 網絡協議 2 - IP 是怎么來,又是怎么沒的? 網絡協議 3 - 從物理層到 MAC 層 網絡協議 4 - 交換機...
閱讀 3025·2021-11-24 10:21
閱讀 1596·2021-10-11 10:57
閱讀 2811·2021-09-22 15:24
閱讀 2666·2021-09-22 14:58
閱讀 2334·2019-08-30 13:16
閱讀 3483·2019-08-29 13:05
閱讀 3417·2019-08-29 12:14
閱讀 3451·2019-08-27 10:55