www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁 > 公眾號精選 > 嵌入式客棧
[導(dǎo)讀]大家好,我是飛哥!在后端相關(guān)崗位的入職面試中,三次握手的出場頻率非常的高,甚至說它是必考題也不為過。一般的答案都是說客戶端如何發(fā)起SYN握手進(jìn)入SYN_SENT狀態(tài),服務(wù)器響應(yīng)SYN并回復(fù)SYNACK,然后進(jìn)入SYN_RECV,......,吧啦吧啦諸如此類。但我今天想給出一份不...

大家好,我是飛哥!


在后端相關(guān)崗位的入職面試中,三次握手的出場頻率非常的高,甚至說它是必考題也不為過。一般的答案都是說客戶端如何發(fā)起 SYN 握手進(jìn)入 SYN_SENT 狀態(tài),服務(wù)器響應(yīng) SYN 并回復(fù) SYNACK,然后進(jìn)入 SYN_RECV,...... , 吧啦吧啦諸如此類。


但我今天想給出一份不一樣的答案。其實三次握手在內(nèi)核的實現(xiàn)中,并不只是簡單的狀態(tài)的流轉(zhuǎn),還包括半連接隊列、syncookie、全連接隊列、重傳計時器等關(guān)鍵操作。如果能深刻理解這些,你對線上把握和理解將更進(jìn)一步。如果有面試官問起你三次握手,相信這份答案一定能幫你在面試官面前贏得非常多的加分。


在基于 TCP 的服務(wù)開發(fā)中,三次握手的主要流程圖如下。



服務(wù)器中的核心代碼是創(chuàng)建 socket,綁定端口,listen 監(jiān)聽,最后 accept 接收客戶端的請求。


//服務(wù)端核心代碼
int main(int argc, char const *argv[])
{
int fd = socket(AF_INET, SOCK_STREAM, 0);
bind(fd, ...);
listen(fd, 128);
accept(fd, ...);
...
}
客戶端的相關(guān)代碼是創(chuàng)建 socket,然后調(diào)用 connect 連接 server。


//客戶端核心代碼
int main(){
fd = socket(AF_INET,SOCK_STREAM, 0);
connect(fd, ...);
...
}
圍繞這個三次握手圖,以及客戶端,服務(wù)端的核心代碼,我們來深度探索一下三次握手過程中的內(nèi)部操作。我們從和三次握手過程關(guān)系比較大的 listen 講起!


友情提示:本文中內(nèi)核源碼會比較多。如果你能理解的了更好,如果覺得理解起來有困難,那直接重點看本文中的描述性的文字,尤其是加粗部分的即可。另外文章最后有一張總結(jié)圖歸納和整理了全文內(nèi)容。


一、服務(wù)器的 listen

我們都知道,服務(wù)器在開始提供服務(wù)之前都需要先 listen 一下。但 listen 內(nèi)部究竟干了啥,我們平時很少去琢磨。


今天就讓我們詳細(xì)來看看,直接上一段 listen 時執(zhí)行到的內(nèi)核代碼。


//file: net/core/request_sock.c
int reqsk_queue_alloc(struct request_sock_queue *queue,
unsigned int nr_table_entries)

{
size_t lopt_size = sizeof(struct listen_sock);
struct listen_sock *lopt;

//計算半連接隊列的長度
nr_table_entries = min_t(u32, nr_table_entries, sysctl_max_syn_backlog);
nr_table_entries = ......

//為半連接隊列申請內(nèi)存
lopt_size  = nr_table_entries * sizeof(struct request_sock *);
if (lopt_size > PAGE_SIZE)
lopt = vzalloc(lopt_size);
else
lopt = kzalloc(lopt_size, GFP_KERNEL);

//全連接隊列頭初始化
queue->rskq_accept_head = NULL;

//半連接隊列設(shè)置
lopt->nr_table_entries = nr_table_entries;
queue->listen_opt = lopt;
......
}
在這段代碼里,內(nèi)核計算了半連接隊列的長度。然后據(jù)此算出半連接隊列所需要的實際內(nèi)存大小,開始申請用于管理半連接隊列對象的內(nèi)存(半連接隊列需要快速查找,所以內(nèi)核是用哈希表來管理半連接隊列的,具體在 listen_sock 下的 syn_table 下)。最后將半連接隊列掛到了接收隊列 queue 上。


另外 queue->rskq_accept_head 代表的是全連接隊列,它是一個鏈表的形式。在 listen 這里因為還沒有連接,所以將全連接隊列頭 queue->rskq_accept_head 設(shè)置成 NULL。


當(dāng)全連接隊列和半連接隊列中有元素的時候,他們在內(nèi)核中的結(jié)構(gòu)圖大致如下。



在服務(wù)器 listen 的時候,主要是進(jìn)行了全/半連接隊列的長度限制計算,以及相關(guān)的內(nèi)存申請和初始化。全/連接隊列初始化了以后才可以相應(yīng)來自客戶端的握手請求。


二、客戶端 connect

客戶端通過調(diào)用 connect 來發(fā)起連接。在 connect 系統(tǒng)調(diào)用中會進(jìn)入到內(nèi)核源碼的 tcp_v4_connect。


//file: net/ipv4/tcp_ipv4.c
int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
//設(shè)置 socket 狀態(tài)為 TCP_SYN_SENT
tcp_set_state(sk, TCP_SYN_SENT);

//動態(tài)選擇一個端口
err = inet_hash_connect(
本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。

嵌入式客棧

132 篇文章

關(guān)注

發(fā)布文章

編輯精選

技術(shù)子站

關(guān)閉