Java Socket超時淺析

套接字或插座(socket)是一種軟件形式的抽象,用於表達兩臺機器間一個鏈接的「終端」。針對一個特定的鏈接,每臺機器上都有一個「套接字」,能夠想象它們之間有一條虛擬的「線纜」。JAVA 有兩個基於數據流的套接字類:ServerSocket,服務器用它「偵聽」進入的鏈接;Socket,客戶端用它初始一次鏈接。偵聽套接字只能接收新的鏈接請求,不能接收實際的數據包。
    套接字是基於TCP/IP實現的,它是用來提供一個訪問TCP的服務接口,或者說套接字socket是TCP的應用編程接口API,經過它應用層就能夠訪問TCP提供的服務。
    在JAVA中,咱們用ServerSocket、Socket類建立一個套接字鏈接,從套接字獲得的結果是一個InputStream以及OutputStream對象,以便將鏈接做爲一個IO流對象對待。經過IO流能夠從流中讀取數據或者寫數據到流中,讀寫IO流會有異常IOException產生。
    套接字底層是基於TCP的,因此socket的超時和TCP超時是相同的。下面先討論套接字讀寫緩衝區,接着討論鏈接創建超時、讀寫超時以及JAVA套接字編程的嵌套異常捕獲和一個超時例子程序的抓包示例。
一、socket讀寫緩衝區
    一旦建立了一個套接字實例,操做系統就會爲其分配緩衝區以存放接收和要發送的數據。編程

 JAVA能夠設置讀寫緩衝區的大小-setReceiveBufferSize(intsize), setSendBufferSize(int size)。
    向輸出流寫數據並不意味着數據實際上已經被髮送,它們只是被複制到了發送緩衝區隊列SendQ,就是在Socket的OutputStream上調用 flush()方法,也不能保證數據可以當即發送到網絡。真正的數據發送是由操做系統的TCP協議棧模塊從緩衝區中取數據發送到網絡來完成的。
    當有數據從網絡來到時,TCP協議棧模塊接收數據並放入接收緩衝區隊列RecvQ,輸入流InputStream經過read方法從RecvQ中取出數據。
二、socket鏈接創建超時
    socket鏈接創建是基於TCP的鏈接創建過程。TCP的鏈接須要經過3次握手報文來完成,開始創建TCP鏈接時須要發送同步SYN報文,而後等待確認報文SYN+ACK,最後再發送確認報文ACK。TCP鏈接的關閉經過4次揮手來完成,主動關閉TCP鏈接的一方發送FIN報文,等待對方的確認報文;被動關閉的一方也發送FIN報文,然等待確認報文。服務器

 正在等待TCP鏈接請求的一端有一個固定長度的鏈接隊列,該隊列中的鏈接已經被TCP接受(即三次握手已經完成),但尚未被應用層所接受。TCP接受一個鏈接是將其放入這個鏈接隊列,而應用層接受鏈接是將其從該隊列中移出。應用層能夠經過設置backlog變量來指明該鏈接隊列的最大長度,即已被TCP接受而等待應用層接受的最大鏈接數。
    當一個鏈接請求SYN到達時,TCP肯定是否接受這個鏈接。若是隊列中還有空間,TCP模塊將對SYN進行確認並完成鏈接的創建。但應用層只有在三次握手中的第三個報文收到後纔會知道這個新鏈接。若是隊列沒有空間,TCP將不理會收到的SYN。
    若是應用層不能及時接受已被TCP接受的鏈接,這些鏈接可能佔滿整個鏈接隊列,新的鏈接請求可能不被響應而會超時。若是一個鏈接請求SYN發送後,一段時間後沒有收到確認SYN+ACK,TCP會重傳這個鏈接請求SYN兩次,每次重傳的時間間隔加倍,在規定的時間內仍沒有收到SYN+ACK,TCP將放棄這個鏈接請求,鏈接創建就超時了。
    JAVA Socket鏈接創建超時和TCP是相同的,若是TCP創建鏈接時三次握手超時,那麼致使Socket鏈接創建也就超時了。能夠設置Socket鏈接創建的超時時間-
    connect(SocketAddress endpoint, inttimeout)
    若是在timeout內,鏈接沒有創建成功,在TimeoutException異常被拋出。若是timeout的值小於三次握手的時間,那麼Socket鏈接永遠也不會創建。
    不一樣的應用層有不一樣的鏈接創建過程,Socket的鏈接創建和TCP同樣-僅僅須要三次握手就完成鏈接,但有些應用程序須要交互不少信息後才能成功創建鏈接,好比Telnet協議,在TCP三次握手完成後,須要進行選項協商以後,Telnet鏈接才創建完成。
三、socket讀超時
    若是輸入緩衝隊列RecvQ中沒有數據,read操做會一直阻塞而掛起線程,直到有新的數據到來或者有異常產生。調用setSoTimeout(int timeout)能夠設置超時時間,若是到了超時時間仍沒有數據,read會拋出一個SocketTimeoutException,程序須要捕獲這個異常,可是當前的socket鏈接仍然是有效的。
    若是對方進程崩潰、對方機器忽然重啓、網絡斷開,本端的read會一直阻塞下去,這時設置超時時間是很是重要的,不然調用read的線程會一直掛起。
    TCP模塊把接收到的數據放入RecvQ中,直到應用層調用輸入流的read方法來讀取。若是RecvQ隊列被填滿了,這時TCP會根據滑動窗口機制通知對方不要繼續發送數據,本端中止接收從對端發送來的數據,直到接收者應用程序調用輸入流的read方法後騰出了空間。
四、socket寫超時
    socket的寫超時是基於TCP的超時重傳。超時重傳是TCP保證數據可靠性傳輸的一個重要機制,其原理是在發送一個數據報文後就開啓一個計時器,在必定時間內若是沒有獲得發送報文的確認ACK,那麼就從新發送報文。若是從新發送屢次以後,仍沒有確認報文,就發送一個復位報文RST,而後關閉TCP鏈接。首次數據報文發送與復位報文傳輸之間的時間差大約爲9分鐘,也就是說若是9分鐘內沒有獲得確認報文,就關閉鏈接。可是這個值是根據不一樣的TCP協議棧實現而不一樣。
    若是發送端調用write持續地寫出數據,直到SendQ隊列被填滿。若是在SendQ隊列已滿時調用write方法,則write將被阻塞,直到 SendQ有新的空閒空間爲止,也就是說直到一些字節傳輸到了接收者套接字的RecvQ中。若是此時RecvQ隊列也已經被填滿,全部操做都將中止,直到接收端調用read方法將一些字節傳輸到應用程序。
    當Socket的write發送數據時,若是網線斷開、對端進程崩潰或者對端機器重啓動,TCP模塊會重傳數據,最後超時而關閉鏈接。下次如再調用write會致使一個異常而退出。
    Socket寫超時是基於TCP協議棧的超時重傳機制,通常不須要設置write的超時時間,也沒有提供這種方法。網絡

相關文章
相關標籤/搜索