久久国产精品一区二区三区四区,久色婷婷小香蕉久久,国产日韩欧美在线播放不卡,另类av一区二区

Socket到底是什么?你想知道嗎?
來源:小白debug    時間:2022-12-21 09:02:07

我相信大家剛開始學socket的時候,都跟我一樣。

云里霧里的,對socket的概念很模糊。


(相關資料圖)

這篇文章我打算從一個初學者的角度開始聊起,讓大家了解下我眼里的socket是什么以及socket的原理和內核實現。

socket的概念

故事要從一個插頭說起。

插頭與插座

當我將插頭插入插座,那看起來就像是將兩者連起來了。

風扇與電力系統建立"連接"

而插座的英文,又叫socket。

巧了,我們程序員搞網絡編程時也會用到一個叫socket的東西。

其實兩者非常相似。通過socket,我們可以與某臺機子建立"連接",建立"連接"的過程,就像是將插口插入插槽一樣。

大概概念是了解了,但我相信各位對socket其實還是很模糊。

我們從大家最熟悉的使用場景開始說起。

socket的使用場景

我們想要將數據從A電腦的某個進程發到B電腦的某個進程。

這時候我們需要選擇將數據發過去的方式,如果需要確保數據要能發給對方,那就選可靠的TCP協議,如果數據丟了也沒關系,看天意,那就選擇不可靠的UDP協議。

初學者毫無疑問,首選TCP。

TCP是什么

那這時候就需要用socket進行編程。

于是第一步就是創建個關于TCP的socket。就像下面這樣。

sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

這個方法會返回socket_fd,它是socket文件的句柄,是個數字,相當于socket的身份證號。

得到了socket_fd之后,對于服務端,就可以依次執行bind(),listen(),accept()方法,然后坐等客戶端的連接請求。

對于客戶端,得到socket_fd之后,你就可以執行connect()方法向服務端發起建立連接的請求,此時就會發生TCP三次握手。

握手建立連接流程

連接建立完成后,客戶端可以執行send()方法發送消息,服務端可以執行recv()方法接收消息,反過來,服務器也可以執行send(),客戶端執行recv()方法。

到這里為止,就是我們大部分程序員最熟悉的使用場景。

socket的設計

現在,socket我們見過,也用過,但對大部分程序員來說,它是個黑盒。

那既然是黑盒,我們索性假設我們忘了socket。重新設計一個內核網絡傳輸功能。

網絡傳輸,從操作上來看,無非就是,發數據和遠端之間互相收發數據。也就是對應著寫數據和讀數據。

讀寫收發

但顯然,事情沒那么簡單。

這里還有兩個問題。

第一個是,接收端和發送端可能不止一個,因此我們需要一些信息做下區分,這個大家肯定很熟悉,可以用IP和端口。IP用來定位是哪臺電腦,端口用來定位是這臺電腦上的哪個進程。

第二個是,發送端和接收端的傳輸方式有很多區別,可以是可靠的TCP協議,也可以是不可靠的UDP協議,甚至還需要支持基于icmp協議的ping命令。

sock是什么

寫過代碼的都知道,為了支持這些功能,我們需要定義一個數據結構去支持這些功能。

這個數據結構,叫sock。

為了解決上面的第一個問題,我們可以在sock里加入IP和端口字段。

sock加入IP和端口字段

而第二個問題,我們會發現這些協議雖然各不相同,但還是有一些功能相似的地方,比如收發數據時的一些邏輯完全可以復用。按面向對象編程的思想,我們可以將不同的協議當成是不同的對象類(或結構體),將公共的部分提取出來,通過"繼承"的方式,復用功能。

基于各種sock實現網絡傳輸功能

于是,我們將功能重新劃分下,定義了一些數據結構。

繼承sock的各類sock

sock是最基礎的結構,維護一些任何協議都有可能會用到的收發數據緩沖區。

inet_sock特指用了網絡傳輸功能的sock,在sock的基礎上還加入了TTL,端口,IP地址這些跟網絡傳輸相關的字段信息。說到這里大家就懵了,難道還有不是用網絡傳輸的?有,比如Unix domain socket,用于本機進程之間的通信,直接讀寫文件,不需要經過網絡協議棧。這是個非常有用的東西,我以后一定講講(畫餅)。

inet_connection_sock是指面向連接的sock,在inet_sock的基礎上加入面向連接的協議里相關字段,比如accept隊列,數據包分片大小,握手失敗重試次數等。雖然我們現在提到面向連接的協議就是指TCP,但設計上linux需要支持擴展其他面向連接的新協議,

