目錄
1.前言
2.正文
2.1TCP協議與UDP協議
2.2socket API進行網絡編程
2.2.1DatagramPacket類
2.2.1.1發送數據報
2.2.1.2接收數據報
2.2.1.3獲取數據報內容
2.2.1.4設置數據報內容
2.2.2DatagramSocket類
2.2.2.1構造方法
2.2.2.2常用方法
2.2.3具體代碼與解釋
3.小結
1.前言
哈嘍大家好吖,今天繼續給大家分享計算機網絡相關的知識,先講解傳輸層兩個重要的UDP和TCP協議,再講解在UDP中通過socket API來進行網絡編程。
2.正文
2.1TCP協議與UDP協議
在講解二者的概念與對比之前,先提前介紹幾個涉及到的概念:
有連接/無連接:指的是通信的A和B兩端是否互相保留對方的信息(即彼此之間是否知道是誰和它建立連接)。
可靠傳輸/不可靠傳輸:在網絡上,很容易出現丟包這種情況,因為光信號和電信號都可能受到外界干擾,如果在傳輸中識別到某些出錯的數據,打包丟掉,保證正常數據傳輸,這個叫做可靠傳輸,反之只管發送,不管中間的錯誤就是不可靠傳輸。
面向字節流/面向數據包:面向字節流,讀寫數據的時候,是以字節為單位面向數據報,讀寫數據的時候,以一個數據報為單位(不是字符)。
全雙工/半雙工:一個通信鏈路,支持雙向通信(能讀,也能寫) / 一個通信鏈路,只支持單向通信~~(要么讀,要么寫)
講解完以上概念,正文就可以順利開始~?
TCP(傳輸控制協議)和UDP(用戶數據報協議)是互聯網中最重要的兩種傳輸層協議,負責在應用程序之間傳輸數據。它們在設計目標、工作原理和應用場景上有顯著差異。以下是詳細的對比和解析:
TCP
特點:
面向連接:通信前需通過三次握手建立可靠連接,結束時通過四次揮手釋放連接。
可靠傳輸:通過確認應答(ACK)、超時重傳、數據排序、流量控制和擁塞控制確保數據完整、有序、無丟失。
全雙工通信:支持雙向數據流傳輸。
字節流模式:數據被看作無結構的字節流,由TCP自行分割和重組。
優點:
數據可靠性高,適合文件傳輸、網頁瀏覽等場景。
自動處理數據分片、重組和錯誤恢復。
缺點:
建立和釋放連接的開銷大。
傳輸延遲較高。
UDP
特點:
無連接:直接發送數據,無需建立連接。
不可靠傳輸:不保證數據到達、順序或完整性。
數據報模式:每個數據包獨立處理,保留邊界。
全雙工通信:支持雙向數據流傳輸。
優點:
傳輸速度快、延遲低。
資源占用少,適合實時性要求高的場景。
支持廣播和多播(一對多通信)。
缺點:
不保證數據可靠性,可能丟失或亂序。
2.2socket API進行網絡編程
接下來,就到了咱們的代碼環節了,我們將利用socket API來創建一個本地的UDP回顯服務器。那么何為回顯服務器呢?
UDP回顯服務器是一種簡單的網絡服務,它接收客戶端發送的UDP數據報,并將相同的數據報原樣返回給客戶端,即請求和響應相同。
2.2.1DatagramPacket類
在開始創建一個本地的UDP回顯服務器之前,需要先介紹一下我們要使用的DatagramPacket類:
DatagramPacket
類用于封裝UDP數據報。它表示一個UDP數據報的結構,包括數據內容、目標地址、目標端口、數據長度等信息。
2.2.1.1發送數據報
DatagramPacket(byte[] buf, int length, InetAddress address, int port)
buf
:要發送的數據,存儲在字節數組中。
length
:要發送的數據的長度。
address
:目標主機的 IP 地址,類型為InetAddress
。
port
:目標主機的端口號。
2.2.1.2接收數據報
DatagramPacket(byte[] buf, int length)
buf
:用于存儲接收到的數據的字節數組。
length
:字節數組的長度,即最大可接收的數據長度。
2.2.1.3獲取數據報內容
getData()
:返回數據報的數據,類型為byte[]
。
getLength()
:返回數據報的實際數據長度。
getAddress()
:返回發送方的 IP 地址(接收時使用)。
getPort()
:返回發送方的端口號(接收時使用)。
2.2.1.4設置數據報內容
setData(byte[] buf)
:設置數據報的數據。
setLength(int length)
:設置數據報的長度。
setAddress(InetAddress address)
:設置目標地址。
setPort(int port)
:設置目標端口。
2.2.2DatagramSocket類
DatagramSocket
類用于發送和接收UDP數據報。它表示一個UDP端點,可以綁定到本地端口,并通過網絡發送和接收數據報。
2.2.2.1構造方法
- DatagramSocket():系統隨機分配一個未使用的本地端口。
- DatagramSocket(int port):綁定指定的本地端口。
- DatagramSocket(int port, InetAddress address):綁定到指定的本地端口和地址。
2.2.2.2常用方法
- void send(DatagramPacket p):發送數據報。
- void receive(DatagramPacket p):接收數據報。
- void close():關閉套接字。
2.2.3具體代碼與解釋
下面是具有詳細的注釋的代碼與講解:
UdpEchoServer:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;public class UdpEchoServer {private DatagramSocket socket = null;public UdpEchoServer(int port) throws SocketException {// 指定了一個固定端口號, 讓服務器來使用.socket = new DatagramSocket(port);}public void start() throws IOException {// 啟動服務器System.out.println("服務器啟動");while (true) {// 循環一次, 就相當于處理一次請求.// 處理請求的過程, 典型的服務器都是分成三個步驟的.// 1. 讀取請求并解析.// DatagramPacket 表示一個 UDP 數據報. 此處傳入的字節數組, 就保存 UDP 的載荷部分.DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);socket.receive(requestPacket);// 把讀取到的二進制數據, 轉成字符串. 只是構造有效的部分.String request = new String(requestPacket.getData(), 0, requestPacket.getLength());// 2. 根據請求, 計算響應. (服務器最關鍵的邏輯)// 但是此處寫的是回顯服務器. 這個環節相當于省略了.String response = process(request);// 3. 把響應返回給客戶端// 根據 response 構造 DatagramPacket, 發送給客戶端.// 此處不能使用 response.length()DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,requestPacket.getSocketAddress());// 此處還不能直接發送. UDP 協議自身沒有保存對方的信息(不知道發給誰)// 需要指定 目的 ip 和 目的端口.socket.send(responsePacket);// 4. 打印一個日志System.out.printf("[%s:%d] req: %s, resp: %s\n", requestPacket.getAddress().toString(), requestPacket.getPort(),request, response);}}// 后續如果要寫別的服務器, 只修改這個地方就好了.// 不要忘記, private 方法不能被重寫. 需要改成 publicpublic String process(String request) {return request;}public static void main(String[] args) throws IOException {UdpEchoServer server = new UdpEchoServer(9090);server.start();}
}
?UdpEchoClient:
import java.io.IOException;
import java.net.*;
import java.util.Scanner;public class UdpEchoClient {private DatagramSocket socket = null;// UDP 本身不保存對端的信息, 就自己的代碼中保存一下private String serverIp;private int serverPort;// 和服務器不同, 此處的構造方法是要指定訪問的服務器的地址.public UdpEchoClient(String serverIp, int serverPort) throws SocketException {this.serverIp = serverIp;this.serverPort = serverPort;socket = new DatagramSocket();}public void start() throws IOException {Scanner scanner = new Scanner(System.in);while (true) {// 1. 從控制臺讀取用戶輸入的內容.System.out.println("請輸入要發送的內容:");if (!scanner.hasNext()) {break;}String request = scanner.next();// 2. 把請求發送給服務器, 需要構造 DatagramPacket 對象.// 構造過程中, 不光要構造載荷, 還要設置服務器的 IP 和端口號DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,InetAddress.getByName(serverIp), serverPort);// 3. 發送數據報socket.send(requestPacket);// 4. 接收服務器的響應DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);socket.receive(responsePacket);// 5. 從服務器讀取的數據進行解析, 打印出來.String response = new String(responsePacket.getData(), 0, responsePacket.getLength());System.out.println(response);}}public static void main(String[] args) throws IOException {UdpEchoClient client = new UdpEchoClient("127.0.0.1", 9090);client.start();}
}
?
實現核心思路:
服務器端:
打開一個UDP套接字(Socket)。
綁定到一個指定的端口,等待客戶端發送數據。
接收客戶端發送的UDP數據報。
將接收到的數據報原樣發送回客戶端。
重復上述過程,直到服務器關閉。
客戶端:
打開一個UDP套接字。
向服務器的指定端口發送數據報。
接收服務器返回的數據報。
比較發送和接收的數據是否一致,以驗證數據傳輸的完整性。
關閉套接字。
3.小結
今天的分享到這里就結束了,喜歡的小伙伴不要忘記點點贊點個關注,你的鼓勵就是對我最大的支持,加油!