文章目錄
1. 前言
基於傳輸層協議的UDP的網絡通信是不可靠的、無序的、無差錯控制的。使用DatagramSocket類表示UDP通信節點的套接字,使用DatagramPacket表示節點之間發送和接收的數據報。基於UDP通信的節點之間不需要建立任何連接。組播也是一種基於UDP的通信。
2. DatagramSocket 類
每個DatagramSocket對象會綁定本地IP地址和一個UDP端口號,它可以和任意其他DatagramSocket對象之間有通信行爲,但不會建立實時的網絡連接。
2.1 構造DatagramSocket
- DatagramSocket():創建一個DatagramSocket對象,但沒有設置綁定的端口號,相當於綁定了本地的任意一個可用的端口
- DatagramSocket(int port):創建一個DatagramSocket對象,並綁定本地端口號port。
- DatagramSocket(int port,InetAddress addr):創建一個DatagramSocket對象,並綁定本地地址addr和端口號port。IP地址爲網絡接口地址,如果設置爲0.0.0.0,就是通配地址
- DatagramSocket(SocketAddress bindaddr):創建一個DatagramSocket對象,並綁定套接字地址bindaddr
- void bind(SocketAddress addr):如果使用無參數構造方法創建一個DatagramSocket對象,可以進行一些選項的設置,之後使用bind方法綁定IP地址和端口
2.2 DatagramSocket 類的常用方法
2.2.1 發送數據
DatagramSocket的send方法負責發送一個數據報,該方法的定義:void send(DatagramPacket p),其中參數p包含要發送的數據、數據長度、目的IP地址和端口。
//1234本地端口號
DatagramSocket ds = new DatagramSocket(1234);
InetAddress receiver = InetAddress.getLocalHost();
byte[] b = "send a message".getBytes();
//5678目的端口號
DatagramPacket dp = new DatagramPacket(b, b.length, receiver, 5678);
ds.send(dp)
2.2.2 接收數據
void receive(DatagramPacket p): DatagramSocket類的receive方法用於接收消息。消息並不是以返回值的形式得到的,而是存在於p緩衝區中,p中還包括髮送者的IP地址和端口信息。
receive方法是一個阻塞的方法,調用receive的時候,如果沒有收到數據報會一直阻塞,直到收到一個數據報。
byte[] b = new byte[100];
DatagramPacket dp = new DatagramPacket(b,100);
DatagramSocket ds = new DatagramSocket(1234);
ds.receive(dp);
2.2.3 建立固定通信關係
UDP的節點之間是不建立實時連接的,但是卻可以建立這樣一種固定關係:一個節點的DatagramSocket,只能同另一個固定的節點(由IP地址和端口號確定)進行通信
DatagramSocket ds = new DatagramSocket();
ds.connect(InetSocketAddress.createUnresolved("www.foo.com",1234));
2.2.4 解除固定通信關係
DatagramSocket ds = new DatagramSocket(1234);
...
ds.disconnect();
2.2.5 關閉 DatagramSocket
void close():關閉,並釋放所有相關的資源。
...
ds.close();
補充:
- isBound(): 判斷DatagramSocket對象的綁定狀態
- isConnected():判斷DatagramSocket對象是否處於固定通信連接狀態
- isClosed():判斷DatagramSocket對象是否關閉
2.3 設置 DatagramSocket 的選項
選項 | 含義 |
---|---|
SO_BROADCAST | 廣播地址 |
SO_TIMEOUT | 設定接收數據報的等待超時時間 |
SO_RECBUF | 表示接收數據緩衝區的大小 |
SO_REUSEADDR | 表示是否允許重用DatagramSocket所綁定的本地地址 |
3. DatagramPacket 類
3.1 DatagramPacket 類的構造方法
- 發送數據的DatagramPacket對象
- DatagramPacket(byte[] buf, int length, InetAddress address, int port):用於發送的DatagramPacket對象,包括目的節點的IP地址address和端口號port。
- DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port):設置了發送數據的起始位置爲:data[offset]
- DatagramPacket(byte[] buf, int offset, int length, SocketAddress address):將目的地址和端口號合併在了一起
- DatagramPacket(byte[] buf, int length, SocketAddress address)
- 接收數據的DatagramPacket對象
- DatagramPacket(byte[] buf, int length):用於接收數據,只需設置接收緩衝區buf,並指定讀取的字節數length
- DatagramPacket(byte[] buf, int offset, int length):設置了接收緩衝區buf的起始位置偏移量
3.2 DatagramPacket 類的常用方法
3.2.1 查詢 DatagramPacket
- InetAddress getAddress():對於發送數據報,返回目的節點的主機IP地址。對於接收數據報,返回的是數據的來源主機IP地址。總之,返回的是遠程主機的IP地址
- int getPort():返回的是遠程主機的UDP端口號。
- byte[] getData():對於發送數據報,返回的是發送緩衝區從offset開始的數據,對於接收數據,返回的是接收緩衝區的數據。
- int getOffset():返回的是發送或者接收緩衝區的數據偏移量offset
- int getLength():返回的是發送或者接收緩衝區中數據的長度
- SocketAddress getSocketAddress():返回的是遠程主機IP地址和UDP端口號
3.2.2 設置 DatagramPacket
- void setData(byte[] buf):設置數據報的緩衝區數據
- void setData(byte[] data, int offset, int length):數據從buf[offset]開始,長度爲length
- void setAddress(InetAddress iaddr):發送數據報時,使用參數iaddr設置目的主機的IP地址
- void setPort(int iport):發送數據報時,使用參數iport設置目的主機的UDP端口號。
- void setSocketAddress(SocketAddress address):發送數據報時,使用參數iaddr設置目的主機的套接字地址
- void setLength(int length):設置數據報的長度
4. 程序實例
4.1 UDPService
package udp;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.text.SimpleDateFormat;
import java.util.Date;
public class UDPService {
DatagramSocket ds = null;
public UDPService() throws Exception{
ds = new DatagramSocket(5678);
System.out.println("服務啓動");
}
public void service(){
new Thread(){
public void run(){
while(true){
try {
byte[] b = new byte[100];
DatagramPacket dp = new DatagramPacket(b, b.length);
ds.receive(dp);
String msg = new String(b, 0, dp.getLength());
System.out.println("從" + dp.getAddress() + ":" + dp.getPort() + "收到:" + msg);
if(msg.equalsIgnoreCase("date")){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年mm月dd日");
dp.setData(("date:" + sdf.format(new Date())).getBytes());
}else if(msg.equalsIgnoreCase("time")){
SimpleDateFormat sdf = new SimpleDateFormat("HH:MM:SS");
dp.setData(("time:"+ sdf.format(new Date())).getBytes());
}
ds.send(dp);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}.start();
}
public static void main(String[] args) throws Exception {
new UDPService().service();
}
}
4.2 UDPClient
package udp;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.*;
public class UDPClient {
public static void main(String[] args) {
try {
InetAddress server = InetAddress.getByName("localhost");
DatagramSocket ds = new DatagramSocket();
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String msg = null;
while((msg = br.readLine())!= null){
byte[] b = msg.getBytes();
DatagramPacket dp = new DatagramPacket(b, b.length, server, 5678);
ds.send(dp);
DatagramPacket sp = new DatagramPacket(new byte[100], 100);
ds.receive(sp);
msg = new String(sp.getData(),0, sp.getLength());
if(msg.equalsIgnoreCase("bye")){
break;
}
System.out.println("服務器:" + msg);
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
}
}
}
4.3 運行截圖
5. 組播Socket
組播也叫多播,組播組內的所有主機共享同一個D類IP地址,這種地址稱爲組播地址。一臺主機可以自由決定何時加入或離開一個組播組。組播地址是範圍在244.0.0.0~239.255.255.255之間的IP地址
5.1 MulticastSocket類
MulticastSocket類實際上是DatagramSocket類的子類,包含了DatagramSocket類的所有域和方法,還額外定義了與組播有關的一些方法。
5.2 構造 MulticastSocket
- MulticastSocket():創建MulticastSocket對象
- MulticastSocket(int port):創建綁定到端口port的MulticastSocket對象
- MulticastSocket(SocketAddress bindAddress):創建綁定到端口套接字地址bindAddress的MulticastSocket對象。
5.3 MulticastSocket 的常用方法
5.3.1 加入組播組
- void joinGroup(InetAddress mcastaddr) 其中mcastaddr是D類組播IP地址,如果要接收發送到組播組的數據,就必須要加入組播組
- void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf) 其中netIf參數設置了使用哪個網絡接口加入到組播組
5.3.2 離開組播組
- void leaveGroup(InetAddress mcastaddr) 其中,mcastaddr是D類組播IP地址,調用leaveGroup之前,MulticastSocket對象應該已經加入了某個組播組。
- void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf) 其中netIf設置了哪個網絡接口離開組播組。
5.3.3 設置網絡接口
- void setInterface(InetAddress inf) 假設主機有多個網絡接口,通過該方法設置究竟是哪一個接口參與組播操作。
5.3.4 查詢網絡接口
- InetAddress getInterface() 此方法返回用於組播的網路接口地址
6. 程序實例
6.1 MulticastSender
package udp;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.UnknownHostException;
public class MulticastSender {
public static void main(String[] args) throws IOException {
InetAddress group = InetAddress.getByName("226.0.0.1");
MulticastSocket ms = new MulticastSocket();
ms.joinGroup(group);
String msg = "Hello, everybody!";
byte[] b = msg.getBytes();
DatagramPacket dp = new DatagramPacket(b, b.length, group, 5678);
ms.send(dp);
System.out.println("發送問候給:"+ group + ":" + 5678);
ms.close();
}
}
6.2 MulticastReceiver
package udp;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
public class MulticastReceiver {
public static void main(String[] args) throws IOException {
InetAddress group = InetAddress.getByName("226.0.0.1");
MulticastSocket ms = new MulticastSocket(5678);
ms.joinGroup(group);
byte[] b = new byte[100];
DatagramPacket dp = new DatagramPacket(b, b.length);
ms.receive(dp);
String str = new String(dp.getData(), 0, dp.getLength());
System.out.println("從" + dp.getAddress().toString() + ":"+dp.getPort()+"收到消息");
System.out.println(str);
ms.leaveGroup(group);
ms.close();
}
}
6.3 運行截圖