成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

網(wǎng)絡(luò)協(xié)議 10 - Socket 編程:實(shí)踐是檢驗(yàn)真理的唯一標(biāo)準(zhǔn)

Pocher / 2758人閱讀

摘要:文件描述符是一個(gè)整數(shù)索引值,是這個(gè)數(shù)組的下標(biāo)?;趨f(xié)議的基于的編程過程和有些不同。通過名字可以看出,這是在父進(jìn)程的基礎(chǔ)上完全拷貝一個(gè)子進(jìn)程。因此父進(jìn)程剛才因?yàn)閯?chuàng)建的已連接也是一個(gè)文件描述符,同樣也會(huì)被子進(jìn)程獲得。

系列文章傳送門:

網(wǎng)絡(luò)協(xié)議 1 - 概述

網(wǎng)絡(luò)協(xié)議 2 - IP 是怎么來,又是怎么沒的?

網(wǎng)絡(luò)協(xié)議 3 - 從物理層到 MAC 層

網(wǎng)絡(luò)協(xié)議 4 - 交換機(jī)與 VLAN:辦公室太復(fù)雜,我要回學(xué)校

網(wǎng)絡(luò)協(xié)議 5 - ICMP 與 ping:投石問路的偵察兵

網(wǎng)絡(luò)協(xié)議 6 - 路由協(xié)議:敢問路在何方?

網(wǎng)絡(luò)協(xié)議 7 - UDP 協(xié)議:性善碰到城會(huì)玩

網(wǎng)絡(luò)協(xié)議 8 - TCP 協(xié)議(上):性惡就要套路深

網(wǎng)絡(luò)協(xié)議 9 - TCP協(xié)議(下):聰明反被聰明誤


????前面一直在說各種協(xié)議,偏理論方面的知識(shí),這次咱們就來認(rèn)識(shí)下基于 TCP 和 UDP 協(xié)議這些理論知識(shí)的 Socket 編程。

????說 TCP 和 UDP 的時(shí)候,我們是分成客戶端和服務(wù)端來認(rèn)識(shí)的,那在寫 Socket 的時(shí)候,我們也這樣分。

????Socket 這個(gè)名字很有意思,可以作插口或者插槽講。我們寫程序時(shí),就可以將 Socket 想象為,一頭插在客戶端,一頭插在服務(wù)端,然后進(jìn)行通信。

????在建立 Socket 的時(shí)候,應(yīng)該設(shè)置什么參數(shù)呢?Socket 編程進(jìn)行的是端到端的通信,往往意識(shí)不到中間經(jīng)過多少局域網(wǎng),多少路由器,因而能夠設(shè)置的參數(shù),也只能是端到端協(xié)議之上網(wǎng)絡(luò)層和傳輸層的。

????對于網(wǎng)絡(luò)層和傳輸層,有以下參數(shù)需要設(shè)置:

IP協(xié)議:IPv4 對應(yīng) AF_INEF,IPv6 對應(yīng) AF_INET6;

傳輸層協(xié)議:TCP 與 UDP。TCP 協(xié)議基于數(shù)據(jù)流,其對應(yīng)值是 SOCKET_STREAM,而 UDP 是基于數(shù)據(jù)報(bào)的,其對應(yīng)值是 SOCKET_DGRAM。

????兩端創(chuàng)建了 Socket 之后,而后面的過程中,TCP 和 UDP 稍有不同,我們先來看看 TCP。

基于 TCP 協(xié)議的 Socket

????對于 TCP 創(chuàng)建 Socket 的過程,有以下幾步走:

????1)TCP 調(diào)用 bind 函數(shù)賦予 Socket IP 地址和端口。

????為什么需要 IP 地址?還記得嗎?咱們之前了解過,一臺(tái)機(jī)器會(huì)有多個(gè)網(wǎng)卡,而每個(gè)網(wǎng)卡就有一個(gè) IP 地址,我們可以選擇監(jiān)聽所有的網(wǎng)卡,也可以選擇監(jiān)聽一個(gè)網(wǎng)卡,只有,發(fā)給指定網(wǎng)卡的包才會(huì)發(fā)給你。

