摘要:現(xiàn)在在本機(jī)同一局域網(wǎng)的一臺(tái)機(jī)器和阿里云主機(jī)上都運(yùn)行然后啟動(dòng)發(fā)送端接收端接收結(jié)果可以看到每個(gè)接收端都正確的接收了發(fā)送端發(fā)送的消息。
今天的主角是 UDP(User Datagram Protocol,用戶(hù)數(shù)據(jù)報(bào)協(xié)議)。
我們都知道 TCP 是一種可靠的協(xié)議 —— 首先客戶(hù)端和服務(wù)端需要建立連接(三次“握手”),數(shù)據(jù)發(fā)送完畢需要斷開(kāi)連接(四次“揮手”);如果發(fā)送數(shù)據(jù)時(shí)數(shù)據(jù)損壞或者丟失,那么 TCP 會(huì)重新發(fā)送。保證可靠的代價(jià)就是效率的降低(建立連接和斷開(kāi)連接就需要時(shí)間,保證數(shù)據(jù)的可靠性也需要額外的消耗)。與 TCP 相對(duì)應(yīng),UDP 是面向無(wú)連接的協(xié)議,并且它不保證數(shù)據(jù)是否會(huì)到達(dá),也不保證到達(dá)的數(shù)據(jù)是否準(zhǔn)確和數(shù)據(jù)順序是否正確 —— 所以相比于 TCP, UDP 的速度很快。在 需要不建立連接即可發(fā)送數(shù)據(jù) 的系統(tǒng),或者 保證最快的傳輸速度比每一位數(shù)據(jù)都正確更重要 的系統(tǒng)(如視頻會(huì)議,丟失某個(gè)數(shù)據(jù)包只是一個(gè)畫(huà)面或者聲音的小干擾)中,UDP 才是正確的選擇。實(shí)際上,在同一個(gè)網(wǎng)段,或者在信號(hào)很好的局域網(wǎng),UDP 是非??煽康?。
在 Java 中使用 UDP 時(shí)關(guān)鍵的兩個(gè)類(lèi)分別是:DatagramSocket 和 DatagramPacket。
DatagramPacket 表示數(shù)據(jù)包,而 DatagrameSocket 用來(lái)發(fā)送和接收數(shù)據(jù)包。
發(fā)送數(shù)據(jù)包時(shí):
因?yàn)?UDP 協(xié)議并不需要建立連接,所以我們將數(shù)據(jù)(byte數(shù)組)放入 DatagramPacket 之后,還需要將目的地(IP地址和端口)放入到 DatagramPacket 中 —— DatagramSocket 的 send(DatagramPacket packet) 方法根據(jù) packet 中指定目的地,將其包含的數(shù)據(jù)往這個(gè)目的地發(fā)送。至于數(shù)據(jù)是否能(準(zhǔn)確)到達(dá)目的地,DatagramSocket 并不關(guān)心。
接收數(shù)據(jù)包時(shí):
DatagramSocket 在接收數(shù)據(jù)包時(shí),我們需要為其指定一個(gè)監(jiān)聽(tīng)的端口。當(dāng)有包含了接收端機(jī)器 IP 地址和 DatagramSocket 所監(jiān)聽(tīng)端口的數(shù)據(jù)包到達(dá)時(shí),DatagramSocket 的 receive(DatagramPacket packet) 方法便會(huì)對(duì)數(shù)據(jù)包進(jìn)行接收,并將接收到的數(shù)據(jù)包填入到 packet 中。
現(xiàn)在讓我們來(lái)實(shí)踐 DatagramSocket 和 DatagramPacket:
因?yàn)?UDP 沒(méi)有服務(wù)端和客戶(hù)端之分,所以我們把兩端分別定義為 發(fā)送端 和 接收端。
發(fā)送端代碼:
import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class UDPSender { private static final int RECVER_LISTENING_PORT = 9999; public static void main(String[] args) throws Exception { Listmessages = new ArrayList<>(12); for (int i = 0; i < 4; i++) { String msgBody = (i == 3 ? "" : "Hello UDP-" + i); DatagramPacket msg0 = parseMsg( msgBody, "127.0.0.1", RECVER_LISTENING_PORT); // 發(fā)送給本機(jī) DatagramPacket msg1 = parseMsg( msgBody, "192.168.3.3", RECVER_LISTENING_PORT); // 發(fā)送給同一局域網(wǎng)的一臺(tái)機(jī)器 DatagramPacket msg2 = parseMsg( msgBody, "120.77.**.***", RECVER_LISTENING_PORT); // 120.77.**.*** 是我阿里云主機(jī)的公網(wǎng) IP 地址 // JDK1.5 時(shí) Collections 添加的 addAll 方法,可以一次往某個(gè)集合中添加多個(gè)元素 Collections.addAll(messages, msg0, msg2, msg1); } startSending(messages); } private static void startSending(List messages) throws IOException, InterruptedException { // 無(wú)參構(gòu)造的 DatagramSocket 會(huì)隨機(jī)選擇一個(gè)端口進(jìn)行監(jiān)聽(tīng) // 因?yàn)榇藭r(shí) DatagramSocket 的作用是發(fā)送,所以無(wú)需顯式指定固定端口 try (DatagramSocket socket = new DatagramSocket()) { System.out.println("隨機(jī)給發(fā)送端分配的端口為:" + socket.getLocalPort() + " "); for (DatagramPacket msg : messages) { socket.send(msg); // 發(fā)送數(shù)據(jù)包 int recverPort = msg.getPort(); InetAddress recverAddr = msg.getAddress(); System.out.printf("消息已經(jīng)發(fā)送 -> (%s:%d) ", recverAddr.getHostAddress(), recverPort); Thread.sleep(500); // 設(shè)定 每隔 0.5 秒發(fā)送一個(gè)消息 } } } private static DatagramPacket parseMsg(String msgBody, String addr, int port) throws UnknownHostException { byte[] msgData = msgBody.getBytes(); DatagramPacket msg = new DatagramPacket( msgData, 0, msgData.length, // 數(shù)據(jù)從位置 0 開(kāi)始,長(zhǎng)度為 msgData.length InetAddress.getByName(addr), port); // 目的地 地址為 addr,監(jiān)聽(tīng)端口為 port return msg; } }
接收端代碼:
import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; public class UDPReceiver { private static final int LISTENING_PORT = 9999; private static final int BUFFER_SIZE = 512; public static void main(String[] args) throws Exception { byte[] buffer = new byte[BUFFER_SIZE]; DatagramPacket msg = new DatagramPacket(buffer, buffer.length); try (DatagramSocket socket = new DatagramSocket(LISTENING_PORT)) { System.out.println("接收端已經(jīng)啟動(dòng)... "); while (true) { socket.receive(msg); // 接收數(shù)據(jù)包 String msgBody = new String( msg.getData(), msg.getOffset(), msg.getLength()); if (msgBody.isEmpty()) { // 發(fā)現(xiàn)接收的消息是空字符串("")便跳出循環(huán) break; } int senderPort = msg.getPort(); InetAddress senderAddr = msg.getAddress(); System.out.printf("發(fā)送端 地址和端口 -> (%s:%d) ", senderAddr.getHostAddress(), senderPort); System.out.println("發(fā)送端 發(fā)送的消息 -> " + msgBody + " "); } } System.out.println("接收端已經(jīng)關(guān)閉。"); } }
現(xiàn)在 在本機(jī)、同一局域網(wǎng)的一臺(tái)機(jī)器和阿里云主機(jī)上都運(yùn)行 UDPReceiver:
然后啟動(dòng)發(fā)送端 UDPSender:
接收端接收結(jié)果:
可以看到每個(gè)接收端都正確的接收了發(fā)送端發(fā)送的消息。
(本機(jī) 和 局域網(wǎng)機(jī)器 顯示的發(fā)送端的端口(49756)是一致的,但是遠(yuǎn)程機(jī)器即阿里云主機(jī)顯示的(4802)卻與他們不一致——這個(gè)問(wèn)題留給有興趣的讀者自己思考)
雖然 UDP 是不可靠的協(xié)議,但是因?yàn)樗恍枰⑦B接,效率更快,所以在 需要不建立連接即可發(fā)送數(shù)據(jù) 的系統(tǒng)(比如本文的例子),或者 保證最快的傳輸速度比每一位數(shù)據(jù)都正確更重要 的系統(tǒng)中,我們應(yīng)該使用 UDP。當(dāng)然,基于 UDP 我們同樣可以開(kāi)發(fā)出可靠的協(xié)議——數(shù)據(jù)包的正確與否可以交給應(yīng)用程序來(lái)判斷,如果有問(wèn)題接收端便提示發(fā)送端重新發(fā)送。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/69833.html
摘要:具備發(fā)送和接受功能,在進(jìn)行傳輸時(shí),需要明確一個(gè)是發(fā)送端,一個(gè)是接收端。指定發(fā)送端口,不指定系統(tǒng)會(huì)隨機(jī)分配。傳輸兩個(gè)端點(diǎn)的建立連接后會(huì)有一個(gè)傳輸數(shù)據(jù)的通道,這通道稱(chēng)為流,而且是建立在網(wǎng)絡(luò)基礎(chǔ)上的流,稱(chēng)之為流。 端口:物理端口:邏輯端口: 用于標(biāo)識(shí)進(jìn)程的邏輯地址,不同進(jìn)程的標(biāo)識(shí);有效端口:0~65535,其中0~1024系統(tǒng)使用或保留端口。 java 中ip對(duì)象:InetAddress....
摘要:三端口與套接字端口指一臺(tái)計(jì)算機(jī)只有單一的連接到網(wǎng)絡(luò)的物理連接,所以的數(shù)據(jù)都通過(guò)此連接對(duì)內(nèi)對(duì)外送達(dá)特定的計(jì)算機(jī),這就是端口。三程序設(shè)計(jì)由上面可知基于的信息傳遞速度更快。接收數(shù)據(jù)包使用創(chuàng)建數(shù)據(jù)包套接字,綁定指定端口。 服務(wù)器 網(wǎng)絡(luò) 客戶(hù)機(jī) 第一部分 一.局域網(wǎng)與因特網(wǎng) 服務(wù)器是指提供信息的計(jì)算機(jī)或程序,...
摘要:那沒(méi)有建立連接的情況下,發(fā)現(xiàn)房間這個(gè)功能是怎么實(shí)現(xiàn)的呢首先,既然手機(jī)處于局域網(wǎng)中,那么根據(jù)手機(jī)當(dāng)前在局域網(wǎng)的地址和子網(wǎng)掩碼,就可以獲得這個(gè)局域網(wǎng)內(nèi)所有機(jī)器的地址的范圍。 記得以前我們使用類(lèi)似快牙這些文件分享工具的時(shí)候,一開(kāi)始就是先在 手機(jī)A 上創(chuàng)建一個(gè)房間,然后連接上 手機(jī)A WiFi 熱點(diǎn)的其他手機(jī)(即這些手機(jī)處于一個(gè)局域網(wǎng)內(nèi))就可以發(fā)現(xiàn)到這個(gè)房間并加入到這個(gè)房間里面,然后就可以互相...
摘要:更多資料請(qǐng)看編程之編程協(xié)議用戶(hù)數(shù)據(jù)報(bào)協(xié)議是無(wú)連接的不可靠的無(wú)序的速度快進(jìn)行數(shù)據(jù)傳輸時(shí),首先將要傳輸?shù)臄?shù)據(jù)定義成數(shù)據(jù)報(bào),大小限制在,在數(shù)據(jù)報(bào)中指明數(shù)據(jù)索要達(dá)到的主機(jī)地址和端口號(hào),然后再將數(shù)據(jù)報(bào)發(fā)送出去類(lèi)表示數(shù)據(jù)報(bào)包類(lèi)進(jìn)行端到端通信的類(lèi)服務(wù)器端 更多資料請(qǐng)看:https://www.yuque.com/shizhiy... Java Socket編程之UDP編程 UDP協(xié)議(用戶(hù)數(shù)據(jù)報(bào)協(xié)...
摘要:應(yīng)用層主要負(fù)責(zé)應(yīng)用程序的協(xié)議,例如協(xié)議協(xié)議等。在計(jì)算機(jī)中,不同的應(yīng)用程序是通過(guò)端口號(hào)區(qū)分的。區(qū)別在于,中只有發(fā)送端和接收端,不區(qū)分客戶(hù)端與服務(wù)器端,計(jì)算機(jī)之間可以任意地發(fā)送數(shù)據(jù)。 01網(wǎng)絡(luò)模型 *A:網(wǎng)絡(luò)模型 TCP/IP協(xié)議中的四層分別是應(yīng)用層、傳輸層、網(wǎng)絡(luò)層和鏈路層,每層分別負(fù)責(zé)不同的通信功能,接下來(lái)針對(duì)這四層進(jìn)行詳細(xì)地講解。 鏈路層:鏈路層是用于定義物理傳輸通道,通常是對(duì)...
閱讀 1467·2021-11-24 09:39
閱讀 3641·2021-09-29 09:47
閱讀 1584·2021-09-29 09:34
閱讀 3087·2021-09-10 10:51
閱讀 2552·2019-08-30 15:54
閱讀 3230·2019-08-30 15:54
閱讀 883·2019-08-30 11:07
閱讀 1022·2019-08-29 18:36