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);
連接無連接都可以使用