????為什么需要端口?要知道,咱們寫的是一個(gè)應(yīng)用程序,當(dāng)一個(gè)網(wǎng)絡(luò)包來的時(shí)候,內(nèi)核就是要通過 TCP 里面的端口號(hào)來找到對應(yīng)的應(yīng)用程序,把包給你。

????2)調(diào)用 listen 函數(shù)監(jiān)聽端口。 在 TCP 的狀態(tài)圖了,有一個(gè) listen 狀態(tài),當(dāng)調(diào)用這個(gè)函數(shù)之后,服務(wù)端就進(jìn)入了這個(gè)狀態(tài),這個(gè)時(shí)候客戶端就可以發(fā)起連接了。

????在內(nèi)核中,為每個(gè) Socket 維護(hù)兩個(gè)隊(duì)列。一個(gè)是已經(jīng)建立了連接的隊(duì)列,這里面的連接已經(jīng)完成三次握手,處于 established 狀態(tài);另一個(gè)是還沒有完全建立連接的隊(duì)列,這里面的連接還沒有完成三次握手,處于 syn_rcvd 狀態(tài)。

????3)服務(wù)端調(diào)用 accept 函數(shù)。 這時(shí)候服務(wù)端會(huì)拿出一個(gè)已經(jīng)完成的連接進(jìn)行處理,如果還沒有已經(jīng)完成的連接,就要等著。

????在服務(wù)端等待的時(shí)候,客戶端可以通過 connect 函數(shù)發(fā)起連接??蛻舳讼仍趨?shù)中指明要連接的 IP 地址和端口號(hào),然后開始發(fā)起三次握手。內(nèi)核會(huì)給客戶端分配一個(gè)臨時(shí)的端口,一旦握手成功,服務(wù)端的 accept 就會(huì)返回另一個(gè) Socket。

????注意,從上面的過程中可以看出,監(jiān)聽的 Socket 和真正用來傳數(shù)據(jù)的 Socket 是不同的兩個(gè)。 一個(gè)叫做監(jiān)聽 Socket,一個(gè)叫做已連接 Socket。

????下圖就是基于 TCP 協(xié)議的 Socket 函數(shù)調(diào)用過程:

????連接建立成功之后,雙方開始通過 read 和write 函數(shù)來讀寫數(shù)據(jù),就像往一個(gè)文件流里寫東西一樣。

????這里說 TCP 的 Socket 是一個(gè)文件流,是非常準(zhǔn)確的。因?yàn)?Socket 在 linux 中就是以文件的形式存在的。除此之外,還存在文件描述符。寫入和讀出,也是通過文件描述符。

????每一個(gè)進(jìn)程都有一個(gè)數(shù)據(jù)結(jié)構(gòu) task_struct,里面指向一個(gè)文件描述符數(shù)組,來列出這個(gè)進(jìn)程打開的所有文件的文件描述符。文件描述符是一個(gè)整數(shù)索引值,是這個(gè)數(shù)組的下標(biāo)。

????這個(gè)數(shù)組中的內(nèi)容是一個(gè)指針,指向內(nèi)核中所有打開的文件列表。而每個(gè)文件也會(huì)有一個(gè) inode(索引節(jié)點(diǎn))。

????對于 Socke 而言,它是一個(gè)文件,也就有對于的文件描述符。與真正的文件系統(tǒng)不一樣的是,Socket 對于的 inode 并不是保存在硬盤上,而是在內(nèi)存中。在這個(gè) inode 中,指向了 Socket 在內(nèi)核中的 Socket 結(jié)構(gòu)。

