DatagramPacket
UDP在Socket網(wǎng)絡(luò)中都是通過DatagramPacket的數(shù)據(jù)格式進(jìn)行傳輸?shù)模簿褪菙?shù)據(jù)報(bào)的形式腺逛。UDP數(shù)據(jù)報(bào)是基于IP數(shù)據(jù)報(bào)建立的豺憔,UDP數(shù)據(jù)報(bào)同樣分為首部和主體迅腔,UDP數(shù)據(jù)報(bào)的首部比IP數(shù)據(jù)報(bào)的首部多了8個(gè)字節(jié),其中UDP的首部包括:源端口號(hào)和目標(biāo)端口號(hào)、 IP首部之后所有內(nèi)容的長(zhǎng)度嗽元、 可選的檢驗(yàn)和敛纲。 理論上UDP包中的數(shù)據(jù)長(zhǎng)度最大是65507字節(jié),但實(shí)際上總是比這少得多剂癌。
DatagramPacket相關(guān)API
- 接收數(shù)據(jù)報(bào)的構(gòu)造函數(shù)
public DatagramPacket(byte buf[], int length)
public DatagramPacket(byte buf[], int offset, int length)
這兩個(gè)構(gòu)造函數(shù)可以創(chuàng)建新的DatagramPacket對(duì)象并從網(wǎng)絡(luò)接收數(shù)據(jù)淤翔。
- 第一個(gè)構(gòu)造函數(shù):當(dāng)Socket接收一個(gè)數(shù)據(jù)報(bào)時(shí),它將數(shù)據(jù)報(bào)的數(shù)據(jù)部分存儲(chǔ)在buf字節(jié)數(shù)組中佩谷,從buf[0]開始一直到包完全存儲(chǔ)旁壮,或者直到向字節(jié)數(shù)組中寫入了length個(gè)字節(jié)為止。
- 第二個(gè)構(gòu)造函數(shù):將從buf[offset]開始存儲(chǔ)谐檀,直到包完全存儲(chǔ)抡谐,或者直到向字節(jié)數(shù)組中寫入了length個(gè)字節(jié)為止。
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, 0, buf.length);
- 發(fā)送數(shù)據(jù)報(bào)的構(gòu)造函數(shù)
public DatagramPacket(byte buf[], int length, SocketAddress address)
public DatagramPacket(byte buf[], int offset, int length, SocketAddress address)
public DatagramPacket(byte buf[], int length,InetAddress address, int port)
public DatagramPacket(byte buf[], int offset, int length,InetAddress address, int port)
這四個(gè)構(gòu)造函數(shù)可以創(chuàng)建新的DatagramPacket對(duì)象并從網(wǎng)絡(luò)發(fā)送數(shù)據(jù)桐猬。
- 這個(gè)包用buf字節(jié)數(shù)據(jù)中從offset(如果沒有指定該參數(shù)則默認(rèn)為0)開始的length個(gè)字節(jié)填充麦撵,指定了要發(fā)送的數(shù)據(jù)。
- InetAddress或SocketAddress對(duì)象指定了包要發(fā)送的目標(biāo)主機(jī)地址溃肪。
- port參數(shù)表示該主機(jī)上的端口免胃。
// 準(zhǔn)備要發(fā)送的數(shù)據(jù)
String str = "This is a test.";
// 轉(zhuǎn)化為byte字節(jié)數(shù)組
byte[] buf = str.getBytes("UTF-8");
// 構(gòu)造發(fā)送的目標(biāo)地址和端口
InetAddress address = InetAddress.getByName("www.ibiblio.org");
int port = 7;
// 構(gòu)造DatagramPacket
DatagramPacket packet = new DatagramPacket(buf, buf.length, address, port);
// 發(fā)送數(shù)據(jù)
DatagramSocket socket = new DatagramSocket();
socket.send(packet);
- 獲取DatagramPacket信息的方法
DatagramPacket有6個(gè)獲取數(shù)據(jù)報(bào)不同部分的方法,這些部分包括具體的數(shù)據(jù)以及首部的字段惫撰,這些方法主要用于從網(wǎng)絡(luò)接收的數(shù)據(jù)報(bào)羔沙。
public synchronized InetAddress getAddress()
public synchronized int getPort()
public synchronized SocketAddress getSocketAddress()
public synchronized byte[] getData()
public synchronized int getLength()
public synchronized int getOffset()
- getAddress():返回一個(gè)InetAddress對(duì)象,指示遠(yuǎn)程主機(jī)的地址厨钻。這里有兩種情況:一是如果數(shù)據(jù)報(bào)是從Internet接收的撬碟,返回的地址則是發(fā)送該數(shù)據(jù)報(bào)的機(jī)器的地址(源地址);二是如果數(shù)據(jù)報(bào)是本地創(chuàng)建準(zhǔn)備發(fā)送到遠(yuǎn)程主機(jī)的莉撇,返回的地址則是要發(fā)往的遠(yuǎn)程主機(jī)的地址(目標(biāo)地址)。這個(gè)方法常用于確定發(fā)送UDP數(shù)據(jù)報(bào)的主機(jī)地址惶傻,使接收方可以回復(fù)棍郎。www.ibiblio.org/152.19.134.40
- getPort():返回一個(gè)int整數(shù),指示遠(yuǎn)程端口號(hào)银室。同樣的涂佃,如果數(shù)據(jù)報(bào)是從Internet接收的,返回的端口號(hào)則是發(fā)送該數(shù)據(jù)報(bào)的機(jī)器的端口號(hào)(源端口號(hào))蜈敢;如果數(shù)據(jù)報(bào)是本地創(chuàng)建準(zhǔn)備發(fā)送到遠(yuǎn)程主機(jī)的辜荠,返回的端口號(hào)則是要發(fā)往的遠(yuǎn)程主機(jī)的端口號(hào)(目標(biāo)端口號(hào))。
- getSocketAddress():返回一個(gè)SocketAddress對(duì)象抓狭,指示遠(yuǎn)程主機(jī)的IP地址和端口號(hào)伯病。如果數(shù)據(jù)報(bào)是從Internet接收的,返回的地址則是發(fā)送該數(shù)據(jù)報(bào)的機(jī)器的地址(源地址)否过;如果數(shù)據(jù)報(bào)是本地創(chuàng)建準(zhǔn)備發(fā)送到遠(yuǎn)程主機(jī)的午笛,返回的地址則是要發(fā)往的遠(yuǎn)程主機(jī)的地址(目標(biāo)地址)惭蟋。一般要在回復(fù)前調(diào)用這個(gè)方法,獲取發(fā)送這個(gè)DatagramPacket數(shù)據(jù)報(bào)的主機(jī)地址和端口號(hào)药磺,以便進(jìn)行回復(fù)告组。與同時(shí)調(diào)用getAddress()和getPort()相比,實(shí)際結(jié)果沒有顯著區(qū)別癌佩。www.ibiblio.org/152.19.134.40:7
- getData():返回一個(gè)byte數(shù)組木缝,指示數(shù)據(jù)報(bào)中的數(shù)據(jù)部分。通常必須將接收到的字節(jié)數(shù)組轉(zhuǎn)化為其他的某種數(shù)據(jù)形式以便使用围辙。
- getLength():返回一個(gè)int整數(shù)我碟,指示數(shù)據(jù)報(bào)中數(shù)據(jù)部分的字節(jié)數(shù)。它不一定等于getData()返回的數(shù)組長(zhǎng)度(即getData().length)酌畜,甚至可能小于怎囚。
- getOffset():返回一個(gè)int整數(shù),指示該字節(jié)數(shù)組中的一個(gè)位置桥胞,即開始填充數(shù)據(jù)報(bào)的位置恳守。
// 轉(zhuǎn)化接收到的字節(jié)數(shù)組的技巧
// 1. 直接調(diào)用String的構(gòu)造函數(shù)進(jìn)行轉(zhuǎn)化
String data = new String(packet.getData(), "UTF-8");
// 2. 如果數(shù)據(jù)報(bào)不包含文本,可以轉(zhuǎn)化為ByteArrayInputStream
InputStream in = new ByteArrayInputStream(packet.getData(), packet.getOffset(), packet.getLength());
DataInputStream din = new DataInputStream(in);
// 這里可以使用DataInputStream的readInt()贩虾、readChar()等方法直接讀取...
- 設(shè)置DatagramPacket信息的方法
DatagramPacket提供了6個(gè)方法可以在創(chuàng)建數(shù)據(jù)報(bào)之后改變數(shù)據(jù)催烘、遠(yuǎn)程地址、遠(yuǎn)程端口號(hào)缎罢。如果創(chuàng)建和銷毀DatagramPacket對(duì)象的時(shí)間會(huì)嚴(yán)重影響性能伊群,那么這些方法就很重要了。有些情況下策精,重用對(duì)象比構(gòu)造新對(duì)象要快得多舰始。
public synchronized void setData(byte[] buf)
public synchronized void setData(byte[] buf, int offset, int length)
public synchronized void setAddress(InetAddress iaddr)
public synchronized void setPort(int iport)
public synchronized void setSocketAddress(SocketAddress address)
public synchronized void setLength(int length)
- setData():該方法會(huì)改變數(shù)據(jù)報(bào)的數(shù)據(jù)部分。如果要向遠(yuǎn)程主機(jī)發(fā)送大文件可能會(huì)用到這個(gè)方法咽袜。
- setData():該重載方法提供另一個(gè)途徑來發(fā)送大量的數(shù)據(jù)丸卷。與發(fā)送大量新數(shù)組不同,可以將所有數(shù)據(jù)放在一個(gè)數(shù)據(jù)中询刹,每次發(fā)送一部分谜嫉。
- setAddress():該方法會(huì)修改數(shù)據(jù)報(bào)發(fā)往的地址。允許將同一個(gè)數(shù)據(jù)報(bào)發(fā)送給多個(gè)不同的接收方凹联。
- setPort():該方法會(huì)修改數(shù)據(jù)報(bào)發(fā)往的端口沐兰。
- setSocketAddress():該方法會(huì)修改數(shù)據(jù)報(bào)要發(fā)往的地址和端口。在響應(yīng)回復(fù)時(shí)可以使用這個(gè)方法蔽挠。
- setLength():該方法會(huì)修改內(nèi)部緩沖區(qū)中包含實(shí)際數(shù)據(jù)報(bào)數(shù)據(jù)的字節(jié)數(shù)住闯,而不包括未填充數(shù)據(jù)的空間。這個(gè)方法在接收數(shù)據(jù)報(bào)時(shí)很有用,當(dāng)接收到數(shù)據(jù)報(bào)時(shí)寞秃,其長(zhǎng)度設(shè)置為入站數(shù)據(jù)的長(zhǎng)度斟叼。
DatagramSocket
要發(fā)送和接受數(shù)據(jù)報(bào)DatagramPacket,必須通過DatagramSocket進(jìn)行傳輸春寿,所有DatagramSocket都綁定到一個(gè)本地端口朗涩,在這個(gè)端口上監(jiān)聽入站數(shù)據(jù),這個(gè)端口也會(huì)放置到出站數(shù)據(jù)報(bào)的首部中绑改,以便服務(wù)器用來響應(yīng)數(shù)據(jù)報(bào)的發(fā)送地址谢床。一般情況下,客戶端使用匿名端口厘线,服務(wù)器需要指定監(jiān)聽端口识腿。
DatagramSocket相關(guān)API
- 構(gòu)造函數(shù)
public DatagramSocket() throws SocketException
public DatagramSocket(int port) throws SocketException
public DatagramSocket(int port, InetAddress laddr) throws SocketException
public DatagramSocket(SocketAddress bindaddr) throws SocketException
protected DatagramSocket(DatagramSocketImpl impl)
所有構(gòu)造函數(shù)都只處理本地地址和端口,遠(yuǎn)程地址和端口存儲(chǔ)在DatagramPacket中造壮,一個(gè)DatagramSocket可以從多個(gè)遠(yuǎn)程主機(jī)和端口收發(fā)數(shù)據(jù)報(bào)渡讼。
- 該構(gòu)造函數(shù)創(chuàng)建一個(gè)綁定到匿名端口的Socket,系統(tǒng)會(huì)自動(dòng)為客戶端分配一個(gè)端口耳璧。
- 該構(gòu)造函數(shù)創(chuàng)建一個(gè)指定端口的Socket成箫,可以使用這個(gè)方法編寫在已知端口監(jiān)聽的服務(wù)器。
- 該構(gòu)造函數(shù)創(chuàng)建在指定端口和網(wǎng)絡(luò)接口的Socket旨枯,多用于多宿主主機(jī)(一個(gè)主機(jī)有多個(gè)IP地址)蹬昌。第二個(gè)參數(shù)是匹配該主機(jī)某個(gè)網(wǎng)絡(luò)接口的InetAddress對(duì)象。
- 該構(gòu)造函數(shù)與第3個(gè)相似攀隔,只是網(wǎng)絡(luò)接口地址和端口由SocketAddress包裝皂贩。
- 該構(gòu)造函數(shù)允許子類提供自己的UDP實(shí)現(xiàn),而不接受默認(rèn)實(shí)現(xiàn)昆汹。與其他四個(gè)方法不同明刷,這個(gè)Socket一開始沒有與端口綁定,使用前必須通過bind()方法綁定到一個(gè)SocketAddress满粗。
- 發(fā)送和接受數(shù)據(jù)報(bào)
public void send(DatagramPacket p) throws IOException
public synchronized void receive(DatagramPacket p) throws IOException
public void close()
public int getLocalPort()
public InetAddress getLocalAddress()
- getLocalPort():返回一個(gè)int整數(shù)遮精,表示Socket正在監(jiān)聽的本地端口。如果創(chuàng)建了一個(gè)匿名端口的DatagramSocket败潦,可以使用該方法獲取到監(jiān)聽的端口號(hào)。
- getLocalAddress():返回一個(gè)InetAddress對(duì)象准脂,表示Socket綁定到的本地地址劫扒。實(shí)際中很少這樣做,因?yàn)槟阋呀?jīng)知道了監(jiān)聽的地址狸膏。
- 管理連接
public void connect(InetAddress address, int port)
public void connect(SocketAddress addr) throws SocketException
public void disconnect()
public int getPort()
public InetAddress getInetAddress()
public SocketAddress getRemoteSocketAddress()
利用上面5個(gè)方法沟饥,可以選擇允許收發(fā)數(shù)據(jù)報(bào)的主機(jī),而拒絕其它所有主機(jī)的包。
- connect():并不真正建立TCP意義上的連接贤旷,不過它確實(shí)指定了DatagramSocket只對(duì)指定遠(yuǎn)程主機(jī)和指定遠(yuǎn)程端口發(fā)送和接受數(shù)據(jù)報(bào)广料。試圖向其它主機(jī)和端口發(fā)送數(shù)據(jù)報(bào)將拋出異常,而從其它主機(jī)和端口接受的數(shù)據(jù)報(bào)將直接丟棄幼驶,沒有異常也沒有通知艾杏。
- disconnect():中斷連接,從而可以再次收發(fā)其它主機(jī)和端口的數(shù)據(jù)報(bào)盅藻。
- getPort():當(dāng)且僅當(dāng)DatagramSocket已連接時(shí)购桑,該方法返回所連接的遠(yuǎn)程端口,否則返回-1氏淑。
- getInetAddress():當(dāng)且僅當(dāng)DatagramSocket已連接時(shí)勃蜘,該方法返回所連接的遠(yuǎn)程主機(jī)地址,否則返回null假残。
- getRemoteSocketAddress():如果DatagramSocket已連接缭贡,該方法返回所連接的遠(yuǎn)程主機(jī)的地址,否則返回null辉懒。
設(shè)置DatagramSocket屬性
- SO_TIMEOUT
- SO_RCVBUF
- SO_SNDBUF
- SO_REUSEADDR
- SO_BROADCAST
- IP_TOS
- SO_TIMEOUT
public synchronized void setSoTimeout(int timeout) throws SocketException
該屬性是receive()方法在拋出InterruptedException異常前等待接收數(shù)據(jù)報(bào)的時(shí)間阳惹。如果該屬性值為0,則receive()方法永遠(yuǎn)不會(huì)超時(shí)耗帕。如果要實(shí)現(xiàn)一個(gè)安全協(xié)議穆端,需要在一定時(shí)間內(nèi)響應(yīng)就可能需要設(shè)置該屬性。setSoTimeout()方法可以設(shè)置超時(shí)時(shí)間仿便,如果超時(shí)了体啰,阻塞的receive()方法就會(huì)拋出SocketTimeoutException異常。必須在receive()方法前調(diào)用嗽仪。
- SO_RCVBUF
public synchronized void setReceiveBufferSize(int size) throws SocketException
對(duì)于相當(dāng)快的連接(如以太網(wǎng)的連接)荒勇,較大的緩沖區(qū)有助于提升性能,因?yàn)樵谝绯銮翱梢源鎯?chǔ)更多的入站數(shù)據(jù)報(bào)闻坚。與TCP相比沽翔,對(duì)于UDP而言,足夠大的接收緩沖區(qū)甚至更為重要窿凤,因?yàn)樵诰彌_區(qū)滿時(shí)到達(dá)的UDP數(shù)據(jù)報(bào)會(huì)丟失仅偎,而緩沖區(qū)滿時(shí)到達(dá)的TCP數(shù)據(jù)報(bào)最后還會(huì)重傳。
- SO_SNDBUF
public synchronized void setSendBufferSize(int size) throws SocketException
設(shè)置網(wǎng)絡(luò)輸出的發(fā)送緩沖區(qū)大小雳殊。
- SO_REUSEADDR
public synchronized void setReuseAddress(boolean on) throws SocketException
對(duì)于UDP來說橘沥,該屬性可以控制是否允許多個(gè)數(shù)據(jù)報(bào)Socket同時(shí)綁定到相同的端口和地址。如果多個(gè)Socket綁定到相同端口夯秃,接收的包將復(fù)制給綁定的所有Socket座咆。必須在新Socket綁定到端口前調(diào)用setReuseAddress()痢艺。
- SO_BROADCAST
public synchronized void setBroadcast(boolean on) throws SocketException
該屬性控制是否允許一個(gè)Socket向廣播地址收發(fā)數(shù)據(jù)報(bào)。默認(rèn)為true介陶。