談一談我的失敗的華為實習生面試


很幸運,在華為的上機考試中一道題,也沒有做,然后就去參加面試,鬼知道時怎么回事,方正比其他人幸運多了。

但可悲的是二面沒過,天哪,我知道是什么原因,我先簡單談一談我的面試經歷。

第一面的時候,起始很隨意,就考察一些基本知識,沒有什么難度,然后講一講自己的項目經歷,這些都挺簡單的,過了一面,然后做了下性格測試,因為以前的性格測試不合格。

哦,真是的,這詞做完只用了二十分鍾,比第一次做的時候快多了,然后性格測試也過了,旁邊一哥們被性格測試刷下去了。。。。

到了總和面的時候,確實沒有准備好,因為前一天,忙着把一個設備驅動的源碼分析了下,很少去復習一些基本知識,明知道三次握手,四次揮手肯定會考查,沒有准備好,那我先談一談三次握手和四次揮手吧,挺簡單的是,如果當時課上多想想,那面試的時候就不會那么尷尬了。

三次握手:

 TCP 的連接建立:

若A 是運行TCP客戶程序的務器,而B 為運行服務器程序的服務器。兩者最初的狀態都是CLOSEDE,狀態。

TCP 的連接是由服務器開始的。B運行服務器程序的進程首先創建傳輸控制塊,說白了,應該就是一大堆sock的結構體集合。首先我們關注這些狀態的集合,注意

這是用來表征連接狀態的,要與sock的狀態區分開來。如果分析過內核源碼會發現 連接狀態為 socket->sock->sk_state.

而描述 socket 的狀態集合如下:該集合用來描述 socket->state

這也就是我們經常所說的socket 編程,這樣我們就很容易理解。socket對於用戶層來講,可以直觀的看到socket 狀態的變化,而sock 是運行在內核中,對上層用戶透明。

好了到這兒,我們繼續TCP 連接的建立。

首先客戶端和服務端都處於CLOSED 狀態。首先服務器端創建socket ,我不太習慣使用套接字這個東西,簡單的講,我寧願將他描述為一個用於通信的描述符。

首先我們需要明白,TCP 和UDP 等協議的基本架構就是C/S 結構,簡單的客戶服務器模式,所以所謂的套接字編程,就是實現簡單的客戶服務器程序模型。

這也就是為什么要先運行服務器端,那服務器怎么知道,是哪個客戶向自己發起請求呢,好吧就是綁定端口,基於C/S 模式的通信程序都是這樣做的。服務器先綁定一個端口,然后

不斷監聽這個端口,來發現是否有客戶端發起請求,然后進入LISTEN狀態,這個端口是邏輯上的端口。若發現有客戶進行請求,則立即處理該請求。

運行客戶程序的進程也會創建傳輸控制塊,我更願意把它稱為sock ,哦,天哪,我感覺人們起的名字真奇妙。好吧,客戶端會創建 socket ,然后發出主動連接請求,該請求調用函數為

connect () 一堆參數,在寫socket 的時候你肯定見過的。這時候,服務器端得有個接受函數 accept 函數,若接收到客戶端的請求,服務器端的accep 函數會創建一個socket 與之通信,接下來,雙方就開始通信了,客戶服務器模式就是這樣的,挺簡單的吧,我們簡單說明一下這個過程中發生了什么,三次握手到底是怎么來的,為什么是三次,不是兩次一次呢,四次揮手,為什么不是三次,兩次,而是四次呢,我盡量講的簡單明白,並結合內核源碼加深理解吧。

首先,我先簡單的講一講tcp報文的格式吧 簡單的說就是tcp 頭部的構成,很簡單,看代碼。