????在這個(gè)機(jī)構(gòu)里面,主要有兩個(gè)隊(duì)列。一個(gè)發(fā)送隊(duì)列,一個(gè)接收隊(duì)列。這兩個(gè)隊(duì)列里面,保存的是一個(gè)緩存 sk_buff。這個(gè)緩存里能夠看到完整的包結(jié)構(gòu)。說到這里,你應(yīng)該就會(huì)發(fā)現(xiàn),數(shù)據(jù)結(jié)構(gòu)以及和前面了解的收發(fā)包的場景聯(lián)系起來了。

????上面整個(gè)過程說起來稍顯混亂,可對比下圖加深理解。

基于 UDP 協(xié)議的 Socket

????基于 UDP 的 Socket 編程過程和 TCP 有些不同。UDP 是沒有連接狀態(tài)的,所以不需要三次握手,也就不需要調(diào)用 listen 和 connect。沒有連接狀態(tài),也就不需要維護(hù)連接狀態(tài),因而不需要對每個(gè)連接建立一組 Socket,只要建立一組 Socket,就能和多個(gè)客戶端通信。也正是因?yàn)闆]有連接狀態(tài),每次通信的時(shí)候,都可以調(diào)用 sendto 和 recvfrom 傳入 IP 地址和端口。

????下圖是基于 UDP 的 Socket 函數(shù)調(diào)用過程:

服務(wù)器最大并發(fā)量

????了解了基本的 Socket 函數(shù)后,就可以寫出一個(gè)網(wǎng)絡(luò)交互的程序了。就像上面的過程一樣,在建立連接后,進(jìn)行一個(gè) while 循環(huán),客戶端發(fā)了收,服務(wù)端收了發(fā)。

????很明顯,這種一臺(tái)服務(wù)器服務(wù)一個(gè)客戶的方式和我們的實(shí)際需要相差甚遠(yuǎn)。這就相當(dāng)于老板成立了一個(gè)公司,只有自己一個(gè)人,自己親自服務(wù)客戶,只能干完一家再干下一家。這種方式肯定賺不了錢,這時(shí)候,就要想,我最多能接多少項(xiàng)目呢?

????我們可以先來算下理論最大值,也就是理論最大連接數(shù)。系統(tǒng)會(huì)用一個(gè)四元組來標(biāo)識(shí)一個(gè) TCP 連接:

{本機(jī) IP,本機(jī)端口,對端 IP,對端端口}

????服務(wù)器通常固定監(jiān)聽某個(gè)本地端口,等待客戶端連接請求。因此,上面四元組中,可變的項(xiàng)只有對端 IP 和對端端口,也就是客戶端 IP 和客戶端端口。不難得出:

最大 TCP 連接數(shù) = 客戶端 IP 數(shù) x 客戶端端口數(shù)。

????對于 IPv4:

客戶端最大 IP 數(shù) = 2 的 32 次方

????對于端口數(shù):

客戶端最大端口數(shù) = 2 的 16 次方

????因此:

最大 TCP 連接數(shù) = 2 的 48 次方(估算值)

????當(dāng)然,服務(wù)端最大并發(fā) TCP 連接數(shù)遠(yuǎn)不能達(dá)到理論最大值。主要有以下原因:

文件描述符限制。按照上面的原理,Socket 都是文件,所以首先要通過 ulimit 配置文件描述符的數(shù)目;

內(nèi)存限制。按上面的數(shù)據(jù)結(jié)構(gòu),每個(gè) TCP 連接都要占用一定的內(nèi)存,而系統(tǒng)內(nèi)存是有限的。

????所以,作為老板,在資源有限的情況下,要想接更多的項(xiàng)目,賺更多的錢,就要降低每個(gè)項(xiàng)目消耗的資源數(shù)目。

????本著這個(gè)原則,我們可以找到以下幾種方式來最可能的降低消耗項(xiàng)目消耗資源。

1)將項(xiàng)目外包給其他公司(多進(jìn)程方式)

