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