高性能网络编程1----accept建立连接
轉 http://taohui.org.cn/tcpperf1.html ?陶輝 taohui.org.cn
?
回到應用層,往往只需要調用類似于accept的API就可以建立TCP連接。建立連接的流程大家都了解--三次握手,它如何與accept交互呢?下面以一個不太精確卻通俗易懂的圖來說明之:
研究過backlog含義的朋友都很容易理解上圖。這兩個隊列是內核實現的,當服務器綁定、監聽了某個端口后,這個端口的SYN隊列和ACCEPT隊列就建立好了。客戶端使用connect向服務器發起TCP連接,當圖中1.1步驟客戶端的SYN包到達了服務器后,內核會把這一信息放到SYN隊列(即未完成握手隊列)中,同時回一個SYN+ACK包給客戶端。一段時間后,在較中2.1步驟中客戶端再次發來了針對服務器SYN包的ACK網絡分組時,內核會把連接從SYN隊列中取出,再把這個連接放到ACCEPT隊列(即已完成握手隊列)中。而服務器在第3步調用accept時,其實就是直接從ACCEPT隊列中取出已經建立成功的連接套接字而已。
現有我們可以來討論應用層組件:為何有的應用服務器進程中,會單獨使用1個線程,只調用accept方法來建立連接,例如tomcat;有的應用服務器進程中,卻用1個線程做所有的事,包括accept獲取新連接。
原因在于:首先,SYN隊列和ACCEPT隊列都不是無限長度的,它們的長度限制與調用listen監聽某個地址端口時傳遞的backlog參數有關。既然隊列長度是一個值,那么,隊列會滿嗎?當然會,如果上圖中第1步執行的速度大于第2步執行的速度,SYN隊列就會不斷增大直到隊列滿;如果第2步執行的速度遠大于第3步執行的速度,ACCEPT隊列同樣會達到上限。第1、2步不是應用程序可控的,但第3步卻是應用程序的行為,假設進程中調用accept獲取新連接的代碼段長期得不到執行,例如獲取不到鎖、IO阻塞等。
那么,這兩個隊列滿了后,新的請求到達了又將發生什么?
若SYN隊列滿,則會直接丟棄請求,即新的SYN網絡分組會被丟棄;如果ACCEPT隊列滿,則不會導致放棄連接,也不會把連接從SYN列隊中移出,這會加劇SYN隊列的增長alt。所以,對應用服務器來說,如果ACCEPT隊列中有已經建立好的TCP連接,卻沒有及時的把它取出來,這樣,一旦導致兩個隊列滿了后,就會使客戶端不能再建立新連接,引發嚴重問題。
所以,如TOMCAT等服務器會使用獨立的線程,只做accept獲取連接這一件事,以防止不能及時的去accept獲取連接。
那么,為什么如Nginx等一些服務器,在一個線程內做accept的同時,還會做其他IO等操作呢?
這里就帶出阻塞和非阻塞的概念。應用程序可以把listen時設置的套接字設為非阻塞模式(默認為阻塞模式),這兩種模式會導致accept方法有不同的行為。對阻塞套接字,accept行為如下圖:
這幅圖中可以看到,阻塞套接字上使用accept,第一個階段是等待ACCEPT隊列不為空的階段,它耗時不定,由客戶端是否向自己發起了TCP請求而定,可能會耗時很長。 對非阻塞套接字,accept會有兩種返回,如下圖:
?
由?http://www.cnblogs.com/diegodu/p/3977739.html?可知,無論是阻塞還是非阻塞都是同步的,因為都要阻塞在從內核考到應用程序的過程上。
非阻塞套接字上的accept,不存在等待ACCEPT隊列不為空的階段,它要么返回成功并拿到建立好的連接,要么返回失敗。
所以,企業級的服務器進程中,若某一線程既使用accept獲取新連接,又繼續在這個連接上讀、寫字符流,那么,這個連接對應的套接字通常要設為非阻塞。原因如上圖,調用accept時不會長期占用所屬線程的CPU時間片,使得線程能夠及時的做其他工作。
總結
以上是生活随笔為你收集整理的高性能网络编程1----accept建立连接的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在 MySQL 中查找含有目标字段的表
- 下一篇: 数据表主键,外键