????這就相當(dāng)于你是一個(gè)代理,監(jiān)聽來的請求,一旦建立一個(gè)連接,就會(huì)有一個(gè)已連接的 Socket,這時(shí)候你可以創(chuàng)建一個(gè)紫禁城,然后將基于已連接的 Socket 交互交給這個(gè)新的子進(jìn)程來做。就像來了一個(gè)新項(xiàng)目,你可以注冊一家子公司,招人,然后把項(xiàng)目轉(zhuǎn)包給這就公司做,這樣你就又可以去接新的項(xiàng)目了。

????這里有個(gè)問題是,如何創(chuàng)建子公司,并將項(xiàng)目移交給子公司?

????在 Linux 下,創(chuàng)建子進(jìn)程使用 fork 函數(shù)。通過名字可以看出,這是在父進(jìn)程的基礎(chǔ)上完全拷貝一個(gè)子進(jìn)程。在 Linux 內(nèi)核中,會(huì)復(fù)制文件描述符的列表,也會(huì)復(fù)制內(nèi)存空間,還會(huì)復(fù)制一條記錄當(dāng)前執(zhí)行到了哪一行程序的進(jìn)程。

????這樣,復(fù)制完成后,父進(jìn)程和子進(jìn)程都會(huì)記錄當(dāng)前剛剛執(zhí)行完 fork。這兩個(gè)進(jìn)程剛復(fù)制完的時(shí)候,幾乎一模一樣,只是根據(jù) fork 的返回值來區(qū)分是父進(jìn)程還是子進(jìn)程。如果返回值是 0,則是子進(jìn)程,如果返回值是其他的整數(shù),就是父進(jìn)程,這里返回的整數(shù),就是子進(jìn)程的 ID。

????進(jìn)程復(fù)制過程如下圖:

????因?yàn)閺?fù)制了文件描述符列表,而文件描述符都是指向整個(gè)內(nèi)核統(tǒng)一的打開文件列表的。因此父進(jìn)程剛才因?yàn)?accept 創(chuàng)建的已連接 Socket 也是一個(gè)文件描述符,同樣也會(huì)被子進(jìn)程獲得。

????接下來,子進(jìn)程就可以通過這個(gè)已連接 Socket 和客戶端進(jìn)行通信了。當(dāng)通信完成后,就可以退出進(jìn)程。那父進(jìn)程如何知道子進(jìn)程干完了項(xiàng)目要退出呢?父進(jìn)程中 fork 函數(shù)返回的整數(shù)就是子進(jìn)程的 ID,父進(jìn)程可以通過這個(gè) ID 查看子進(jìn)程是否完成項(xiàng)目,是否需要退出。

2)將項(xiàng)目轉(zhuǎn)包給獨(dú)立的項(xiàng)目組(多線程方式)

????上面這種方式你應(yīng)該能發(fā)現(xiàn)問題,如果每接一個(gè)項(xiàng)目,都申請一個(gè)新公司,然后干完了,就注銷掉,實(shí)在是太麻煩了。而且新公司要有新公司的資產(chǎn)、辦公家具,每次都買了再賣,不劃算。

????這時(shí)候,我們應(yīng)該已經(jīng)想到了線程。相比于進(jìn)程來講,線程更加輕量級(jí)。如果創(chuàng)建進(jìn)程相當(dāng)于成立新公司,而創(chuàng)建線程,就相當(dāng)于在同一個(gè)公司成立新的項(xiàng)目組。一個(gè)項(xiàng)目做完了,就解散項(xiàng)目組,成立新的項(xiàng)目組,辦公家具還可以共用。

????在 Linux 下,通過 pthread_create 創(chuàng)建一個(gè)線程,也是調(diào)用 do_fork。不同的是,雖然新的線程在 task 列表會(huì)新創(chuàng)建一項(xiàng),但是很多資源,例如文件描述符列表、進(jìn)程空間,這些還是共享的,只不過多了一個(gè)引用而已。

