C++面試 一 socket、tcp相關知識總結

1. socket編程知識

    1> server和client函數調用流程

        server: socket、bind、listen、accept、send/recv

        client:socket、connect、recv/send

    2> listen函數第二個參數的作用

        當客戶端發起連接,發送SYN到服務端後,服務端的socket就處於SYN_RCVD狀態,並將其放入未完成隊列,然後服務端發送SYN ACK到客戶端,客服端應答ACK到服務端,此時服務端的socket編程ESTABLISHED狀態,並將其放入已完成隊列。

        調用accept之前,隊列的大小,就是listen函數的第二個參數,backlog的大小。

        調用accept是從已完成隊列中取出一個,這樣,未完成隊列的就可以進入已完成隊列。

        比如設置10,一次來了20個連接,會先處理10個,剩下的會拒絕, TCP是忽略這些請求,也就是不發送RST,這樣客戶端會重發SYN。

        設置了syncookie選項,則第二個參數無效,是爲了防範SYN Flood攻擊,原理是:在TCP服務器收到SYN包並返回SYN+ACK包時,不分配一個專門的數據區,二是根據這個SYN包計算一個cookie值,在收到ACK包時,根據cookie值檢查這個ACK包的合法性,如果合法,再分配專門的數據區進行處理。

    3> recv函數返回值是什麼含義

        recv先等待發送緩衝區的數據被協議傳送完畢,如果傳送中出現網絡錯誤,則recv函數返回SOCKET_ERROR錯誤

        如果接收緩衝區中沒有數據,或正在接受數據,recv會等待數據接收完畢,然後把接收緩衝區中的數據copy到buf中,recv僅僅是copy數據,真正的接收數據時協議完成的。

        阻塞和非阻塞的recv返回值沒有區別,

        > 0 是實際copy數據的大小

        = 0 是連接正常關閉

        < 0 是出錯,出錯時需要根據errno判斷需要做什麼處理

            errno == EINTR 操作被信號中斷,認爲連接正常,可以jxu接收數據

            errno == EWOULDBLOCK || EAGIN 阻塞或非阻塞socket,沒有數據或接收超時,也認爲連接正常,可以繼續接收

            errno == 其他錯誤認爲連接不正常,可以關閉連接

    4> 怎麼判斷字符接收完畢

        一般是自定義一個結束符,比如\n,

        或者由socket的接收和發送端約定好一種消息的結構,比如先發數據長度,再發實際數據

    5> accept返回的socket和listen中socket的關係

        在服務端創建的socket,用於listen和accept,這個socket不能用於和客戶端之間交互數據

        accept接受客戶端的連接後,返回一個新的socket,這個socket用於和這次連接的客戶端之間進行通信,使用getsockname和getpeername可以獲取到這個新socket的本端地址和對端地址。

2. TCP相關問題

    1> TCP的三次握手和四次揮手

        

   2> 爲什麼是三次握手?

        1. 防止已經過期的連接請求突然傳送到服務器,從而產生錯誤。客戶端發送連接請求SYN後,由於某個網絡節點擁塞導致很長時間後纔到達服務端,如果是兩次握手,服務端收到後,應答,就認爲建立連接了,但是在客戶端看來,這是一個已經失效的連接。

        2. 如果服務端收到連接請求後,應答的消息丟失,客戶端會進行重傳,如果數據一直丟失,服務端就會產生很多無效連接,佔用資源。 syn flood攻擊

    3> 爲什麼四次揮手?

        確保數據能夠傳輸完成。當客戶端主動發起關閉連接時,服務端收到FIN,僅僅表示客戶端沒有數據要發送了,服務端應答ACK,之後還可以繼續發送數據,發送完之後,再發FIN,表示自己也沒有數據要發送了,服務端的ACK和FIN多數情況下是可以分開發送的

    4> 關閉的發起端,最後爲什麼等待2MSL

        MSL是報文最大生存時間。發起端第四次揮手發送ACK之後,就進入到TIME_WAIT狀態,必須等待2MSL,是因爲如果最後一個ACK,對方沒有收到,那麼對方在超時之後,會重發第三次揮手的FIN包,發起端收到重發的FIN包後,可以再發送一個ACK應答。

        處於2MSL等待狀態TIME_WAIT時,任何遲到的報文都將被丟棄.

    5> TCP的多連接,高併發

        1. 單機支持最大TCP連接數

            accept返回的socket用來標識一個TCP連接,

            系統使用一個四元組來表示一個連接{local ip, local port, remote ip, remote port}

            客戶端最大的TCP連接數由端口數決定,端口是unsigned short,最大65535

           服務端監聽在一個固定端口,因此最大tcp連接數爲客戶端的ip數*端口數,大約爲2的32次方 * 2的16次方,也就是服務端最大支持2的48次方,但是實際上,linux系統限制連接數的因素主要是 內存和允許的文件描述符個數。

            ulinit -n 輸出1024表示一個進程最多隻能打開1024個文件

            臨時修改 ulimit -n 100000 重啓或用戶退出後失效

            永久修改 /etc/rc.local 添加 ulimit -SHn 100000

            全侷限制 cat /proc/sys/fs/file -nr 輸出9033 0 592026分別表示已經分配的文件句柄數、已分配但未使用的文件句柄數、最大文件句柄數

