SOCKET 協議
Socket 是傳輸層協議的具體軟件實現,它封裝了協議底層的復雜實現方法,為開發人員提供了便利的網絡連接。Socket 是網絡編程的基石,像 Http 的請求,MySQL 數據庫的連接等絕大部分的網絡連接都是基于 Socket 實現的。
1. 傳輸層協議
傳輸層有 TCP/UDP 兩種連接方式,所以對應的 Socket 也有兩種不同實現方式,掌握 Socket 的前提是了解清楚這兩種協議。
1.1 TCP 協議
面向連接,且具備順序控制和重發機制的可靠傳輸。他的可靠性是在于傳輸數據前要先建立連接,確保要傳輸的對方有響應才進行數據的傳輸。因此 TCP 有個經典的 3 次握手和 4 次揮手。
3 次握手
握手的目的是為了相互確認通信雙方的狀態都是正常的,沒有問題后才會進行正式的通信:
- 第一次握手:客戶端發送請求連接的消息給服務端,但發出去的消息是否到達并不清楚,要基于第二次握手的反饋;
- 第二次握手:服務端返回消息說明客戶端的消息收到了,此時它也糾結了,我的反饋信息對方有沒有收到,所以得依托第三次得握手;
- 第三次握手:客戶端反饋第二次握手的消息收到了。至此,通信雙發的發送消息和接受消息能力都得到了檢驗。
3 次握手的整個過程看著似乎有點過于謹慎,但是互聯網的初期網絡基礎設施是很落后的,丟包的概率非常大的。而且這個過程也只是在通信前期建立連接的時候進行,3 次握手過后就是正常的消息傳輸了。
4 次揮手
4 次揮手的目的跟 3 次握手目的是一樣的,謹慎的確保雙方消息狀態的準確:
- 第一次揮手:客戶端(服務端也可以主動斷開)向服務端說明想要關閉連接;
- 第二次揮手:服務端首先回復第一次的消息已經收到。但是并不是立馬關閉,因為此時服務端可能還有數據在傳輸中;
- 第三次揮手:待到數據傳輸都結束后,服務端向客戶端發出消息,告知一切都準備好了,我要斷開連接了;
- 第四次揮手:客戶端收到服務端的斷開信息后,給予確認。服務端收到確認后正式關閉??蛻舳俗约阂舶l出關閉信息,因為服務端已經關閉了無法確認,等到一段時間后客戶端正式關閉。
1.2 UDP 協議
UDP 是一種不可靠的傳輸機制,但是它的數據報文比 TCP 小,所以相同數據的傳輸 UDP 所需的帶寬更少,傳輸速度更快。它不要事先建立連接,知道對方的地址后直接數據包就扔過去,也不保證對方有沒有收到。
2. 連接方式
我們知道 TCP 數據發送前要建立連接,UDP 不需要,而 Socket 的連接又有如下區分:
2.1 長連接
- 兩個節點建立連接并保持不斷開的狀態;
- 兩邊雙向自由的進行數據傳輸;
- 直到數據全部交互結束才斷開。
2.2 短連接
- 節點 A 向節點 B 建立連接;
- A 發送數據給 B;
- 一條數據發送完立馬斷開。
2.3 適用場景
- 連接的建立需要開銷,頻繁的重建連接容易造成資源浪費,長連接適合客戶端和服務端都比較明確且傳輸數據比較大的情況;
- 每臺服務器的連接數都是有限制的,如果太多的長連接阻塞會影響到新連接的建立。Http 是一種短連接的方式,這樣有利于他處理高并發的請求。有一種
slowHttp
的攻擊,就是利用 Http 協議的特點,故意制造了一個很長的報文,然后每次發送很少量的數據,使請求一直占用最終耗盡服務器的連接。所以 Http 雖然是短連接,但是一般是等到數據傳輸完成才斷開的,我們應該根據具體業務設置 Http 請求的超時時間。
3. socket 編程
下面的代碼實現了一個 Socket 的服務端服務和一個客戶端,服務端在 6000 端口上面監聽連接,收到客戶端的連接后向客戶端發出 hello
問候語,客戶端打印出服務端發送過來的消息。
3.1 服務端
public class Server {
public static void main(String[] args) {
// 創建一個serverSocket監聽本地的6000端口
try(ServerSocket server = new ServerSocket (6000)) {
// 當有客戶端連接上就建立一個socket通道
Socket socket = server.accept();
OutputStream outputStream = socket.getOutputStream();
// 有客戶端連接上來就主動發送問候語
outputStream.write("hello".getBytes());
outputStream.flush();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.2 客戶端
public class Client {
public static void main(String[] args) {
// 根據{IP}+{port}與服務器建立連接
try( Socket socket=new Socket("127.0.0.1",6000)){
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
// 打印服務端發送的信息
System.out.println("Client:"+bufferedReader.readLine());
} catch (IOException e) {
e.printStackTrace();
}
}
}
4. websocket
Websocket 是一種升級版的 Http 服務,傳統的 Http 服務都是客戶端發起,服務端響應,而 Websocket 支持服務端向客戶端主動推送消息,增強了瀏覽器的交互場景。Websocket 也是應用層協議,跟 Http 一樣具體的實現都要基于 Socket,除此之外并沒有什么特殊。
5. 小結
幾乎所有的軟件都需要通信,而幾乎所有的通信都是基于 Socket 實現的,Socket 從軟件的層面屏蔽了傳輸層的細節,開發人員可以很方便的使用。Socket 起源于 Unix,而 Unix/Linux 基本哲學之一就是“一切皆文件”,使用的時候就打開,不用就關閉。