????下圖是線程復(fù)制過程:

????新的線程也可以通過已連接 Socket 處理請求,從而達(dá)到并發(fā)處理的目的。

????上面兩種方式,無論是基于進(jìn)程還是線程模型的,其實(shí)還是有問題的。新到來一個(gè) TCP 連接,就需要分配一個(gè)進(jìn)程或者線程。一臺(tái)機(jī)器能創(chuàng)建的進(jìn)程和線程數(shù)是有限的,并不能很好的發(fā)揮服務(wù)器的性能。著名的C10K問題,就是說一臺(tái)機(jī)器如何維護(hù) 1 萬了連接。按我們上面的方式,系統(tǒng)就要?jiǎng)?chuàng)建 1 萬個(gè)進(jìn)程或者線程,這是操作系統(tǒng)無法承受的。

????那既然一個(gè)線程負(fù)責(zé)一個(gè) TCP 連接不行,能不能一個(gè)進(jìn)程或線程負(fù)責(zé)多個(gè) TCP 連接呢?這就引出了下面兩種方式。

3)一個(gè)項(xiàng)目組支撐多個(gè)項(xiàng)目(IO 多路復(fù)用,一個(gè)線程維護(hù)多個(gè) Socket)

????當(dāng)一個(gè)項(xiàng)目組負(fù)責(zé)多個(gè)項(xiàng)目時(shí),就要有個(gè)項(xiàng)目進(jìn)度墻來把控每個(gè)項(xiàng)目的進(jìn)度,除此之外,還得有個(gè)人專門盯著進(jìn)度墻。

????上面說過,Socket 是文件描述符,因此某個(gè)線程盯的所有的 Socket,都放在一個(gè)文件描述符集合 fd_set 中,這就是項(xiàng)目進(jìn)度墻。然后調(diào)用 select 函數(shù)來監(jiān)聽文件描述符集合是否有變化,一旦有變化,就會(huì)依次查看每個(gè)文件描述符。那些發(fā)生變化的文件描述符在 fd_set 對應(yīng)的位都設(shè)為 1,表示 Socket 可讀或者可寫,從而可以進(jìn)行讀寫操作,然后再調(diào)用 select,接著盯著下一輪的變化。

4)一個(gè)項(xiàng)目組支撐多個(gè)項(xiàng)目(IO 多路復(fù)用,從“派人盯著”到“有事通知”)

????上面 select 函數(shù)還是有問題的,因?yàn)槊看?Socket 所在的文件描述符集合中有發(fā)生變化的時(shí)候,都需要通過輪詢的方式將所有的 Socket 查看一遍,這大大影響了一個(gè)進(jìn)程或者線程能夠支撐的最大連接數(shù)量。使用 select,能夠同時(shí)監(jiān)聽的數(shù)量由 FD_SETSIZE 限制。

????如果改成事件通知的方式,情況就會(huì)好很多。項(xiàng)目組不需要通過輪詢挨個(gè)盯著所有項(xiàng)目,而是當(dāng)項(xiàng)目進(jìn)度發(fā)生變化的時(shí)候,主動(dòng)通知項(xiàng)目組,然后項(xiàng)目組再根據(jù)項(xiàng)目進(jìn)展情況做相應(yīng)的操作。

????而 epoll 函數(shù)就能完成事件通知。它在內(nèi)核中的實(shí)現(xiàn)不是通過輪詢的方式,而是通過注冊 callback 函數(shù)的方式,當(dāng)某個(gè)文件描述符發(fā)生變化的時(shí)候,主動(dòng)通知。

????如上圖所示,假設(shè)進(jìn)程打開了 Socket m、n、x 等多個(gè)文件描述符,現(xiàn)在需要通過 epoll 來監(jiān)聽這些 Socket 是否有事件發(fā)生。其中 epoll_create 創(chuàng)建一個(gè) epoll 對象,也是一個(gè)文件,對應(yīng)一個(gè)文件描述符,同樣也對應(yīng)著打開文件列表中的一項(xiàng)。在這項(xiàng)里面有一個(gè)紅黑樹,在紅黑樹里,要保存這個(gè) epoll 監(jiān)聽的所有的 Socket。