3. I/O多路複用select、poll、epoll機制

    IO多路複用就是通過一種機制,可以監視多個描述符,一旦某個描述符可讀可寫,就能夠通知程序進行響應的操作。select、poll、epoll本質上都是同步I/O,因爲它們都需要在讀寫事件就緒後,自己負責讀寫。異步IO的實現會負責把數據從內核拷貝到用戶空間。

    select實現

        1. 將fd_set從用戶空間寶貝到內核空間,並註冊回調函數

        2. 遍歷所有的fd,調用pollwait方法,主要工作是吧當前進程掛到設備的等待隊列中

        3. poll方法會返回描述符讀寫操作是否就緒的掩碼mask,並給fd_set賦值,遍歷完所有的fd,還沒有可讀寫的,就調用timeout進入睡眠,如果超過一定時間,則重新喚醒

       4. 將fd_set從內核空間拷貝到用戶空間

       select的缺點:1. 每次都把fd_set拷貝到內核,2.需要遍歷所有的fd,3. 最大支持1024

    poll的實現

        本質上和select類似,但是沒有最大文件描述符數量的限制,另外兩個缺點和select一樣

    epoll的實現

        epoll_create

        epoll_ctl 註冊新的時間到epoll句柄中時,會把fd拷貝到內核,而不是在epoll_wait的時候拷貝,保證了每個fd在整個過程中 只會拷貝一次,解決了select的第一個缺點。

        epoll_wait,不同於輪訓,epoll會把發生的可讀寫事件通過callback回調機制通知到用戶程序,相當於事件驅動

        epoll對文件描述符操作有兩種模式:LT水平模式和ET邊緣模式

        LT模式:當epoll_wait檢測到事件發生時通知應用程序,可以不處理,下一次調用epoll_wait時,會再次通知到應用程序。

        ET模式:當epoll_wait檢測到事件發生時,必須馬上處理,如果不處理,下一次調用epoll_wait時,將不再通知到應用。

        ET模式很大程度上減少了epoll事件被重複觸發的次數。因此效率要比 LT高。

    epoll的優點:

        1. 沒有最大併發連接的限制,能打開的fd上限,遠遠超過1024

        2. 效率高,不是輪詢的方式,而是隻有活躍的FD纔會調用callback

       3. 利用mmap文件映射內存,加速用戶空間和內核空間的消息傳遞,減少複製開銷

4. TCP的6個標識位的作用

16位源端口號 16位目的端口號
32位序列號
32位確認號

4位部首

長度

保留

6位

URG ACK PSH RST SYN FIN 16位窗口大小
16位校驗和 16位緊急指針
選項
數據

    源端口號:表示發送端端口號,16位

    目的端口號: 表示接收端端口號,16位

    序列號:表示發送數據的位置,32位,沒發送一次數據,就累加一次該數據字節數的大小。序號不會從0或1開始,而是建立連接時產生一個隨機數,通過SYN發給服務端,然後每次累加

    確認序號:表示下一次應該收到的數據序列號,32位,這個序號之前的數據都已經正常接收

    部首長度:長度4位,單位是4字節

    控制位:

        URG:緊急指針是否有效,爲1表示某一位要被優先處理

        ACK:確認號是否有效,一般爲1

        PSH:表示接收端應用程序立即從TCP緩衝區讀取數據

        RST:對方要求重新建立連接,復位

        SYN:請求建立連接,並在序列號字段設置初始值

        FIN:希望斷開連接

        窗口大小:接收緩衝區的大小

        檢驗和:發送端填充CRC校驗,TCP校驗TCP首部加數據,UDP只校驗數據

        緊急指針:只在URG爲1時有效,表示本報文段中緊急數據的指針

        選項:用於提高TCP傳輸性能,最大長度40字節

