深入理解?Linux?socket
socket fd 長(zhǎng)什么樣子?
什么是 socket fd ?粗糙的來(lái)講,就是網(wǎng)絡(luò) fd,比如我們最常見(jiàn)的 C/S 客戶(hù)端服務(wù)端的編程模式,就是網(wǎng)絡(luò)通信的一種方式。撇開(kāi)底層和協(xié)議細(xì)節(jié),網(wǎng)絡(luò)通信和文件讀寫(xiě)從接口上有本質(zhì)區(qū)別嗎?
其實(shí)沒(méi)啥區(qū)別,不就是讀過(guò)來(lái)和寫(xiě)過(guò)去嘛,簡(jiǎn)稱(chēng) IO 。我們先看一下 socket fd 是什么樣子的?隨便找了個(gè)進(jìn)程
root@ubuntu:~#?ll?/proc/1583/fd
total?0
lrwx------?1?root?root?64?Jul?19?12:37?7?->?socket:[18892]
lrwx------?1?root?root?64?Jul?19?12:37?8?->?socket:[18893]
這里我們看到 fd 7、8 都是一個(gè) socket fd,名字:socket:[18892]
整數(shù)句柄后面一般會(huì)跟一些信息,用于幫助我們了解這個(gè) fd 是什么。舉個(gè)例子,如果是文件 fd,那么箭頭后面一般是路徑名稱(chēng)?,F(xiàn)在拆解一下這個(gè)名字:- socket :標(biāo)識(shí)這是一個(gè) socket 類(lèi)型的 fd
[18892]
?:這個(gè)是一個(gè) inode 號(hào),能夠唯一標(biāo)識(shí)本機(jī)的一條網(wǎng)絡(luò)連接;
/proc/net/tcp
文件。這個(gè)文件里面能看到所有的 tcp 連接的信息。root@ubuntu:~#?grep?-i?"18892"?/proc/net/tcp
??18:?00000000:1F93?00000000:0000?0A?00000000:00000000?00:00000000?00000000?????0????????0?18892?1?ffff880197fba580?100?0?0?10?0?????????????????????
root@ubuntu:~#?grep?-i?"18893"?/proc/net/tcp
??28:?00000000:1F7C?00000000:0000?0A?00000000:00000000?00:00000000?00000000?????0????????0?18893?1?ffff880197fbad00?100?0?0?10?0????
知識(shí)點(diǎn)又來(lái)了,/proc/net/tcp
這個(gè)文件記錄了 tcp 連接的信息,這份信息是非常有用的。包含了 TCP 連接的地址(16進(jìn)制顯示),inode 的信息,連接的狀態(tài)等等。socket fd 是什么?
環(huán)境聲明:
Linux 內(nèi)核版本 4.19?為了方便,如果沒(méi)特意說(shuō)明協(xié)議,默認(rèn) TCP 協(xié)議;
socket
可能你還沒(méi)反應(yīng)過(guò)來(lái),中文名:套接字 是不是更熟悉點(diǎn)。Linux 網(wǎng)絡(luò)編程甚至可以叫做套接字編程。有些概念你必須捋一捋 。我們思考幾個(gè)小問(wèn)題:socket 跟 tcp/ip 有什么區(qū)別?就不該把這兩個(gè)東西放在一起比較討論,就不是一個(gè)東西。tcp/ip 是網(wǎng)絡(luò)協(xié)議棧,socket 是操作系統(tǒng)為了方便網(wǎng)絡(luò)編程而設(shè)計(jì)出來(lái)的編程接口而已。理論基礎(chǔ)是各種網(wǎng)絡(luò)協(xié)議,協(xié)議棧呀,啥的。但是如果你要進(jìn)行網(wǎng)絡(luò)編程,落到實(shí)處,對(duì)程序猿來(lái)講就是 socket 編程。對(duì)于網(wǎng)絡(luò)的操作,由 socket 體現(xiàn)為 open -> read/write ->close 這樣的編程模式,這個(gè)統(tǒng)一到文件的一種形式。socket 的 open 就是 socket(int domain, int type, int protocol)
,和文件一樣,都是獲取一個(gè)句柄。
網(wǎng)絡(luò)模型一般會(huì)對(duì)應(yīng)到兩種:
- 完美理論的 OSI 七層模型;
- 現(xiàn)實(shí)應(yīng)用的 5 層模型;
- 客戶(hù)端和服務(wù)端都用
socket
調(diào)用創(chuàng)建套接字; - 服務(wù)端用
bind
綁定監(jiān)聽(tīng)地址,用listen
把套接字轉(zhuǎn)化為監(jiān)聽(tīng)套接字,用accept
撈取一個(gè)客戶(hù)端來(lái)的連接; - 客戶(hù)端用
connect
進(jìn)行建連,用write/read
進(jìn)行網(wǎng)絡(luò) IO;
socket fd 的類(lèi)型
上面我們提到了套接字,這是我們網(wǎng)絡(luò)編程的主體,套接字由
socket()
系統(tǒng)調(diào)用創(chuàng)建,但你可知套接字其實(shí)可分為兩種類(lèi)型,監(jiān)聽(tīng)套接字和普通套接字。而監(jiān)聽(tīng)套接字是由 listen()
把 socket fd 轉(zhuǎn)化而成。?1???監(jiān)聽(tīng)套接字
對(duì)于監(jiān)聽(tīng)套接字,不走數(shù)據(jù)流,只管理連接的建立。
accept
將從全連接隊(duì)列獲取一個(gè)創(chuàng)建好的 socket( 3 次握手完成),對(duì)于監(jiān)聽(tīng)套接字的可讀事件就是全連接隊(duì)列非空。對(duì)于監(jiān)聽(tīng)套接字,我們只在乎可讀事件。?2???普通套接字
普通套接字就是走數(shù)據(jù)流的,也就是網(wǎng)絡(luò) IO,針對(duì)普通套接字我們關(guān)注可讀可寫(xiě)事件。在說(shuō) socket 的可讀可寫(xiě)事件之前,我們先捋順套接字的讀寫(xiě)大概是什么樣子吧。套接字層是內(nèi)核提供給程序員用來(lái)網(wǎng)絡(luò)編程的,程序猿讀寫(xiě)都是針對(duì)套接字而言,那么
write( socketfd, /* 參數(shù) */)
和 read( socketfd, /* 參數(shù) */)
都會(huì)發(fā)生什么呢?- write 數(shù)據(jù)到 socketfd,大部分情況下,數(shù)據(jù)寫(xiě)到 socket 的內(nèi)存 buffer,就結(jié)束了,并沒(méi)有發(fā)送到對(duì)端網(wǎng)絡(luò)(異步發(fā)送);
- read socketfd 的數(shù)據(jù),也只是從 socket 的 內(nèi)存 buffer 里讀數(shù)據(jù)而已,而不是從網(wǎng)卡讀(雖然數(shù)據(jù)是從網(wǎng)卡一層層遞上來(lái)的);
- socketfd 可讀:其實(shí)就是 socket buffer 內(nèi)有數(shù)據(jù)(超過(guò)閾值 SO_RCLOWAT );
- socketfd 可寫(xiě):就是 socket buffer 還有空間讓你寫(xiě)(閾值 SO_SNDLOWAT );
socket fd 為什么能具備“文件”的語(yǔ)義,從而和 eventfd,ext2 fd 這樣的句柄一樣,統(tǒng)一提供對(duì)外 io 的樣子?
核心就是:sockfs ,這也是個(gè)文件系統(tǒng),只不過(guò)普通用戶(hù)看不見(jiàn),這是只由內(nèi)核管理的文件系統(tǒng),位于 vfs 之下,為了封裝 socket 對(duì)上的文件語(yǔ)義。
//?net/socket.c
static?int?__init?sock_init(void)
{
????//?注冊(cè)?sockfs?文件系統(tǒng)
????err?=?register_filesystem(