????當(dāng) epoll_ctl 添加一個(gè) Scoket 的時(shí)候,其實(shí)就是加入這個(gè)紅黑樹中。同時(shí),紅黑樹里面的節(jié)點(diǎn)指向一個(gè)結(jié)構(gòu),將這個(gè)結(jié)構(gòu)掛在被監(jiān)聽的 Socket 的事件列表中。當(dāng)一個(gè) Socket 發(fā)生某個(gè)事件時(shí),可以從這個(gè)列表中得到 epoll 對象,并調(diào)用 call_back 通知它。

????這種事件通知的方式使得監(jiān)聽的 Socket 數(shù)量增加的同時(shí),效率也不會(huì)大幅度降低。因此,能夠同時(shí)監(jiān)聽的 Socket 的數(shù)量就非常的多了。上限為系統(tǒng)定義的,進(jìn)程打開的最大文件描述符個(gè)數(shù)。因而,epoll 被稱為解決 C10K 問題的利器。

小結(jié)

牢記基于 TCP 和 UDP 的 Socket 編程中,客戶端和服務(wù)端需要調(diào)用的函數(shù);

epoll 機(jī)制能夠解決 C10K 問題。

參考:

The TCP/IP Guide;

百度百科 - Socket 詞條;

劉超 - 趣談網(wǎng)絡(luò)協(xié)議系列課;

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/29762.html

相關(guān)文章

  • 網(wǎng)絡(luò)協(xié)議 11 - Socket 編程(下):眼見為實(shí)耳聽為虛

    摘要:有興趣的博友可以按各編程語言進(jìn)行相關(guān)改寫,然后拿著我們的分析系統(tǒng)真實(shí)的看看網(wǎng)絡(luò)通信過程。本機(jī)請求轉(zhuǎn)發(fā)到網(wǎng)關(guān)代碼中的是內(nèi)網(wǎng)另一臺(tái)服務(wù)器,樓主的是。主要是下面的分析過程。分析系統(tǒng)介紹上面用到的分析系統(tǒng)叫科來網(wǎng)絡(luò)分析系統(tǒng),點(diǎn)我下載。 系列文章傳送門: 網(wǎng)絡(luò)協(xié)議 1 - 概述 網(wǎng)絡(luò)協(xié)議 2 - IP 是怎么來,又是怎么沒的? 網(wǎng)絡(luò)協(xié)議 3 - 從物理層到 MAC 層 網(wǎng)絡(luò)協(xié)議 4 - 交換機(jī)...

    WalkerXu 評論0 收藏0
  • 網(wǎng)絡(luò)協(xié)議 11 - Socket 編程(下):眼見為實(shí)耳聽為虛

    摘要:有興趣的博友可以按各編程語言進(jìn)行相關(guān)改寫,然后拿著我們的分析系統(tǒng)真實(shí)的看看網(wǎng)絡(luò)通信過程。本機(jī)請求轉(zhuǎn)發(fā)到網(wǎng)關(guān)代碼中的是內(nèi)網(wǎng)另一臺(tái)服務(wù)器,樓主的是。主要是下面的分析過程。分析系統(tǒng)介紹上面用到的分析系統(tǒng)叫科來網(wǎng)絡(luò)分析系統(tǒng),點(diǎn)我下載。 系列文章傳送門: 網(wǎng)絡(luò)協(xié)議 1 - 概述 網(wǎng)絡(luò)協(xié)議 2 - IP 是怎么來,又是怎么沒的? 網(wǎng)絡(luò)協(xié)議 3 - 從物理層到 MAC 層 網(wǎng)絡(luò)協(xié)議 4 - 交換機(jī)...

    newtrek 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<