tcp_sock就是正兒八經的tcp協議專用的sock結構了,在inet_connection_sock基礎上還加入了tcp特有的滑動窗口、擁塞避免等功能。同樣udp協議也會有一個專用的數據結構,叫udp_sock。

好了,現在有了這套數據結構,我們將它們跟硬件網卡對接一下,就實現了網絡傳輸的功能。

提供socket層

可以想象得到,這里面的代碼肯定非常復雜,同時還操作了網卡硬件,需要比較高的操作系統權限,再考慮到性能和安全,于是決定將它放在操作系統內核里。

既然網絡傳輸功能做在內核里,那用戶空間的應用程序想要用這部分功能的話,該怎么辦呢?

這個好辦,本著不重復造輪子的原則,我們將這部分功能抽象成一個個簡單的接口。以后別人只需要調用這些接口,就可以驅動我們寫好的這一大堆復雜的數據結構去發送數據。

那么問題來了,怎么樣將這部分功能暴露出去呢?讓其他程序員更方便的使用呢?

既然跟遠端服務端進程收發數據可以抽象為“讀和寫”,操作文件也可以抽象為"讀和寫",正好有句話叫,"linux里一切皆是文件",那我們索性,將內核的sock封裝成文件就好了。創建sock的同時也創建一個文件,文件有個句柄fd,說白了就是個文件系統里的身份證號碼,通過它可以唯一確定是哪個sock。

這個文件句柄fd其實就是sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)里的sock_fd。

將句柄暴露給用戶,之后用戶就可以像操作文件句柄那樣去操作這個sock句柄。在用戶空間里操作這個句柄,文件系統就會將操作指向內核sock結構。

是的,操作這個特殊的文件就相當于操作內核里對應的sock。

通過文件找到sock

有了sock_fd句柄之后,我們就需要提供一些接口方法,讓用戶更方便的實現特定的網絡編程功能。這些接口,我們列了一下,發現需要有send(),recv(),bind(),listen(),connect()這些。到這里,我們的內核網絡傳輸功能就算設計完成了。

現在是不是眼熟了,上面這些接口方法其實就是socket提供出來的接口。

所以說,socket其實就是個代碼庫 or 接口層,它介于內核和應用程序之間,提供了一些高度封裝過的接口,讓我們去使用內核網絡傳輸功能。

基于sock實現網絡傳輸功能

到這里,我們應該明白了。我們平時寫的應用程序里代碼里雖然用了socket實現了收發數據包的功能,但其實真正執行網絡通信功能的,不是應用程序,而是linux內核。相當于應用程序通過socket提供的接口,將網絡傳輸的這部分工作外包給了linux內核。

這聽起來像不像我們最熟悉的前后端分離的服務架構,雖然這么說不太嚴謹,但看上去linux就像是被分成了應用程序和內核兩個服務。內核就像是后端,暴露了好多個api接口,其中一類就是socket的??send()???和??recv()??這些方法。應用程序就像是前端,負責調用內核提供的接口來實現想要的功能。

進程通過socket調用內核功能

看到這里,我擔心大家會有點混亂,來做個小的總結。

在操作系統內核空間里,實現網絡傳輸功能的結構是sock,基于不同的協議和應用場景,會被泛化為各種類型的xx_sock,它們結合硬件,共同實現了網絡傳輸功能。為了將這部分功能暴露給用戶空間的應用程序使用,于是引入了socket層,同時將sock嵌入到文件系統的框架里,sock就變成了一個特殊的文件,用戶就可以在用戶空間使用文件句柄,也就是socket_fd來操作內核sock的網絡傳輸能力。

這個socket_fd是一個int類型的數字?,F在回去看socket的中文翻譯,套接字,我將它理解為一套用于連接的數字,是不是就覺得特別合理了。

網絡分層與基于sock實現網絡傳輸功能

socket如何實現網絡通信

上面關于怎么實現網絡通信功能這一塊一筆帶過了。

現在我們來聊聊。

這套sock的結構其實非常復雜。我們以最常用的TCP協議為例,簡單了解下它是怎么實現網絡傳輸功能的。

我將它分為兩階段,分別是建立連接和數據傳輸。

建立連接

對于TCP,要傳數據,就得先在客戶端和服務端中間建立連接。

在客戶端,代碼執行socket提供的connect(sockfd, "ip:port")方法時,會通過sockfd句柄找到對應的文件,再根據文件里的信息指向內核的sock結構。通過這個sock結構主動發起三次握手。

TCP三次握手

在服務端握手次數還沒達到"三次"的連接,叫半連接,完成好三次握手的連接,叫全連接。它們分別會用半連接隊列和全連接隊列來存放,這兩個隊列會在你執行listen()方法的時候創建好。當服務端執行accept()方法時,就會從全連接隊列里拿出一條全連接。

半連接隊列和全連接隊列

至此,連接就算準備好了,之后,就可以開始傳輸數據。

雖然都叫隊列,但半連接隊列其實是個hash表,而全連接隊列其實是個鏈表。

那么問題來了,為什么半連接隊列要設計成哈希表而全連接隊列是個鏈表?這個在我在我之前寫的《沒有accept,能建立TCP連接嗎?》已經提到過,不再重復。

數據傳輸

為了實現發送和接收數據的功能,sock結構體里帶了一個發送緩沖區和一個接收緩沖區,說是緩沖區,但其實就是個鏈表,上面掛著一個個準備要發送或接收的數據。

當應用執行send()方法發送數據時,同樣也會通過sock_fd句柄找到對應的文件,根據文件指向的sock結構,找到這個sock結構里帶的發送緩沖區,將數據會放到發送緩沖區,然后結束流程,內核看心情決定什么時候將這份數據發送出去。

接收數據流程也類似,當數據送到linux內核后,數據不是立馬給到應用程序的,而是先放在接收緩沖區中,數據靜靜躺著,卑微的等待應用程序什么時候執行recv()方法來拿一下。就像我的文章,躺在你的推文列表里,卑微的等一個點贊關注轉發三連。懂?

sock的發送和接收緩沖區

IP和端口其實不在sock下,而在inet_sock下,上面這么畫只是為了簡化。。。

那么問題來了,發送數據是應用程序主動發起,這個大家都沒問題。

那接收數據呢?數據從遠端發過來了,怎么通知并給到應用程序呢?

這就需要用到等待隊列。

sock內的等待隊列

當你的應用進程執行recv()方法嘗試獲取(阻塞場景下)接收緩沖區的數據時。

如果有數據,那正好,取走就好了。這點沒啥疑問。但如果沒數據,就會將自己的進程信息注冊到這個sock用的等待隊列里,然后進程休眠。如果這時候有數據從遠端發過來了,數據進入到接收緩沖區時,內核就會取出sock的等待隊列里的進程,喚醒進程來取數據。

recv時無數據進程進入等待隊列

有時候,你會看到多個進程通過fork的方式,listen了同一個socket_fd。在內核,它們都是同一個sock,多個進程執行listen()之后,都嗷嗷等待連接進來,所以都會將自身的進程信息注冊到這個socket_fd對應的內核sock的等待隊列中。如果這時真來了一個連接,是該喚醒等待隊列里的哪個進程來接收連接呢?這個問題的答案比較有趣。

在linux 2.6以前,會喚醒等待隊列里的所有進程。但最后其實只有一個進程會處理這個連接請求,其他進程又重新進入休眠,這些被喚醒了又無事可做最后只能重新回去休眠的進程會消耗一定的資源。就好像你在廣東的街頭,想問路,叫一聲靚仔,幾十個人同時回頭,但你其實只需要其中一個靚仔告訴你路該怎么走。你這種一不小心驚動這群靚仔的場景,在計算機領域中,就叫驚群效應。在linux 2.6之后,只會喚醒等待隊列里的其中一個進程。是的,socket監聽的驚群效應問題被修復了。

驚群效應

看到這里,問題又來了。

服務端 listen 的時候,那么多數據到一個 socket 怎么區分多個客戶端的?

以TCP為例,服務端執行listen方法后,會等待客戶端發送數據來。客戶端發來的數據包上會有源IP地址和端口,以及目的IP地址和端口,這四個元素構成一個四元組,可以用于唯一標記一個客戶端。

其實說四元組并不嚴謹,因為過程中還有很多其他信息,也可以說是五元組。。。但大概理解就好,就這樣吧。。。

四元組

服務端會創建一個新的內核sock,并用四元組生成一個hash key,將它放入到一個hash表中。

四元組映射成hash鍵

下次再有消息進來的時候,通過消息自帶的四元組生成hash key再到這個hash表里重新取出對應的sock就好了。所以說服務端是通過四元組來區分多個客戶端的。

多個hash_key對應多個客戶端

