服務端socket

服務端Socket

1、什麼是服務端socket

服務端socket是一個運行在服務器上的,監聽入站TCP連接的連接。每個服務端socket監聽服務器上的一個特定端口。當遠程主機上的一個客戶端嘗試連接這個端口時,服務端就被喚醒,協商建立客戶端和服務器之間的連接,並返回一個常規的Socket對象,表示兩臺主機之間的socket。換句話說,服務端socket等待連接,而客戶端發起連接。一旦serverSocket建立了連接,服務器會使用一個常規的socket對象向客戶端發送數據。數據總是通過常規的socket傳輸。

 

2、服務端socket的生命週期。

(1)、使用一個ServerSocket()構造函數在一個特定端口創建一個新的ServerSocket

(2)ServerSocket使用其accept()方法監聽這個端口的入站連接。accept()會一直阻塞,直到客戶端嘗試建立連接,此時accept()將返回一個連接客戶端和服務器的scoket對象。

(3)、根據服務爲的類型,會調用socketgetInputStream()方法或getOutputStream()方法,或者這兩個方法都調用,以獲得客戶端的輸入和輸出流。

(4)、服務器和客戶端根據已經協商好的協議進行交互,直到要關閉連接。

(5)、服務器或客戶端關閉連接。

(6)、服務端返回到步驟(2),等待下一次連接。

 

 

注意:結束處理時一定要關閉socket。因爲客戶端不能依賴連接的另外一端關閉的socket,對於服務器尤其如此。客戶端可能超時或崩潰;用戶可能取消事務;網絡可能在流量高峯期間癱瘓;黑客可能發動拒絕服務攻擊。出於諸如此類的衆多原因,你不能依賴於客戶端關閉socket,即使協議有這個要求也不能完全相信客戶端一定會關閉socket

3、多線程服務器與線程池服務器

由於操作系統把指向某個特定端口的入站連接請求存儲在一個先進先出隊列中。默認地,java將這個隊列的長度設置爲50,不同的操作系統會有所不同。在這些操作系統中,java服務器socket的隊列長度將是操作系統所允許的最大值(小於或等於50)。隊列中填入的未處理的連接達到最大時,主機會拒絕這個端口上的額外的連接,直到隊列騰出新的位置位置。很多客戶端在首次連接被拒絕外還會多次嘗試建立連接,因此,能夠比新連接到來的速度更快的清空隊列尤爲重要。

這裏可以採用一個多線程的服務器爲每一個連接提供一個線程,與接收入棧的放入隊列的那個線程分開,這樣可以防止一個慢客戶端阻塞所有其他客戶端。(這裏開始不是很懂,請教了一下學長,打個比方:如果在只有主線程的情況下,隊列中所有已經出隊列並建立起連接的請求的執行順序也是按照連接的順序執行,也就是說如果一個客戶端跟服務器建立起連接後,在發送數據的過程中因爲某些原因,比如路由過多,某一路由上的網絡阻塞導致數據的傳輸很久才能到達對方,對於其他的連接請求就必須等待較長的時間,而越是在後面的連接請求執行的時間越是長,這樣使得後面接入的連接請求響應速度十分緩慢,造成不好得用戶體驗;並且在主線程中如果主線程既要處理客戶端發送過來的連接請求,又要處理已經連接並在傳輸數據的請求,當請求的數量十分龐大時,造成如I/O讀取的效率非常低。而多線程服務器將接入請求創建連接和處理業務邏輯相分離,使得兩個部分都能夠得到運行,提高了CPU的利用率和I/O讀取以及用戶體驗度等

 


 

以上示例使用try-with-resources來自動關閉服務器socket。不過,對於服務器socket接受的客戶端socket這裏有意沒有使用try-with-resources。這是因爲,客戶端socket避開了try塊,而放在一個單獨的線程中。如果使用了try-with-resources,主線程一旦到達while循環末尾就會關閉socket,而此時新生成的線程可能還沒有使用完這個socket,因爲連接是雙向的,所以不會關閉客戶端socket,只會關閉服務端socket

這個服務器上確實有可能發生一種拒絕服務器攻擊,由於示例會爲每一個連接生成一個新的線程,大量幾乎同時的入站連接可能導致極大的線程數量,最終導致jvm虛擬機會耗盡內存而崩潰。因此採用一個固定的線程池來限制可能的資源使用,不論負載多大,都不會崩潰。

 

 


 

4、用socket寫入服務器

大多數協議要求服務器同時讀寫客戶端。與之前一樣,先是創建一個連接,然後獲取InputStream讀取客戶端。另外使用OutputStream寫入客戶端。關鍵是要理解協議:明確何時寫入和何時讀取。

 

5、關閉服務器Socket

如果使用完一個服務器Socket,就應當將他關閉,特別是當程序還要繼續執行一段時間時更是如此。這會釋放端口,使其他希望使用這個端口的橫須可以使用。不要把關閉ServerSocket和關閉socket混淆。關閉ServerSocket會釋放本地主機的一個端口,允許另一個服務器綁定到這個端口。它還會中斷該ServerSocket已經接受的目前處於打開狀態的所有Socket

 

6、日誌

服務器要在無人看管的情況下運行很長時間。通常需要在很久之後對服務器中發生的情況進行調試,這很重要。由於這個原因,建議在存儲服務器日誌,至少要存儲一段時間的日誌。日誌記錄中通常包含兩個主要內容,一個是請求,一個是服務器錯誤。可以採用log4j或者Apache Commons Loggin之類的第三方日誌庫進行記錄。

 

7、構造服務器Socket

ServerSocket(int port);

ServerSocket(int port, int queueLength);

ServerSocket(int port, int queueLength,InetAdress bindAddress);

ServerSocket();

8Socket選項

對於服務端Socket有以下三個選項,SO_TIMEOUT(服務器超時時間),SO_REUSEADDR(確定是否允許一個新的Socket是否綁定到之前使用過的一個端口,而此時可能還有一些發送到原Socket的數據正在網絡上傳輸,此時要注意新端口是否會接收到這些沒有用的數據,這跟客戶端Socket十分類似),SO_RCVBUF(設置服務端接收的客戶端Socket默認接收緩衝區大小)。

 

 

 

發佈了40 篇原創文章 · 獲贊 25 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章