5. TCP的重發機制

    TCP使用兩套獨立的機制完成重傳,一是基於時間,二是基於確認信息

        1. 基於時間:設置一個定時器判斷數據是否長時間沒有應答,超時則重傳

        2. 基於確認信息:

            1. 快速重傳:當接收端收到失序報文時,要立即生成確認信息ACK,發送端會連續收到多個重複的ACK,此時發送端就可以判斷出丟失的數據,然後馬上重傳

            2. SACK選項:TCP雖然是有序的,但是基於IP層的傳輸是無序的,TCP的接收端可能收到的序列號是不連續的,使用SACK選項,保存接收方已經接收的序列號範圍,每個範圍被稱作一個SACK塊,起始和結束序列號都是32位,最大包含3個SACK塊。

    TCP滑動窗口機制

        窗口的大小是指無需等待確認,就可以繼續發送數據的最大值,比如窗口大小是400,每次發送100,則第2、3、4包,不需要等待應答,就可以連續發送

        當收到第1包的ACK後,滑動窗口後移,繼續發第5包

        系統開闢發送緩衝區來記錄哪些數據沒有應答,只有確認應答了的數據纔會從緩衝區中刪除

        窗口越大,網絡吞吐率越高

6. TCP擁塞控制使用的算法和具體過程

    擁塞的標誌:1. 重傳計時器超時 2 接收到三個重複確認(丟包)

    基於丟包的擁塞控制分爲四個階段:慢啓動、擁塞避免、快重傳、快恢復

    cwnd擁塞窗口、ssthresh慢開始門限、RTT往返時間

    1. 慢開始:剛剛加入網絡的連接,一點一點的提速,不要一上來就把路佔滿

        連接建立之後,先初始化cwnd=1,

        每收到一個ACK,cwnd++; 線性增長

        每當過了一個RTT,cwnd = cwnd*2; 指數增長

        爲防止cwnd增長過大引起網絡擁塞,設置一個慢開始門限

            當cwnd < ssthresh時,使用慢開始算法

            當cwnd = ssthresh時,既可以使用慢開始,也可以使用擁塞避免算法

            當cwnd > ssthresh時,使用擁塞避免算法

    2. 擁塞避免:(擁塞窗口線性增長)並不能避免擁塞,而是使網絡比較不容易出現擁塞

        沒收到一個ACK,cwnd = cwnd + 1/cwnd

        每過一個RTT,cwnd = cwnd + 1

        每經過一個RTT就把發送方的擁塞窗口加1

        無論在慢開始,還是在擁塞避免階段,只要發送發判斷網絡出現擁塞(沒有搜到確認),就把慢開始門限設置爲出現擁塞時的發送窗口大小的一半,然後把擁塞窗口設置爲1,執行慢開始算法

    3. 快重傳:要求接收方在收到一個失序報文段後就立即發出重複確認,而不是等自己要發數據時捎帶確認,(爲的是及早通知到發送方有數據丟失),快重傳算法規定:發送發只要一連收到三個重複確認,就應答立即重傳對方尚未收到的報文段,而不必等待設置的重傳計時器時間到。

    4. 快恢復:至少收到了3個重複的ACK,表明網絡沒那麼糟糕,可以執行快恢復算法。

        每收到一個重複的ACK,則cwnd = cwnd + 1;

        收到非重複的ACK時,cwnd = ssthresh,轉擁塞避免算法

        如果發生超時重傳,則ssthresh = cwnd/2; cwnd = 1進入慢啓動

7. SOCKET相關選項

1. SO_LINGGER選項用於控制close系統調用在關閉tcp連接時的行爲

l_onoff = 0, l_linger = 0 是默認情況,close將FIN送入發送緩衝區,並立即返回,發送緩衝區由系統接管,系統將數據發送完成後,釋放fd

l_onoff = 1, l_linger = 0 close會立即返回,發送緩衝區數據將丟棄,並給對端發送RST信號重置連接,close發送方直接進入close狀態,沒有time_wait狀態

l_onoff = 1, l_linger > 0 close的行爲取決於發送緩衝區是否有數據,和是否阻塞,如果是阻塞socket,close會等待linger的時間,直到發送緩衝區發送完畢,如果是非阻塞socket,close將立即返回,此時需要根據返回值和errno判斷結果

2. SO_RCVTIMEO設置socket接收(發送)超時,防止socket一直等待造成阻塞

3. SO_REUSEADDR選項,一般情況下,一個端口釋放後,等待2分鐘才能再次被使用, 這個選項讓端口釋放後立即就可以被再次使用。

8. recv recvfrom recvmsg

ssize_t recv(int sockfd, void * buf, size_t nbytes, int flags);

只能用在建立連接的socket上, 比如TCP使用recv,針對SOCK_STREAM套接字,接收的數據 可以比發送的少,MSG_WAITALL可以改變這種行爲,但這個標誌對包傳輸沒有影響

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

連接無連接都可以使用,可以獲取數據發送者的地址,通常用於udp,否則等同於recv

ssize_t recvmsg(int sockfd, struct msghdr * msg, int flag);

連接無連接都可以使用

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章