因爲工做並非很忙,閒暇之餘就讀了下tomcat的源代碼。我是從事java服務器開發工做的,大致的一些服務器線程模型我都是瞭解的。其大部分都是由一個線程調用監聽端口等待客戶端的連接,創建鏈接後再交由其餘的線程負責具體的網絡io操做。可tomcat竟然是用多個線程調用同一個ServerSocket實例的accept方法。我讀過mina也讀過netty的源碼,本身在大學時也寫過很多的基於socket通訊的程序,可是這種用法本身從未想過也從未見過。(恕本人咕嚕寡聞了,-_-|||)難免好奇,這麼作原來沒問題啊?可這麼作能有什麼好處嗎?java
要明白這麼作的道理,恐怖不得不去搞清楚套接字的accept方法底層到底幹了什麼,與TCP的三步握手又是什麼關係。我查了一些資料大致把這些搞明白了,在這裏記錄下來以增強本身的認識,也同時共享給哪些跟我同樣對socket的認識有所誤差的人。tomcat
1、首先說一下TCP三步握手的基本流程,以下圖:服務器
注意說到的兩個隊列:半鏈接隊列和徹底鏈接隊列網絡
2、socket操做和TCP的關係socket
也許好多人都是認爲,當咱們調用ServerSocket的accept方法時,是在監聽等待客戶端的鏈接,當客戶端請求服務器時就創立鏈接並返回與客戶端通訊的socket。實際呢卻不是這樣子的。咱們來看一個現象tcp
首先咱們啓動一個服務器程序,程序很簡單:代碼以下spa
public class Server { public static void main(String[] args) throws IOException { ServerSocket sSocket = new ServerSocket(3661, 2);//第二個參數的含義後邊會講解 System.in.read();//防止程序退出 } }
能夠看到,這裏咱們並無調用serversocket的accept方法。若是按上邊說的,accept方法是監聽端口等待客戶單的鏈接並完成鏈接的創建的話。咱們這個服務器程序根本沒法監聽端口並完成鏈接創建。接下來驗證一下,命令行
首先運行服務程序程序,而後咱們在命令行下用netstat -an查看一下端口狀況:線程
能夠看出端口依然被監聽了,並處於tcp三步握手的LISTEN狀態。接下來咱們鏈接試試能不能進行鏈接,啓動一個命令行窗口,執行:netty
telnet 127.0.0.1 3661。不要把這個關啦,再啓動一個命令行窗口執行如下上邊的netstat -an命令:
能夠看到,鏈接已成功創建了,TCP已經進入ESTABLISHED階段。看以看到,咱們不調用accept方法同樣能夠監聽端口並和客戶端創建鏈接。
由此咱們能夠證實accept方法並非監聽端口等待客戶端的鏈接並創建鏈接。那是什麼起到了監聽端口等待客戶單鏈接的做用的,經過看源碼咱們會發現構造方法調用了bind方法。
看到這裏想必都能知道,其實在咱們調用accept返回一個socket時,tcp早已把三步握手的過程完成了,鏈接已經都創建好了。那咱們的accept具體幹什麼事呢?還記得上邊提到的徹底鏈接隊列吧。accept就是從徹底鏈接隊列取出鏈接並封裝成socket。ServerSocket的構造方法參數backlog就是指定這個隊列的大小。好比上邊服務器程序咱們制定了backlog的值爲2,當咱們用三個命令行分別調用telnet 127.0.0.1 3661時,咱們會發現第三個鏈接請求將是沒法鏈接。