## 46 ServerSocket 源碼及面試題
## 引導語
上一小節我們學習了 Socket,本文我們來看看服務端套接字 API:ServerSocket,本文學習完畢之后,我們就可以把客服端 Socket 和服務端 ServerSocket 串聯起來,做一個真實的網絡通信的 demo 了。
### 1 類屬性
ServerSocket 的主要作用,是作為服務端的套接字,接受客戶端套接字傳遞過來的信息,并把響應回傳給客戶端,其屬性非常簡單,如下:
```
private boolean created = false;// 已創建 private boolean bound = false;// 綁定 private boolean closed = false;// 已關閉 // 底層的功能都依靠 SocketImpl 來實現 private SocketImpl impl;
```
ServerSocket 和 Socket 一樣,底層都是依靠 SocketImpl 的能力,而 SocketImpl 底層能力的實現基本上都是 native 方法實現的。
### 2 初始化
初始化大概可以分成兩類:無參構造器和有參構造器。
1. 無參構造器做的事情比較簡單,只指定了 SocketImpl 為 SocksSocketImpl 類;
2. 有參構造器有幾種初始化的形式,我們一起來看一下參數最多的構造器的源碼。
```
public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException {
// 默認是 SocksSocketImpl 實現 setImpl(); // 端口必須大于 0,小于 65535 if (port < 0 || port > 0xFFFF) throw new IllegalArgumentException( "Port value out of range: " + port); // 最大可連接數如果小于1,那么采取默認的 50 if (backlog < 1) backlog = 50; try { // 底層 navtive 方法 bind(new InetSocketAddress(bindAddr, port), backlog); } catch(SecurityException e) { close(); throw e; } catch(IOException e) { close(); throw e; } }
```
入參 port 指的是 ServerSocket 需要綁定本地那個端口。
入參 backlog 指的是服務端接受客戶端連接隊列的最大長度,這里需要注意的是,這里并不是限制客戶端連接的個數,我們在 JDK8 版本下做過實驗,我們把服務端的 backlog 設置成 1,并且變慢服務端的處理速度,當服務端并發請求過來時,并不是第二個請求過來就拒絕連接,我們在實際工作中,最好也不要用 backlog 來限制客戶端連接的個數。
還有點需要注意的是 backlog 小于 1 時,backlog 會被設置成默認的 50。
入參 InetAddress 表示 ip 地址。
### 3 bind
bind 方法主要作用是把 ServerSocket 綁定到本地的端口上,只有當我們使用無參構造器初始化 ServerSocket 時,才會用到這個方法,如果使用有參構造器的話,在初始化時就已經綁定到本地的端口上了。
配合無參構造器,一般我們這么用:
```
// 進行初始化 ServerSocket serverSocket = new ServerSocket(); // 進行綁定 serverSocket.bind(new InetSocketAddress("localhost", 7007));
```
### 4 accept
accept 方法主要是用來 ServerSocket 接受來自客戶端的套接字的,如果此時沒有來自客戶端的請求時,該方法就會一直阻塞,如果有通過 setSoTimeout 方法設置超時時間,那么 accept 只會在超時間內阻塞,過了超時時間就會拋出異常。
bind 和 accept 方法底層都是 native 方法實現,我們就不看源碼了。
### 5 面試題
#### 5.1 說說你對 Socket 和 ServerSocket 的理解?
答:兩者我們都可以稱為套接字,底層基于 TCP/UDP 協議,套接字對底層協議進行了封裝,讓我們使用時更加方便,Socket 常被使用在客戶端,用于向服務端請求數據和接受響應,ServerSocket 常用于在服務端,用于接受客戶端的請求并進行處理,兩者其底層使用都是依靠 SocketImpl 的子類的 native 方法。
#### 5.2 說說對 SocketOptions 中的 SO_TIMEOUT 的理解?
答:SocketOptions 類有很多屬性設置,比如 SOTIMEOUT 、SOLINGER 等等,這些問題說一下自己的理解即可,可以參考 《Socket 源碼及面試題》 中對各種屬性的解析。
#### 5.3 在構造 Socket 的時候,我可以選擇 TCP 或 UDP 么?應該如何選擇?
答:可以的,Socket 有三個參數的構造器,第三個參數表示你想使用 TCP 還是 UDP。
#### 5.4 TCP 有自動檢測服務端是否存活的機制么?有沒有更好的辦法?
答:有的,我們可以通過 setKeepAlive 方法來激活該功能,如果兩小時內,客戶端和服務端的套接字之間沒有任何通信,TCP 會自動發送 keepalive 探測給服務端,預測服務端有三種情況:
1. 服務端使用預期的 ACK 回復,說明一切正常;
2. 服務端回復 RST,表示服務端處于死機或者重啟狀態,終止連接;
3. 沒有得到服務端的響應(會嘗試多次),表示套接字已經關閉了。
但我們并不建議使用這種方式,我們可以自己起一個定時任務,定時的訪問服務端的特殊接口,如果服務端返回的數據和預期一致,說明服務端是存活的。
- 前言
- 第1章 基礎
- 01 開篇詞:為什么學習本專欄
- 02 String、Long 源碼解析和面試題
- 03 Java 常用關鍵字理解
- 04 Arrays、Collections、Objects 常用方法源碼解析
- 第2章 集合
- 05 ArrayList 源碼解析和設計思路
- 06 LinkedList 源碼解析
- 07 List 源碼會問哪些面試題
- 08 HashMap 源碼解析
- 09 TreeMap 和 LinkedHashMap 核心源碼解析
- 10 Map源碼會問哪些面試題
- 11 HashSet、TreeSet 源碼解析
- 12 彰顯細節:看集合源碼對我們實際工作的幫助和應用
- 13 差異對比:集合在 Java 7 和 8 有何不同和改進
- 14 簡化工作:Guava Lists Maps 實際工作運用和源碼
- 第3章 并發集合類
- 15 CopyOnWriteArrayList 源碼解析和設計思路
- 16 ConcurrentHashMap 源碼解析和設計思路
- 17 并發 List、Map源碼面試題
- 18 場景集合:并發 List、Map的應用場景
- 第4章 隊列
- 19 LinkedBlockingQueue 源碼解析
- 20 SynchronousQueue 源碼解析
- 21 DelayQueue 源碼解析
- 22 ArrayBlockingQueue 源碼解析
- 23 隊列在源碼方面的面試題
- 24 舉一反三:隊列在 Java 其它源碼中的應用
- 25 整體設計:隊列設計思想、工作中使用場景
- 26 驚嘆面試官:由淺入深手寫隊列
- 第5章 線程
- 27 Thread 源碼解析
- 28 Future、ExecutorService 源碼解析
- 29 押寶線程源碼面試題
- 第6章 鎖
- 30 AbstractQueuedSynchronizer 源碼解析(上)
- 31 AbstractQueuedSynchronizer 源碼解析(下)
- 32 ReentrantLock 源碼解析
- 33 CountDownLatch、Atomic 等其它源碼解析
- 34 只求問倒:連環相扣系列鎖面試題
- 35 經驗總結:各種鎖在工作中使用場景和細節
- 36 從容不迫:重寫鎖的設計結構和細節
- 第7章 線程池
- 37 ThreadPoolExecutor 源碼解析
- 38 線程池源碼面試題
- 39 經驗總結:不同場景,如何使用線程池
- 40 打動面試官:線程池流程編排中的運用實戰
- 第8章 Lambda 流
- 41 突破難點:如何看 Lambda 源碼
- 42 常用的 Lambda 表達式使用場景解析和應用
- 第9章 其他
- 43 ThreadLocal 源碼解析
- 44 場景實戰:ThreadLocal 在上下文傳值場景下的實踐
- 45 Socket 源碼及面試題
- 46 ServerSocket 源碼及面試題
- 47 工作實戰:Socket 結合線程池的使用
- 第10章 專欄總結
- 48 一起看過的 Java 源碼和面試真題