struct tcphdr {
    __be16    source;// 16位的源端口
    __be16    dest;// 16位的目的端口
    __be32    seq;// 表示此次發送的數據在整個報文段中的起始字節數,序列號,在建立通信時,雙方使用一個隨機的序列號
    __be32    ack_seq;// 期望下一次收到的第一個數據字節的序號,我們經常說的ack
#if defined(__LITTLE_ENDIAN_BITFIELD)
    __u16    res1:4,// 保留位 兩個字節
        doff:4,//tcp 頭部的長度,指明在tcp 頭部包含多少個32位的字。即數據部分在本地報文段開始的偏移量,因為首部的長度是可變的,所以數據偏移字段的設立是必須的
        fin:1,//用於釋放一個連接,fin=1 表示欲發送的數據已發送完畢,並要求釋放傳輸連接。
        syn:1,// 同步序號,用來發起一個連接。當syn=1 ,而ack=0,時表示這個報文是一個連接請求報文,若對方同意連接,則會在應答報文中使得syn=1,ack=1,可見 syn=1,表示該報文是一個連接請求報文還是一個連接接受報文
        rst:1,//rst=1 ,表示tcp連接中出現了重大問題,必須釋放傳輸連接,而后再重建。該位可以用來拒絕一個非法的報文段或拒絕一個連接請求
        psh:1,//psh=1 表示請求接收端tcp 將此報文段立即送往應用層,而不是將它緩存起來直到整個緩沖區被填滿后再向上交付。
        ack:1,//當ack=1 時,確認好才有意義,tcp規定所有鏈接建立后,在連接后所有傳送的報文都必須吧ack 置為1
        urg:1,// 緊急指針,表示本報文的數據的緊急程度,urg=1 表示該報文應該具有高優先級,應盡快被發送。若接收端收到 urg=1 的報文段,他將利用緊急指針的值從報文段提取緊急數據,不再按序交給應用層需。
        ece:1,
        cwr:1;
#elif defined(__BIG_ENDIAN_BITFIELD)
    __u16    doff:4,
        res1:4,
        cwr:1,
        ece:1,
        urg:1,
        ack:1,
        psh:1,
        rst:1,
        syn:1,
        fin:1;
#else
#error    "Adjust your <asm/byteorder.h> defines"
#endif    
    __be16    window;// 窗口,用來控制流的大小。窗口值的的大小為 0-65535 ,通常由接收端確定,指的是發送報文段的一方的接收窗口大小。窗口值為0 ,表示接收端狀態不佳。
    __sum16    check;// 校驗和,該校驗和是整個報文段,包括首部和數據。
    __be16    urg_ptr;// 緊急指針,urg=1 時才有意義,他指出了緊急數據在報文中的位置,使得接收端能知道緊急數據的字節數。
};

 

 

連接建立主要分為以下三步:

1 客戶進程向服務器發出連接請求的報文,調用函數 tcp_connect () 發起主動連接,會創建一個tcp 報文,其中SYN=1,同時選擇一個 sn 即序列號,表明在即將傳輸的數據的第一個

字節序列號為i。TCP 標准規定,對 SYN =1 的報文段要賦一個序列號,即使這個報文沒有數據,此時客戶端進入 SYN_SENT 狀態。更准確的說是進入 TCP_SYN_SNET 狀態。

我們看一看內核源碼:

/* Build a SYN and send it off. */
int tcp_connect(struct sock *sk)
{
    struct tcp_sock *tp = tcp_sk(sk);
    struct sk_buff *buff;
    int err;

    tcp_connect_init(sk);

    if (unlikely(tp->repair)) {
        tcp_finish_connect(sk, NULL);
        return 0;
    }

    buff = sk_stream_alloc_skb(sk, 0, sk->sk_allocation);
    if (unlikely(!buff))
        return -ENOBUFS;

    tcp_init_nondata_skb(buff, tp->write_seq++, TCPHDR_SYN);
    tp->retrans_stamp = tcp_time_stamp;
    tcp_connect_queue_skb(sk, buff);
    tcp_ecn_send_syn(sk, buff);

    /* Send off SYN; include data in Fast Open. */
    err = tp->fastopen_req ? tcp_send_syn_data(sk, buff) :
          tcp_transmit_skb(sk, buff, 1, sk->sk_allocation);// 構造tcp 報文並發送
    if (err == -ECONNREFUSED)
        return err;

    /* We change tp->snd_nxt after the tcp_transmit_skb() call
     * in order to make this packet get counted in tcpOutSegs.
     */
    tp->snd_nxt = tp->write_seq;
    tp->pushed_seq = tp->write_seq;
    TCP_INC_STATS(sock_net(sk), TCP_MIB_ACTIVEOPENS);

    /* Timer for repeating the SYN until an answer. */
    inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
                  inet_csk(sk)->icsk_rto, TCP_RTO_MAX);
    return 0;
}

2 服務器接收到連接請求后,如果同意連接,則回答此報文。確認報文首部中的SYN=1,ACK=1,序列號為 seq=j,ack_seq=i+1.此時服務器端進入 SYN_RECV 狀態。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM