Network——Socket網路通訊機制以及實現舉例(TCP、UDP等)
1. 網路通訊與Socket前言
TCP/IP協議族中網路層的IP地址可以唯一標識網路中的主機,而傳輸層的埠可以唯一標識主機中的應用程式。Socket通常也稱作"套接字",用於描述IP地址和埠,是一個通訊鏈的控制代碼,可以用來實現不同虛擬機器或不同計算機之間的通訊。在Internet上的主機一般運行了多個服務軟體,同時提供幾種服務。每種服務都開啟一個Socket,並繫結到一個埠上,不同的埠對應於不同的服務。
Socket所支援的協議種類不止TCP/IP,因此兩者之間是沒有必然聯絡的。在Java環境下,Socket程式設計主要是指基於TCP/IP協議的網路程式設計。
2. 傳輸層協議
2.1 TCP
TCP
優缺點和應用場景
(1)可靠穩定,傳遞資料前,會有三次握手建立連線,面向有連線。
(2)傳遞資料時,有確認、視窗(流量控制)、重傳、擁塞控制。確認機制保證接收端每收到一個正確包都會迴應給傳送端,超時或者資料包不完整的話傳送端會重傳。
缺點也比較明顯了:
(1)傳遞資料前,建立連線需要耗時,傳遞資料時,確認、重傳、擁塞等會消耗大量時間以及CPU和記憶體等硬體資源。
(2)因為有確認機制、三次握手等機制,容易被人利用,實現DOS 、DDOS攻擊。
應用場景的話,當對網路通訊質量有要求的時候,比如整個資料要準確無誤的傳遞給對方,這往往用於一些要求可靠的應用,如HTTP、HTTPS、FTP等傳輸檔案的協議,POP、SMTP等郵件傳輸的協議。
TCP連線的三次握手和四次揮手機制
為了控制本篇篇幅,關於TCP三次握手和四次揮手的知識請看這篇。
2.2 UDP
UDP是User Datagram Protocol的簡稱,是一種無連線的協議,每個資料報都是一個獨立的資訊,包括完整的源地址或目的地址,因此無需建立傳送方和接收方的連線,它在網路上以任何可能的路徑傳往目的地,因此能否到達目的地,到達目的地的時間以及內容的正確性、次序性都是
優缺點和應用場景
把TCP的缺點拿過來就成了UDP的優點,效率高了傳輸快了,且更安全。缺點也就是TCP的優點,即UDP不可靠不穩定、容易丟包。
當對網路通訊質量要求不高的時候,要求網路通訊速度能儘量的快,這時就可以使用UDP。 常見使用UDP協議的應用有QQ語音視訊等。
2.3 TCP對比UDP
UDP的特點如下:
(1)UDP面向無連結,不保證可靠性,可能會丟包,而且資料順序不能保證
(2)UDP面向報文,UDP對應用層交付下來的報文,既不合並,也不拆分,直接傳送
(3)UDP沒有擁塞控制
(4)UDP支援一對一、一對多、多對一和多對多的互動通訊
(5)UDP的首部開銷小,只有8位元組
TCP的特點:
(1)TCP是面向連線的,能夠保證可靠性,保證資料不丟失、不重複、並且按序到達
(2)TCP面向位元組流
(3)TCP根據對方給出的視窗和當前網路擁塞的程度來決定一個報文應該包含多少個位元組
(4)TCP連線只能用於一對一
(5)TCP的首部至少要20個位元組
3 基於Socket的Java網路程式設計
3.1 利用Socket建立連線的過程
套接字之間的連線過程分為三個步驟:伺服器監聽,客戶端請求,連線確認。
(1)伺服器監聽:伺服器端套接字處於等待連線的狀態,實時監控網路狀態,等待客戶端的連線請求。
(2)客戶端請求:指客戶端的套接字提出連線請求,要連線的目標是伺服器端的套接字。為此,客戶端的套接字必須首先描述它要連線的伺服器的套接字,指出伺服器端套接字的地址和埠號,然後就向伺服器端套接字提出連線請求。
(3)連線確認:當伺服器端套接字監聽到或者說接收到客戶端套接字的連線請求時,就響應客戶端套接字的請求,建立一個新的執行緒,把伺服器端套接字的描述發給客戶端,一旦客戶端確認了此描述,雙方就正式建立連線。而伺服器端套接字繼續處於監聽狀態,繼續接收其他客戶端套接字的連線請求。
3.2 Socket的Java實現
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class SocketManager {
public static void main(String[] args) {
SocketManager manager = new SocketManager();
manager.doListen();
}
public void doListen() {
ServerSocket server;
try {
server = new ServerSocket(9991);
while (true) {
Socket client = server.accept();
new Thread(new SSocket(client)).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
//伺服器程序
class SSocket implements Runnable {
Socket client;
public SSocket(Socket client) {
this.client = client;
}
public void run() {
DataInputStream input;
DataOutputStream output;
try {
input = new DataInputStream(client.getInputStream());
output = new DataOutputStream(client.getOutputStream());
String listMsg = input.readUTF();
output.writeUTF("Send : " + listMsg + " \r\n HelloVillage...");
System.out.println("Recive: " + listMsg);
listMsg = input.readUTF();
output.writeUTF("Send Second: " + listMsg + " \r\n HelloVillage...");
System.out.println("Recive Second: " + listMsg);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class SocketClient {
public static void main(String[] args) {
Socket socket = null;
try {
socket = new Socket("127.0.0.1", 9991);
//向伺服器端第一次傳送字串
OutputStream netOut = socket.getOutputStream();
DataOutputStream doc = new DataOutputStream(netOut);
DataInputStream in = new DataInputStream(socket.getInputStream());
//向伺服器端第二次傳送字串
doc.writeUTF("list");
String res = in.readUTF();
System.out.println(res);
doc.writeUTF("bye");
res = in.readUTF();
System.out.println(res);
doc.close();
in.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
}
}
}
}
}