Java網絡通信-第21章
1.網絡程序設計基礎
網絡程序設計基礎涵蓋了許多方面,包括網絡協議、Web開發、數據庫連接、安全性等。
1.1局域網與互聯網
局域網(LAN)與互聯網(Internet)是兩個不同的概念,它們分別用于描述不同范圍內的網絡連接。
- 局域網(LAN):
- 定義: 局域網是指在一個相對較小的地理范圍內連接在一起的計算機網絡,如家庭、學校、辦公室或公司。
- 范圍: 通常,局域網覆蓋的范圍限制在一個建筑物或者一組相鄰的建筑物內。
- 連接設備: 局域網內的設備可以通過局域網技術(如以太網、Wi-Fi等)相互通信,共享資源,比如打印機、文件等。
- 互聯網(Internet):
- 定義: 互聯網是一個全球性的計算機網絡,它連接了世界上的數以億計的設備,允許它們之間進行通信和數據交換。
- 范圍: 互聯網沒有地理限制,覆蓋全球范圍。
- 連接設備: 互聯網連接了各種設備,包括個人計算機、智能手機、服務器等。它是通過一系列的互聯網服務提供商(ISP)相互連接起來的。
- 關系:
- 局域網是構建在較小的地理范圍內,用于組織內部設備之間的通信。
- 互聯網是一個覆蓋全球的網絡,將不同地理位置的局域網連接起來,使得全球范圍內的設備能夠相互通信。
- 通信方式:
- 局域網內部的通信通常更快、更可靠,而且通常不需要經過公共互聯網。
- 互聯網通信需要經過公共的基礎設施,如因特網服務提供商(ISP),數據在全球范圍內傳輸。
1.2網絡協議
網絡協議是計算機網絡中設備之間進行通信和數據交換的規則和約定。它們定義了數據的格式、傳輸方法、錯誤檢測等方面的規范,確保不同廠商和不同類型的設備能夠在網絡中互相通信。
1.IP協議
IP(Internet Protocol)是計算機網絡中的一種基礎協議,用于在網絡中傳輸數據包。IP協議定義了數據包的格式和規則,確保數據在網絡中正確傳遞。目前廣泛使用的IP版本有兩個:IPv4(Internet Protocol version 4)和IPv6(Internet Protocol version 6)。
IPv4(Internet Protocol version 4):
- 地址格式: IPv4地址是32位的,通常以點分十進制表示,如
192.168.0.1
。 - 地址空間: IPv4提供了大約42億個唯一的地址,由于互聯網的快速發展,IPv4地址空間已經不夠用,導致IPv4地址枯竭問題。
- 子網: 為了更好地管理IP地址,IPv4引入了子網的概念,允許將一個大的IP地址塊劃分成多個小的子網。
- 私有地址范圍: IPv4定義了一些私有地址范圍,例如
192.168.0.0/16
,這些地址通常用于內部網絡。
IPv6(Internet Protocol version 6):
- 地址格式: IPv6地址是128位的,通常以冒號分隔的十六進制表示,如
2001:0db8:85a3:0000:0000:8a2e:0370:7334
。 - 地址空間: IPv6提供了遠遠超過IPv4的地址空間,理論上足夠支持未來互聯網的發展需求。
- 簡化報頭: IPv6在報頭中做了一些簡化,減少了一些字段,提高了路由和轉發效率。
- 無需NAT: 由于IPv6地址空間足夠大,通常無需使用網絡地址轉換(NAT)技術,簡化了網絡配置和管理。
共同點:
- 數據包傳輸: IPv4和IPv6都是網絡層協議,負責將數據包從源主機傳輸到目標主機。
- 逐跳路由: 在網絡中,數據包通過一系列的路由器逐跳傳輸,最終到達目標主機。
由于IPv4地址枯竭的問題,全球范圍內正在逐漸過渡到IPv6。IPv6提供了更大的地址空間,更好的路由和轉發效率,以及更簡化的網絡配置。在實際應用中,IPv4和IPv6可能同時存在,而且網絡設備和應用程序需要同時支持這兩種協議,這被稱為雙棧(Dual Stack)支持。
2.TCP與UDP協議
TCP(Transmission Control Protocol)和UDP(User Datagram Protocol)是兩種常見的傳輸層協議,它們定義了在計算機網絡中數據如何被傳輸和接收的規則。以下是它們的主要特點和區別:
TCP(Transmission Control Protocol):
- 連接導向: TCP是面向連接的協議,建立了一條雙向的通信路徑,確保數據的可靠傳輸。
- 可靠性: TCP提供可靠的數據傳輸,通過使用確認機制和重傳機制來確保數據的完整性和順序性。
- 流控制: TCP通過流控制機制來控制數據的發送速率,防止接收方被過多的數據淹沒。
- 擁塞控制: TCP使用擁塞控制算法,當網絡擁塞時會調整發送速率,以避免網絡的過載。
- 面向字節: TCP將數據視為一連串的字節流,而非獨立的數據包。
UDP(User Datagram Protocol):
- 無連接: UDP是面向無連接的協議,通信雙方在傳輸數據前不需要建立連接。
- 不可靠性: UDP不提供可靠的數據傳輸,不保證數據的完整性和順序性,也不提供重傳機制。
- 無流控制: UDP不提供流控制機制,因此發送方會一直以固定速率發送數據,而不會根據接收方的處理能力進行調整。
- 無擁塞控制: UDP不具備擁塞控制機制,因此在網絡擁塞時可能會丟失數據包。
- 面向數據報: UDP將數據視為獨立的數據包,而非字節流。
共同點:
- 端口: TCP和UDP都使用端口來標識應用程序,使得數據能夠正確地交付到目標應用。
- 傳輸層協議: TCP和UDP都屬于傳輸層協議,負責將數據從一個端點傳輸到另一個端點。
選擇使用場景:
- TCP適用于:
- 需要可靠數據傳輸的應用,如文件傳輸、電子郵件等。
- 需要確保數據按順序到達的應用,如視頻流、Web頁面加載等。
- UDP適用于:
- 實時性要求較高,能夠容忍一定程度的數據丟失的應用,如實時語音、視頻通話等。
- 需要較低的通信延遲,對數據可靠性要求不高的應用,如在線游戲。
3.端口與套接字
端口(Port)和套接字(Socket)是計算機網絡中重要的概念,它們在網絡通信中起到關鍵的作用。
端口(Port):
- 定義: 端口是一個邏輯上的概念,用于標識一臺計算機中運行的特定應用程序或服務。它允許一臺計算機上的不同應用程序通過網絡進行通信,每個應用程序都被分配一個唯一的端口號。
- 范圍: 端口號范圍從0到65535,其中0到1023的端口號被稱為「系統端口」或「知名端口」,它們通常用于一些常見的服務(如HTTP使用的80端口,FTP使用的21端口等)。1024到49151的端口號是「注冊端口」,用于用戶應用。49152到65535的端口號是「動態或私有端口」,它們用于動態分配,通常由客戶端應用程序使用。
套接字(Socket):
- 定義: 套接字是支持網絡通信的編程接口,它允許進程通過網絡發送和接收數據。套接字由IP地址和端口號組成,標識了通信中的兩個端點。套接字可以用于在同一臺計算機上的進程間通信,也可以用于在不同計算機之間進行網絡通信。
- 類型: 套接字可以分為兩種類型:流套接字(Stream Socket)和數據報套接字(Datagram Socket)。
- 流套接字: 提供面向連接的、可靠的、基于字節流的通信,使用TCP協議。
- 數據報套接字: 提供無連接的、不可靠的、基于數據包的通信,使用UDP協議。
套接字編程通常包括創建套接字、綁定到一個特定的IP地址和端口號、監聽連接請求(對于服務器端)、接受連接(對于服務器端)、連接到服務器(對于客戶端)、發送和接收數據等步驟。
2.TCP程序
TCP(Transmission Control Protocol)是一種面向連接的、可靠的協議,常用于網絡通信中。
2.1InetAddress類
InetAddress
類是 Java 中用于表示 IP 地址的類。它位于 java.net
包中,提供了一種在網絡上標識主機的方法。InetAddress
類主要用于獲取主機的 IP 地址和主機名。
以下是 InetAddress
類的一些主要方法和用法:
-
獲取本地主機的 InetAddress 對象:
InetAddress localHost = InetAddress.getLocalHost();
-
根據主機名獲取 InetAddress 對象:
String hostName = "example.com"; InetAddress address = InetAddress.getByName(hostName);
-
獲取主機的 IP 地址:
byte[] ipAddress = address.getAddress(); // 返回字節數組形式的 IP 地址 String ipAddressStr = address.getHostAddress(); // 返回字符串形式的 IP 地址
-
獲取主機名:
String hostName = address.getHostName();
-
判斷是否是 IP 地址:
boolean isIP = InetAddressUtils.isIPv4Address(ipAddressStr); // 可以使用 InetAddressUtils 類中的方法判斷 IPv4 或 IPv6 地址
InetAddress
類的使用可以幫助我們進行網絡編程中的主機信息獲取和處理。請注意,它主要用于獲取網絡上的主機信息,而不涉及與網絡通信的具體操作。如果需要進行網絡通信,通常需要使用 Socket
或 URL
等類。
2.2ServerSocket類
ServerSocket
類是 Java 中用于創建服務器套接字的類,它位于 java.net
包中。通過 ServerSocket
,你可以監聽特定端口,接受客戶端的連接請求,并與客戶端進行通信。以下是一些 ServerSocket
類的常見用法:
創建 ServerSocket 對象:
int port = 8080;
ServerSocket serverSocket = new ServerSocket(port);
接受客戶端連接:
Socket clientSocket = serverSocket.accept();
這個方法會阻塞程序的執行,直到有客戶端請求連接。一旦有連接請求,它會返回一個新的 Socket
對象,該對象用于與客戶端進行通信。
獲取輸入輸出流進行通信:
javaCopy codeInputStream inputStream = clientSocket.getInputStream();
OutputStream outputStream = clientSocket.getOutputStream();
使用這些流,你可以讀取來自客戶端的數據和向客戶端發送數據。
關閉 ServerSocket:
javaCopy code
serverSocket.close();
當服務器不再需要監聽新的連接時,可以關閉 ServerSocket
。
以下是一個簡單的示例,演示了 ServerSocket
的基本用法:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;public class SimpleServer {public static void main(String[] args) {int port = 8080;try (ServerSocket serverSocket = new ServerSocket(port)) {System.out.println("Server is listening on port " + port);// 等待客戶端連接Socket clientSocket = serverSocket.accept();System.out.println("Client connected");// 獲取輸入輸出流InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream();// 讀取客戶端發送的數據byte[] buffer = new byte[1024];int bytesRead = inputStream.read(buffer);String clientMessage = new String(buffer, 0, bytesRead);System.out.println("Client message: " + clientMessage);// 向客戶端發送響應String responseMessage = "Hello, client!";outputStream.write(responseMessage.getBytes());// 關閉連接clientSocket.close();} catch (IOException e) {e.printStackTrace();}}
}
注意,這只是一個簡單的例子,實際上,服務器通常需要在不同的線程中處理多個客戶端連接,以避免阻塞。此外,網絡編程涉及異常處理等更復雜的問題。
2.3TCP網絡程序設計
設計 TCP 網絡程序通常涉及兩個主要角色:服務器端和客戶端。TCP(傳輸控制協議)是一種面向連接的協議,它提供可靠的、有序的、基于字節流的雙向數據傳輸。下面分別介紹服務器端和客戶端的設計。
服務器端(TCP Server)
-
創建 ServerSocket 對象:
ServerSocket serverSocket = new ServerSocket(port);
-
等待客戶端連接:
Socket clientSocket = serverSocket.accept();
-
獲取輸入輸出流:
InputStream inputStream = clientSocket.getInputStream(); OutputStream outputStream = clientSocket.getOutputStream();
-
處理客戶端發送的數據:
// 讀取數據 byte[] buffer = new byte[1024]; int bytesRead = inputStream.read(buffer); String clientMessage = new String(buffer, 0, bytesRead);// 處理數據,執行業務邏輯// 發送響應 String responseMessage = "Hello, client!"; outputStream.write(responseMessage.getBytes());
-
關閉連接:
clientSocket.close();
-
異常處理: 在實際應用中,需要添加適當的異常處理,以確保程序在發生異常時能夠正確地處理和關閉資源。
客戶端(TCP Client)
-
創建 Socket 對象連接服務器:
Socket socket = new Socket(serverAddress, serverPort);
-
獲取輸入輸出流:
InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream();
-
發送數據給服務器:
// 發送數據 String message = "Hello, server!"; outputStream.write(message.getBytes());
-
接收服務器響應:
// 接收數據 byte[] buffer = new byte[1024]; int bytesRead = inputStream.read(buffer); String serverResponse = new String(buffer, 0, bytesRead); System.out.println("Server response: " + serverResponse);
-
關閉連接:
socket.close();
-
異常處理: 同樣,需要適當地處理可能出現的異常。
示例
以下是一個簡單的示例,演示了一個簡單的 TCP 服務器和客戶端:
TCP Server:
import java.io.*;
import java.net.*;public class TCPServer {public static void main(String[] args) {int port = 8080;try (ServerSocket serverSocket = new ServerSocket(port)) {System.out.println("Server is listening on port " + port);Socket clientSocket = serverSocket.accept();System.out.println("Client connected");InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream();byte[] buffer = new byte[1024];int bytesRead = inputStream.read(buffer);String clientMessage = new String(buffer, 0, bytesRead);System.out.println("Client message: " + clientMessage);String responseMessage = "Hello, client!";outputStream.write(responseMessage.getBytes());clientSocket.close();} catch (IOException e) {e.printStackTrace();}}
}
TCP Client:
import java.io.*;
import java.net.*;public class TCPClient {public static void main(String[] args) {String serverAddress = "localhost";int serverPort = 8080;try (Socket socket = new Socket(serverAddress, serverPort)) {InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream();String message = "Hello, server!";outputStream.write(message.getBytes());byte[] buffer = new byte[1024];int bytesRead = inputStream.read(buffer);String serverResponse = new String(buffer, 0, bytesRead);System.out.println("Server response: " + serverResponse);socket.close();} catch (IOException e) {e.printStackTrace();}}
}
這只是一個簡單的例子,實際上,我們可能需要在不同的線程中處理多個客戶端連接,以及添加更復雜的業務邏輯和異常處理。
運行結果
TCP Server:
TCP Client:
3.UDP程序
UDP(User Datagram Protocol)是一種無連接、不可靠、簡單的面向數據報的協議。UDP 提供了一種輕量級的數據傳輸機制,適用于一些對實時性要求較高、容忍數據丟失的應用場景。
3.1DatagramPacket類
DatagramPacket
類是 Java 中用于表示數據報包的類,通常用于 UDP 協議的通信。DatagramPacket
封裝了數據和目標主機信息,包括目標主機的 IP 地址和端口號。這類被用于在網絡上發送和接收數據報。
構造方法:
-
發送數據時的構造方法:
DatagramPacket(byte[] data, int length, InetAddress address, int port)
data
:要發送的數據,以字節數組形式提供。length
:要發送的數據的長度。address
:目標主機的 IP 地址。port
:目標主機的端口號。
-
接收數據時的構造方法:
javaCopy code DatagramPacket(byte[] data, int length)
data
:用于接收數據的字節數組。length
:接收數據的最大長度。
常用方法:
-
獲取數據:
byte[] getData()
-
獲取數據長度:
int getLength()
-
獲取數據報包的源地址:
InetAddress getAddress()
-
獲取數據報包的源端口號:
int getPort()
-
設置數據報包的目標地址和端口號:
setAddress(InetAddress address) setPort(int port)
3.2DatagramSocket類
DatagramSocket
類是 Java 中用于進行 UDP(User Datagram Protocol)通信的套接字類。它提供了在網絡上發送和接收數據報(DatagramPacket
)的方法。與 TCP 不同,UDP 是一種無連接、不可靠的協議,但它更加輕量且適用于一些實時性要求較高的應用場景。
構造方法:
-
創建 DatagramSocket 實例:
javaCopy code DatagramSocket socket = new DatagramSocket();
創建一個新的
DatagramSocket
實例,用于與其他主機進行 UDP 通信。 -
指定端口號創建 DatagramSocket:
DatagramSocket socket = new DatagramSocket(port);
創建一個監聽指定端口的
DatagramSocket
實例,用于接收數據。 -
指定本地地址和端口創建 DatagramSocket:
DatagramSocket socket = new DatagramSocket(port, InetAddress.getLocalHost());
創建一個綁定到指定本地地址和端口的
DatagramSocket
實例。
常用方法:
-
發送數據報:
send(DatagramPacket packet)
通過
DatagramSocket
發送數據報。 -
接收數據報:
receive(DatagramPacket packet)
通過
DatagramSocket
接收數據報。 -
設置超時時間:
setSoTimeout(int timeout)
設置
DatagramSocket
的超時時間,用于控制在接收時等待數據的最長時間。 -
關閉 DatagramSocket:
close()
關閉
DatagramSocket
,釋放相關資源。
3.3UDP網絡序設計
設計 UDP(User Datagram Protocol)網絡程序通常涉及兩個主要角色:服務器端和客戶端。UDP 是一種無連接、不可靠的協議,適用于對實時性要求較高、能夠容忍一定數據丟失的應用場景。以下是 UDP 服務器端和客戶端的基本設計步驟:
UDP 服務器端設計:
-
創建 DatagramSocket 對象:
DatagramSocket serverSocket = new DatagramSocket(port);
創建一個
DatagramSocket
實例來監聽指定端口。 -
創建 DatagramPacket 對象用于接收數據:
byte[] receiveData = new byte[1024]; DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
用于接收從客戶端發送過來的數據。
-
接收客戶端數據:
serverSocket.receive(receivePacket);
通過
DatagramSocket
接收數據報。 -
處理客戶端數據:
String clientMessage = new String(receivePacket.getData(), 0, receivePacket.getLength()); // 處理客戶端數據...
處理接收到的客戶端數據,執行相應的業務邏輯。
-
發送響應給客戶端(可選):
InetAddress clientAddress = receivePacket.getAddress(); int clientPort = receivePacket.getPort(); String responseMessage = "Hello, client!"; byte[] sendData = responseMessage.getBytes(); DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, clientAddress, clientPort); serverSocket.send(sendPacket);
如果需要,可以通過
DatagramSocket
發送響應給客戶端。 -
關閉 DatagramSocket:
serverSocket.close();
在不再需要監聽時關閉
DatagramSocket
。
UDP 客戶端設計:
-
創建 DatagramSocket 對象:
DatagramSocket clientSocket = new DatagramSocket();
創建一個
DatagramSocket
實例,用于發送和接收數據。 -
創建 DatagramPacket 對象用于發送數據:
String message = "Hello, server!"; byte[] sendData = message.getBytes(); InetAddress serverAddress = InetAddress.getByName(serverHost); int serverPort = 9876; // 服務器端口號 DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, serverAddress, serverPort);
用于將數據發送給服務器。
-
發送數據給服務器:
clientSocket.send(sendPacket);
通過
DatagramSocket
發送數據報給服務器。 -
創建 DatagramPacket 對象用于接收服務器響應:
byte[] receiveData = new byte[1024]; DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
用于接收從服務器返回的數據。
-
接收服務器響應:
clientSocket.receive(receivePacket); String serverResponse = new String(receivePacket.getData(), 0, receivePacket.getLength()); System.out.println("Server response: " + serverResponse);
通過
DatagramSocket
接收服務器的響應。 -
關閉 DatagramSocket:
clientSocket.close();
在不再需要發送和接收數據時關閉
DatagramSocket
。
示例:
以下是一個簡單的 UDP 服務器端和客戶端的示例:
UDP Server:
import java.net.DatagramPacket;
import java.net.DatagramSocket;public class UDPServer {public static void main(String[] args) {int port = 9876;try (DatagramSocket serverSocket = new DatagramSocket(port)) {System.out.println("Server is listening on port " + port);byte[] receiveData = new byte[1024];DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);serverSocket.receive(receivePacket);String clientMessage = new String(receivePacket.getData(), 0, receivePacket.getLength());System.out.println("Client message: " + clientMessage);// 處理客戶端數據...} catch (Exception e) {e.printStackTrace();}}
}
UDP Client:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;public class UDPClient {public static void main(String[] args) {String serverHost = "localhost";int serverPort = 9876;try (DatagramSocket clientSocket = new DatagramSocket()) {String message = "Hello, server!";byte[] sendData = message.getBytes();InetAddress serverAddress = InetAddress.getByName(serverHost);DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, serverAddress, serverPort);clientSocket.send(sendPacket);byte[] receiveData = new byte[1024];DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);clientSocket.receive(receivePacket);String serverResponse = new String(receivePacket.getData(), 0, receivePacket.getLength());System.out.println("Server response: " + serverResponse);} catch (Exception e) {e.printStackTrace();}}
}
在實際應用中,需要注意異常處理、超時機制、數據的序列化和反序列化等方面,以確保程序的穩定性和可靠性。