作者:qq_23473123
來源:CSDN
原文:https://blog.csdn.net/qq_23473123/article/details/51464272
版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請附上博文鏈接!
UDP簡介
UDP 是User Datagram Protocol的簡稱, 中文名是用戶數(shù)據(jù)報協(xié)議,是OSI(Open System Interconnection,開放式系統(tǒng)互聯(lián)) 參考模型中一種無連接的傳輸層協(xié)議,提供面向事務(wù)的簡單不可靠信息傳送服務(wù),IETF RFC 768是UDP的正式規(guī)范。UDP在IP報文的協(xié)議號是17。
UDP協(xié)議全稱是用戶數(shù)據(jù)報協(xié)議,在網(wǎng)絡(luò)中它與TCP協(xié)議一樣用于處理數(shù)據(jù)包,是一種無連接的協(xié)議。在OSI模型中,在第四層——傳輸層,處于IP協(xié)議的上一層。UDP有不提供數(shù)據(jù)包分組、組裝和不能對數(shù)據(jù)包進行排序的缺點,也就是說,當報文發(fā)送之后,是無法得知其是否安全完整到達的。UDP用來支持那些需要在計算機之間傳輸數(shù)據(jù)的網(wǎng)絡(luò)應(yīng)用。包括網(wǎng)絡(luò)視頻會議系統(tǒng)在內(nèi)的眾多的客戶/服務(wù)器模式的網(wǎng)絡(luò)應(yīng)用都需要使用UDP協(xié)議。UDP協(xié)議從問世至今已經(jīng)被使用了很多年,雖然其最初的光彩已經(jīng)被一些類似協(xié)議所掩蓋,但是即使是在今天UDP仍然不失為一項非常實用和可行的網(wǎng)絡(luò)傳輸層協(xié)議。
與所熟知的TCP(傳輸控制協(xié)議)協(xié)議一樣,UDP協(xié)議直接位于IP(網(wǎng)際協(xié)議)協(xié)議的頂層。根據(jù)OSI(開放系統(tǒng)互連)參考模型,UDP和TCP都屬于傳輸層協(xié)議。UDP協(xié)議的主要作用是將網(wǎng)絡(luò)數(shù)據(jù)流量壓縮成數(shù)據(jù)包的形式。一個典型的數(shù)據(jù)包就是一個二進制數(shù)據(jù)的傳輸單位。每一個數(shù)據(jù)包的前8個字節(jié)用來包含報頭信息,剩余字節(jié)則用來包含具體的傳輸數(shù)據(jù)。
UDP和TCP的優(yōu)缺點
TCP的優(yōu)缺點:
優(yōu)點:
可靠,穩(wěn)定
TCP的可靠體現(xiàn)在TCP在傳遞數(shù)據(jù)之前,會有三次握手來建立連接,而且在數(shù)據(jù)傳遞時,有確認、窗口、重傳、擁塞控制機制,在數(shù)據(jù)傳完后,還會斷開連接用來節(jié)約系統(tǒng)資源。
缺點:
慢,效率低,占用系統(tǒng)資源高,易被攻擊
TCP在傳遞數(shù)據(jù)之前,要先建連接,這會消耗時間,而且在數(shù)據(jù)傳遞時,確認機制、重傳機制、擁塞控制機制等都會消耗大量的時間,而且要在每臺設(shè)備上維護所有的傳輸連接,事實上,每個連接都會占用系統(tǒng)的CPU、內(nèi)存等硬件資源。
而且,因為TCP有確認機制、三次握手機制,這些也導(dǎo)致TCP容易被人利用,實現(xiàn)DOS、DDOS、CC等攻擊。
UDP的優(yōu)點:
快,比TCP稍安全
UDP沒有TCP的握手、確認、窗口、重傳、擁塞控制等機制,UDP是一個無狀態(tài)的傳輸協(xié)議,所以它在傳遞數(shù)據(jù)時非常快。沒有TCP的這些機制,UDP較TCP被攻擊者利用的漏洞就要少一些。但UDP也是無法避免攻擊的,比如:UDP Flood攻擊……
UDP的優(yōu)缺點:
不可靠,不穩(wěn)定
因為UDP沒有TCP那些可靠的機制,在數(shù)據(jù)傳遞時,如果網(wǎng)絡(luò)質(zhì)量不好,就會很容易丟包。
基于上面的優(yōu)缺點,那么:
什么時候應(yīng)該使用TCP:
當對網(wǎng)絡(luò)通訊質(zhì)量有要求的時候,比如:整個數(shù)據(jù)要準確無誤的傳遞給對方,這往往用于一些要求可靠的應(yīng)用,比如HTTP、HTTPS、FTP等傳輸文件的協(xié)議,POP、SMTP等郵件傳輸?shù)膮f(xié)議。
在日常生活中,常見使用TCP協(xié)議的應(yīng)用如下:
瀏覽器,用的HTTP
FlashFXP,用的FTP
Outlook,用的POP、SMTP
Putty,用的Telnet、SSH
QQ文件傳輸
…………
什么時候應(yīng)該使用UDP:
當對網(wǎng)絡(luò)通訊質(zhì)量要求不高的時候,要求網(wǎng)絡(luò)通訊速度能盡量的快,這時就可以使用UDP。
比如,日常生活中,常見使用UDP協(xié)議的應(yīng)用如下:
QQ語音
QQ視頻
TFTP
……
UDP 編程
主要用到兩個類DatagramPacket和DatagramSocket,下面分別介紹。
DatagramSocket
具體api見:http://www.javaweb.cc/help/JavaAPI1.6/index.html?java/nio/ReadOnlyBufferException.html
此類表示用來發(fā)送和接收數(shù)據(jù)報包的套接字。
數(shù)據(jù)報套接字是包投遞服務(wù)的發(fā)送或接收點。每個在數(shù)據(jù)報套接字上發(fā)送或接收的包都是單獨編址和路由的。從一臺機器發(fā)送到另一臺機器的多個包可能選擇不同的路由,也可能按不同的順序到達。
在 DatagramSocket 上總是啟用 UDP 廣播發(fā)送。為了接收廣播包,應(yīng)該將 DatagramSocket 綁定到通配符地址。在某些實現(xiàn)中,將 DatagramSocket 綁定到一個更加具體的地址時廣播包也可以被接收。
構(gòu)造方法:
DatagramSocket() ~ 構(gòu)造數(shù)據(jù)報套接字并將其綁定到本地主機上任何可用的端口。
protected DatagramSocket(DatagramSocketImpl impl) ~創(chuàng)建帶有指定 DatagramSocketImpl (數(shù)據(jù)報和多播套接字實現(xiàn)的抽象基類,可以通過它將數(shù)據(jù)報套接字綁定到本地端口和地址。)的未綁定數(shù)據(jù)報套接字。
DatagramSocket(int port) ~創(chuàng)建數(shù)據(jù)報套接字并將其綁定到本地主機上的指定端口。
DatagramSocket(int port, InetAddress laddr) ~創(chuàng)建數(shù)據(jù)報套接字,將其綁定到指定的本地地址。
DatagramSocket(SocketAddress bindaddr) ~創(chuàng)建數(shù)據(jù)報套接字,將其綁定到指定的本地套接字地址。 (SocketAddress 作為一個抽象類,應(yīng)通過特定的、協(xié)議相關(guān)的實現(xiàn)為其創(chuàng)建子類。 它提供不可變對象,供套接字用于綁定、連接或用作返回值。 )
DatagramSocket(DatagramSocketImpl impl)和DatagramSocket(SocketAddress bindaddr)就是通過一個對象來進行本地端口和地址的綁定……。
DatagramPacket
此類表示數(shù)據(jù)報包。
數(shù)據(jù)報包用來實現(xiàn)無連接包投遞服務(wù)。每條報文僅根據(jù)該包中包含的信息從一臺機器路由到另一臺機器。從一臺機器發(fā)送到另一臺機器的多個包可能選擇不同的路由,也可能按不同的順序到達。不對包投遞做出保證。
具體api見:http://www.javaweb.cc/help/JavaAPI1.6/index.html?java/nio/ReadOnlyBufferException.html
流程
? 基于UDP的套接字就是數(shù)據(jù)報套接字(ava.net.DatagramSocketj)。
? 兩個都要先構(gòu)造好相應(yīng)的數(shù)據(jù)包(java.net.DatagramPacket)。
? 在DatagramPacket包中的函數(shù) intgetLength()返回實際接受的字節(jié)數(shù),
byte[]getData()返回接受到的數(shù)據(jù)。
? 要想接受端給發(fā)送端回信息,就需要知道發(fā)送端的IP地址InetAddress getAddress()和發(fā)送端進程所綁定的端口號int getPort()。
? 數(shù)據(jù)報套接字發(fā)送成功之后,就相當于建立了一個虛連接,雙方可以發(fā)送數(shù)據(jù)。
具體代碼
服務(wù)器端:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/*
-
服務(wù)器端,實現(xiàn)基于UDP的用戶登陸
/
public class UDPServer {
public static void main(String[] args) throws IOException {
/
* 接收客戶端發(fā)送的數(shù)據(jù)
*/
// 1.創(chuàng)建服務(wù)器端DatagramSocket,指定端口
DatagramSocket socket = new DatagramSocket(8800);
// 2.創(chuàng)建數(shù)據(jù)報,用于接收客戶端發(fā)送的數(shù)據(jù)
byte[] data = new byte[1024];// 創(chuàng)建字節(jié)數(shù)組,指定接收的數(shù)據(jù)包的大小
DatagramPacket packet = new DatagramPacket(data, data.length);
// 3.接收客戶端發(fā)送的數(shù)據(jù)
System.out.println("****服務(wù)器端已經(jīng)啟動,等待客戶端發(fā)送數(shù)據(jù)");
socket.receive(packet);// 此方法在接收到數(shù)據(jù)報之前會一直阻塞
// 4.讀取數(shù)據(jù)
String info = new String(data, 0, packet.getLength());
System.out.println("我是服務(wù)器,客戶端說:" + info);/* * 向客戶端響應(yīng)數(shù)據(jù) */ // 1.定義客戶端的地址、端口號、數(shù)據(jù) InetAddress address = packet.getAddress(); int port = packet.getPort(); byte[] data2 = "歡迎您!".getBytes(); // 2.創(chuàng)建數(shù)據(jù)報,包含響應(yīng)的數(shù)據(jù)信息 DatagramPacket packet2 = new DatagramPacket(data2, data2.length, address, port); // 3.響應(yīng)客戶端 socket.send(packet2); // 4.關(guān)閉資源 socket.close();}
}
客戶端:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
/*
-
客戶端
/
public class UDPClient {
public static void main(String[] args) throws IOException {
/
* 向服務(wù)器端發(fā)送數(shù)據(jù)
*/
// 1.定義服務(wù)器的地址、端口號、數(shù)據(jù)
InetAddress address = InetAddress.getByName("localhost");
int port = 8800;
byte[] data = "用戶名:admin;密碼:123".getBytes();
// 2.創(chuàng)建數(shù)據(jù)報,包含發(fā)送的數(shù)據(jù)信息
DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
// 3.創(chuàng)建DatagramSocket對象
DatagramSocket socket = new DatagramSocket();
// 4.向服務(wù)器端發(fā)送數(shù)據(jù)報
socket.send(packet);/* * 接收服務(wù)器端響應(yīng)的數(shù)據(jù) */ // 1.創(chuàng)建數(shù)據(jù)報,用于接收服務(wù)器端響應(yīng)的數(shù)據(jù) byte[] data2 = new byte[1024]; DatagramPacket packet2 = new DatagramPacket(data2, data2.length); // 2.接收服務(wù)器響應(yīng)的數(shù)據(jù) socket.receive(packet2); // 3.讀取數(shù)據(jù) String reply = new String(data2, 0, packet2.getLength()); System.out.println("我是客戶端,服務(wù)器說:" + reply); // 4.關(guān)閉資源 socket.close();}
}