導(dǎo)讀目錄
- UDP協(xié)議的基礎(chǔ)
- 使用DatagramSocket發(fā)送、接受數(shù)據(jù)
- 使用MulticastSocket實(shí)現(xiàn)多點(diǎn)廣播
1.UDP協(xié)議的基礎(chǔ)
UDP(User Datagram Protocol, 用戶數(shù)據(jù)報(bào)協(xié)議)是一中不可靠的網(wǎng)絡(luò)協(xié)議,他在通信實(shí)例的兩端各建立一個(gè)Socket,但這兩個(gè)Socket之間并沒有虛擬鏈路,它們只是發(fā)送和接受數(shù)據(jù)包的對(duì)象。
UDP是面向非連接的協(xié)議,即在正式通信之前是不需要與對(duì)方先建立連接(即對(duì)對(duì)方的狀態(tài)不關(guān)心)。
UDP和TCP均位于IP協(xié)議之上。
UDP協(xié)議的主要作用是完成網(wǎng)絡(luò)數(shù)據(jù)流和數(shù)據(jù)報(bào)之間的轉(zhuǎn)換。發(fā)送時(shí):將網(wǎng)絡(luò)數(shù)據(jù)流封裝成數(shù)據(jù)報(bào),然后將數(shù)據(jù)報(bào)發(fā)送出去。接受端:將數(shù)據(jù)報(bào)轉(zhuǎn)換為實(shí)際內(nèi)容
使用DatagramSocket代表基于UDP協(xié)議的Socket
使用DatagramPacket代表要發(fā)送或接受的數(shù)據(jù)報(bào)
UDP和TCP相比:
(1)TCP協(xié)議:可靠,傳輸大小無限制,但是需要時(shí)間建立連接,差錯(cuò)控制開銷大
(2)UDP協(xié)議:不可靠,差錯(cuò)控制開銷小,傳輸大小限制在64KB以下,不需要建立連接
2.使用DatagramSocket發(fā)送、接受數(shù)據(jù)
(1)DatagramSocket
DatagramSocket不維護(hù)狀態(tài),不能產(chǎn)生IO流(前面講的Socket可以產(chǎn)生IO流),它的唯一所用就是接受和發(fā)送數(shù)據(jù)報(bào)(即DatagramPacket對(duì)象)
DatagramSocket的構(gòu)造器
DatagramSocket();//創(chuàng)建的DatagramSocket實(shí)例綁定到默認(rèn)的IP地址和系統(tǒng)隨機(jī)分配的端口
DatagramSocket(int port);//使用默認(rèn)的IP和指定的端口
DatagramSocket(int port, InetAddress laddr);//使用指定的IP和端口
DatagramSocket的收發(fā)數(shù)據(jù)報(bào)的方法
void receive(DatagramPacket p);//從該DatagramPacket中接受數(shù)據(jù)報(bào)
void send(DatagramPacket p);//以該DatagramPacket對(duì)象向外發(fā)送數(shù)據(jù)報(bào)
注意:DatagramSocket并知道數(shù)據(jù)報(bào)要發(fā)送到哪里,而是由DatagramPacket自身決定數(shù)據(jù)報(bào)的目的地。
(2)DatagramPacket
//創(chuàng)建接受數(shù)據(jù)一方的DatagramPacket對(duì)象
DatagramPacket(byte[] buf, int length);
DatagramPacket(byte[] buf, int offset, int length);
//創(chuàng)建發(fā)送數(shù)據(jù)一方的DatagramPacket對(duì)象,因?yàn)橐獍l(fā),因此是需要傳入IP地址和端口號(hào)的
DatagramPacket(byte[] buf, int length, InetAddress address, int port);
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)
由于服務(wù)器端(客戶端一樣)接收到一個(gè)DatagramPacket對(duì)象后,它是不知道誰發(fā)送過來的,因此可以調(diào)用DatagramPacket的如下方法:
(1)InetAddress getAddress();//如果程序準(zhǔn)備發(fā)送此數(shù)據(jù)報(bào)時(shí),該方法將返回此數(shù)據(jù)報(bào)的目標(biāo)IP地址;當(dāng)程序接受到一個(gè)數(shù)據(jù)報(bào)時(shí),該方法返回該數(shù)據(jù)報(bào)的來源主機(jī)的IP地址
(2)int getPort(); //同上,只是返回的是端口,
(3)SocketAddress getSocketAddress();//同上,只是返回的是SocketAddress, 該對(duì)象封裝了一個(gè)InetAddress對(duì)象和代表port的整數(shù)
(4)byte[] getData();//返回傳入Datagrampacket中的數(shù)組
(5)void setData(byte[] buf);//往byte[]數(shù)組中裝入數(shù)據(jù)
(6)void setData(byte[] buf, int offset, int length)
(7)int getLength();//返回讀入數(shù)組中的實(shí)際數(shù)據(jù)的長(zhǎng)度
事例
接收端
//創(chuàng)建一個(gè)DatagramSocket用于收發(fā)DatagramPacket數(shù)據(jù)報(bào)
DatagramSocket dmSocket = new DatagramSocket(3006);
//創(chuàng)建一個(gè)用于裝載數(shù)據(jù)的DatagramPacket
byte[] inBuff = new byte[1024];//為了防止放過來的數(shù)據(jù)比較長(zhǎng),因此將這個(gè)字節(jié)數(shù)組設(shè)置的盡量大些
DatagramPacket inPacket = new DatagramPacket(inBuff, inBuff.length);
//準(zhǔn)備接受收據(jù)報(bào)
while(true) {
dmSocket.receive(inPacket);
//程序執(zhí)行到這里時(shí)就可以通過inPacket來獲取一些關(guān)于發(fā)送端主機(jī)的信息,如IP地址、發(fā)送端口號(hào)等
System.out.println(new String(inBuff, 0, inPacket.getLength()));
}
發(fā)送端
//創(chuàng)建一個(gè)DatagramSocket用于收發(fā)DatagramPacket數(shù)據(jù)報(bào)
DatagramSocket dgSocket = new DatagramSocket();
//創(chuàng)建一個(gè)用于裝載數(shù)據(jù)的DatagramPacket
byte[] outBuff = "string".getBytes();//將數(shù)據(jù)裝入DatagramPacket中
DatagramPacket outPacket = new DatagramPacket(outBuff, outBuff.length, InetAddress.getLocalHost(), 3006);
//發(fā)送數(shù)據(jù)
dgSocket.send(outPacket);
dgSocket.close();
注意:DatagramSocket只允許數(shù)據(jù)報(bào)發(fā)送給指定的目標(biāo)地址,而MulticastSocket可以將數(shù)據(jù)報(bào)以廣播的形式發(fā)送到多個(gè)客戶端
3.使用MulticastSocket實(shí)現(xiàn)多點(diǎn)廣播
參考博文:http://www.cnblogs.com/mengfanrong/p/3758308.html
(1)MulticastSocket的簡(jiǎn)潔
DatagramSocket僅僅同意數(shù)據(jù)報(bào)發(fā)送給指定的目標(biāo)地址,而MulticastSocket能夠?qū)?shù)據(jù)報(bào)以廣播的方式發(fā)送到多個(gè)client。 若要使用多點(diǎn)廣播,則須要讓一個(gè)數(shù)據(jù)報(bào)標(biāo)有一組目標(biāo)主機(jī)地址,當(dāng)數(shù)據(jù)報(bào)發(fā)出后,整個(gè)組的全部全部主機(jī)都能收到該數(shù)據(jù)報(bào)。IP多點(diǎn)廣播(或多點(diǎn)發(fā)送)實(shí)現(xiàn)了將單一信息發(fā)送到多個(gè)接受者的廣播,其思想是設(shè)置一組特殊網(wǎng)絡(luò)地址作為多點(diǎn)廣播地址,每個(gè)多點(diǎn)廣播地址都被看做一個(gè)組,當(dāng)client須要發(fā)送、接收廣播信息時(shí),增加到該組就可以。
應(yīng)用程序僅僅將數(shù)據(jù)報(bào)包發(fā)送給組播地址,路由器將確保包被發(fā)送到該組播組中的全部主機(jī)。
組播地址:稱為組播組的一組主機(jī)所共享的地址。組播地址的范圍在224.0.0.0--- 239.255.255.255之間(都為D類地址 1110開頭)。
備注:假設(shè)如今有三臺(tái)機(jī)器A、B、C,三臺(tái)機(jī)器IP地址都不一樣,A\B為server監(jiān)聽廣播消息,C為client發(fā)送廣播消息,個(gè)人理解是將A、B兩臺(tái)機(jī)器的MulticastSocket對(duì)象綁定在組播地址中的當(dāng)中一個(gè),然后C client發(fā)送消息的組播地址一致,則A、B就行接收C發(fā)送的消息。
假設(shè)MulticastSocket用于發(fā)送信息則使用默認(rèn)地址和隨機(jī)port就可以,可是假設(shè)用來接收信息,則必需要指定port,否則發(fā)送方無法確定發(fā)送數(shù)據(jù)報(bào)的目標(biāo)port。
MulticastSocket既能夠?qū)?shù)據(jù)報(bào)發(fā)送到多點(diǎn)廣播地址,也能夠接收其它主機(jī)的廣播信息
構(gòu)造器
MulticastSocket();//使用默認(rèn)的地址、隨機(jī)端口來創(chuàng)建MulticastSocket對(duì)象,適用于只發(fā)不收的情況下
MulticastSocket(int port);//;//使用默認(rèn)的地址、指定的端口來創(chuàng)建MulticastSocket對(duì)象,可發(fā)可收
MulticastSocket(SocketAddress bindaddr);//使用指定的地址和端口來創(chuàng)建MulticastSocket對(duì)象
方法
(1)joinGroup(InetAddress mcastaddr);//將該MulticastSocket加入指定的多點(diǎn)廣播地址
(2)joinGroup(SocketAddress mcastaddr, NetworkInterface netIf);//將該MulticastSocket加入指定的多點(diǎn)廣播地址,并指定網(wǎng)絡(luò)接口
(3)leaveGroup(InetAddress mcastaddr);//讓該MulticastSocket離開指定的多點(diǎn)廣播地址
(4)leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf);//讓該MulticastSocket離開指定的多點(diǎn)廣播地址,并指定網(wǎng)絡(luò)接口
(5)get/setInterface(InetAddress inf);
(6)get/setNetworkInterface(NetworkInterface netIf);//設(shè)置/獲取該MulticastSocket的網(wǎng)絡(luò)接口
注:
NetworkInterface 網(wǎng)絡(luò)接口類
什么是網(wǎng)絡(luò)接口:網(wǎng)絡(luò)接口名并非計(jì)算機(jī)名,而是用于標(biāo)識(shí)物理或邏輯網(wǎng)絡(luò)接口的名字,通常是由操作系統(tǒng)設(shè)置的。網(wǎng)絡(luò)接口名在大多數(shù)操作系統(tǒng)上(包含Windows、Linux和Unix)是以eth開頭,后面是網(wǎng)絡(luò)接口的索引號(hào),從0開始。如本機(jī)安了三塊網(wǎng)卡,那么網(wǎng)絡(luò)接口名就依次是eth0、eth1和eth2。每一個(gè)網(wǎng)絡(luò)接口都能夠綁定一個(gè)ip地址,也能夠據(jù)此得到設(shè)備的MAC地址。
方法:1. Enumeration<InetAddress> getInetAddresses():一個(gè) Enumeration 對(duì)象,具有綁定到此網(wǎng)絡(luò)接口的所有或部分 InetAddress
Java中InetAddress和InetSocketAddress的區(qū)別:
在Java中InetAddress和InetSocketAddress看起來很相似,用來描述IP地址和主機(jī)名稱。當(dāng)然,它們也支持使用常規(guī)方法來檢查地址:回環(huán)地址、本地地址、組播地址;基本的返回方法:獲得IP,獲得主機(jī)名稱等。
重要的是InetSocketAddress包含InetAddress。這意味著,如果我們想對(duì)InetSocketAddress中的InetAddress做任何操作,只需要通過getInetAddress()方法獲得即可。
對(duì)照表
屬性 InetAddress InetSocketAddress
描述對(duì)象 IP地址 Socket地址(IP地址+端口)
描述 IP和主機(jī)對(duì)象名稱 IP和主機(jī)的對(duì)象名稱,并包括端口號(hào)
解決問題 IP到主機(jī)名稱,主機(jī)名稱到IP IP到主機(jī)名稱,主機(jī)名稱到IP,可以包含端口
獲取對(duì)象 InetAddress.getLocalhost(); InetSocketAddress.createUnresolved(String, port);
InetAddress.getByName(String);
InetAddress.getByAddress(String);
參考自:http://blog.csdn.net/wo541075754/article/details/66971888
(2)實(shí)現(xiàn)多點(diǎn)廣播
1.創(chuàng)建MulticastSocket對(duì)象
2.將該MulticastSocket增加到指定的多點(diǎn)廣播地址:
joinGroup(InetAddress multicastAddr):將該MulticastSocket增加指定的多點(diǎn)廣播地址。
leaveGroup(InetAddress multicastAddr):讓該MulticastSocket離開指定的多點(diǎn)廣播地址。
3.創(chuàng)建相關(guān)的DatagramPacket,用于接受或發(fā)送數(shù)據(jù)
4.接受或發(fā)送數(shù)據(jù)
import java.net.MulticastSocket;
import java.net.InetAddress;
import java.net.DatagramPacket;
import java.io.*;
import java.util.*;
public class MulticastSocketTest implements Runnable {
MulticastSocket mSocket = null;//創(chuàng)建MulticastSocket對(duì)象
byte[] inBuff = new byte[1024];
DatagramPacket inPacket = null;//創(chuàng)建相關(guān)的DatagramPacket,用于接受數(shù)據(jù)
public void init() throws IOException {
//1.創(chuàng)建MulticastSocket對(duì)象
mSocket = new MulticastSocket(3006);//指定端口號(hào),就可以收發(fā)數(shù)據(jù)
//2.將該MulticastSocket加入到組播地址中
InetAddress boradcastAddrss = InetAddress.getByName("230.0.0.1");
mSocket.joinGroup(boradcastAddrss);
//設(shè)置本MulticastSocket發(fā)送的數(shù)據(jù)報(bào)會(huì)被回送到自身
mSocket.setLoopbackMode(false);//true表示不會(huì)回送
//3.創(chuàng)建相關(guān)的DatagramPacket,用于接受數(shù)據(jù)
inPacket = new DatagramPacket(inBuff, inBuff.length);
//3.創(chuàng)建用于發(fā)送數(shù)據(jù)的DatagramPacket(這個(gè)可以在其他類中實(shí)現(xiàn),只是本類設(shè)計(jì)成了可以進(jìn)行收發(fā)的)
DatagramPacket outPacket = new DatagramPacket(inBuff, inBuff.length, boradcastAddrss, 3006);
new Thread(this).start();//開啟單獨(dú)的線程用于接受數(shù)據(jù)
//下面是為了實(shí)現(xiàn)發(fā)送數(shù)據(jù)的邏輯
Scanner sc = new Scanner(System.in);
while(sc.hasNextLine()) {
//將數(shù)據(jù)轉(zhuǎn)換為byte[]
byte[] outBuff = sc.nextLine().getBytes();
//設(shè)置發(fā)送用的DatagramSocket的數(shù)據(jù)
outPacket.setData(outBuff);
//4.發(fā)送數(shù)據(jù)
mSocket.send(outPacket);
}
}
public void run() {
try{
while(true) {
//4.等待接收數(shù)據(jù)
mSocket.receive(inPacket);
System.out.println(new String(inBuff, 0, inPacket.getLength()));
}
}catch(IOException ie) {
ie.printStackTrace();
}
}
//主方法
public static void main(String[] args) throws IOException{
new MulticastSocketTest().init();
}
}