sock怎么實現"繼承"

最后遺留一個問題。

大家都知道linux內核是C語言實現的,而C語言沒有類也沒有繼承的特性,是怎么做到"繼承"的效果的呢?

在C語言里,結構體里的內存是連續的,將要繼承的"父類",放到結構體的第一位,就像下面這樣。

struct tcp_sock { /* inet_connection_sock has to be the first member of tcp_sock */ struct inet_connection_sock inet_conn; // 其他字段}struct inet_connection_sock { /* inet_sock has to be the first member! */ struct inet_sock icsk_inet; // 其他字段}

然后我們就可以通過結構體名的長度來強行截取內存,這樣就能轉換結構體,從而實現類似"繼承"的效果。

// sock 轉為 tcp_sockstatic inline struct tcp_sock *tcp_sk(const struct sock *sk){ return (struct tcp_sock *)sk;}

內存布局

總結socket中文套接字,我理解為一用于連的數。并不一定準確,歡迎評論。sock在內核,socket_fd在用戶空間,socket層介于內核和用戶空間之間。在操作系統內核空間里,實現網絡傳輸功能的結構是sock,基于不同的協議和應用場景,會被泛化為各種類型的xx_sock,它們結合硬件,共同實現了網絡傳輸功能。為了將這部分功能暴露給用戶空間的應用程序使用,于是引入了socket層,同時將sock嵌入到文件系統的框架里,sock就變成了一個特殊的文件,用戶就可以在用戶空間使用文件句柄,也就是socket_fd來操作內核sock的網絡傳輸能力。服務端可以通過四元組來區分多個客戶端。內核通過c語言"結構體里的內存是連續的"這一特點實現了類似繼承的效果。最后

這是圖解網絡系列的第27篇文章。

大部分文章都是通過一個問題去引發話題,然后從基礎開始講起,最后再去回答問題。

我希望能做到的是將一個話題聊深,讓新手能看懂,讓有一些工作經驗的人看了也能有一定的收獲。

所以,從選題到寫文再到畫圖,時間成本極高。

關鍵詞: 應用程序 等待隊列 數據結構 面向連接 文件系統

上一篇:

下一篇:

X 關閉

X 關閉

久久国产精品一区二区三区四区,久色婷婷小香蕉久久,国产日韩欧美在线播放不卡,另类av一区二区
亚洲六月丁香色婷婷综合久久| 欧美影院成人| 国产视频精品网| 日韩视频免费大全中文字幕| 模特精品裸拍一区| 99精品国产福利在线观看免费| 欧美性感一类影片在线播放| 嫩草伊人久久精品少妇av杨幂| 一本不卡影院| 亚洲高清资源| 欧美一区二区精美| 国产一区在线看| 在线亚洲国产精品网站| 午夜精彩国产免费不卡不顿大片| 久久综合九色欧美综合狠狠| 一区三区视频| 欧美在线一二三四区| 欧美三区不卡| 欧美性生交xxxxx久久久| 国产伦精品一区二区三区四区免费| 欧美日韩一区二区在线视频| 一区二区三区视频在线看| 蜜桃视频一区| 欧美主播一区二区三区| 国产欧美一区二区精品秋霞影院| 激情久久中文字幕| 亚洲午夜小视频| 欧美亚洲色图校园春色| 在线成人小视频| 欧美日本中文字幕| 亚洲视频一区在线观看| 性欧美激情精品| 久久国产精品72免费观看| 亚洲美女av在线播放| 欧美大片在线观看一区| 正在播放欧美一区| 亚洲黄色在线看| 亚洲精品中文在线| 国产精品永久| 麻豆9191精品国产| 日韩视频免费大全中文字幕| 国产欧美日韩在线播放| 欧美成人激情视频免费观看| 亚洲尤物精选| 久久精品国产99国产精品澳门| 久久精品99国产精品酒店日本| 在线成人av网站| 久久精品国产亚洲高清剧情介绍| 欧美成人黄色小视频| 欧美日韩一区二区三区四区在线观看| 亚洲在线观看视频| 欧美三级电影一区| 在线一区视频| 亚洲国产精品传媒在线观看| 国产欧美精品日韩| 久久精品中文字幕一区| 在线亚洲一区| 国产精品午夜国产小视频| 99视频在线观看一区三区| 久久精品国产亚洲aⅴ| 欧美aaa级| 国产精品激情| 1000精品久久久久久久久| 国产亚洲欧美一区| 亚洲精品美女在线观看播放| 99精品视频一区二区三区| 亚洲一区二区三区精品视频| 欧美电影免费观看网站| 亚洲欧美在线x视频| 国产精品福利影院| 亚洲破处大片| 你懂的国产精品永久在线| 久久久精品久久久久| 午夜精品在线| 一区二区三区三区在线| 欧美精品一区三区| 国产精品视频专区| 国产欧美视频在线观看| 国产色产综合色产在线视频| 蜜桃av久久久亚洲精品| 亚洲精品欧美| 国产亚洲精品激情久久| 国产欧美日韩一区二区三区在线观看| 一区二区欧美日韩| 久久综合狠狠| 亚洲精品视频免费在线观看| 久久一二三四| 欧美性色视频在线| 欧美日韩国产综合视频在线观看| 一区二区三区中文在线观看| 亚洲精品国产精品国自产在线| 久久久噜噜噜久噜久久| 亚洲欧美国产高清va在线播| 亚洲午夜国产成人av电影男同| 免费国产自线拍一欧美视频| 国产一本一道久久香蕉| 亚洲每日更新| 欧美一区激情视频在线观看| 欧美色网在线| 亚洲精品美女在线观看| 欧美一区激情视频在线观看| 欧美巨乳在线| 欧美亚洲成人精品| 亚洲影院免费观看| 有码中文亚洲精品| 韩日视频一区| 久久亚洲美女| 日韩午夜在线观看视频| 欧美日韩卡一卡二| 久久久精品国产99久久精品芒果| 在线欧美影院| 国产精品午夜视频| 欧美午夜一区二区福利视频| 欧美成人日韩| 另类专区欧美制服同性| 牛牛精品成人免费视频| 国产精品羞羞答答xxdd| 激情小说另类小说亚洲欧美| 亚洲高清在线| 91久久精品国产| 亚洲——在线| 伊人久久综合97精品| 欧美成人精品一区二区三区| 国内久久视频| 国产喷白浆一区二区三区| 亚洲欧美日韩精品一区二区| 国产欧美日韩亚州综合| 亚洲久久在线| 午夜精品视频一区| 欧美日韩精品一本二本三本| 欧美大片网址| 亚洲欧美精品伊人久久| 亚洲国产精品毛片| 亚洲视频精品在线| 亚洲视频一区二区| 狠狠综合久久av一区二区小说| 午夜精品久久久99热福利| 国产精品免费小视频| 亚洲欧美日韩国产中文在线| 国产日韩精品在线| 欧美视频在线不卡| 国产精品一区二区三区久久久| 国产精品免费看片| 亚洲三级色网| 久久综合色影院| 国产精品美腿一区在线看| 欧美午夜精品理论片a级大开眼界| 欧美成人精精品一区二区频| 一区二区三区www| 欧美日韩免费一区二区三区| 亚洲小说春色综合另类电影| 国产私拍一区| 欧美天天影院| 国产精品xxxxx| 欧美成人精品不卡视频在线观看| 国产精品久久9| 久久精品中文| 欧美理论片在线观看| 国产精品高精视频免费| 欧美一级理论片| 欧美日韩一区二区三区在线看| 亚洲网站在线观看| 欧美一区二区三区四区在线观看| 国产精品亚洲综合色区韩国| 99精品国产99久久久久久福利| 亚洲电影毛片| 久久精品水蜜桃av综合天堂| 日韩手机在线导航| 国产一区日韩一区| 在线亚洲国产精品网站| 亚洲午夜高清视频| 亚洲精品黄色| 国产精品美女久久久久aⅴ国产馆| 国产欧美大片| 久久久国产精品一区二区中文| 欧美精品18videos性欧美| 久久在线免费| 在线精品视频一区二区三四| 欧美日韩国产综合网| 欧美日韩免费区域视频在线观看| 久久综合九九| 久久中文精品| 激情丁香综合| 国产精品白丝av嫩草影院| 亚洲精品一区二区三区四区高清| 91久久线看在观草草青青| 亚洲最新合集| 在线亚洲一区二区| 国产在线拍揄自揄视频不卡99| 亚洲午夜性刺激影院| 先锋影音一区二区三区| 国产综合一区二区| 美女黄毛**国产精品啪啪| 国产美女诱惑一区二区| 国产欧美另类| 欧美日韩视频不卡| 欧美va天堂va视频va在线| 国产精品美女一区二区| 国产精品女人久久久久久| 亚洲少妇一区|