## UDP 簡介 UDP(User Datagram Protocol,用戶數據報協議)是傳輸層的另一種協議,比 TCP 具有更快的傳輸速度,但是不可靠。UDP 發送的數據單元被稱為 UDP 數據報,當網路傳輸 UDP 數據報時,無法保證數據報一定到達目的地,也無法保證各個數據報按發送的順序到達目 ...
UDP 簡介
UDP(User Datagram Protocol,用戶數據報協議)是傳輸層的另一種協議,比 TCP 具有更快的傳輸速度,但是不可靠。UDP 發送的數據單元被稱為 UDP 數據報,當網路傳輸 UDP 數據報時,無法保證數據報一定到達目的地,也無法保證各個數據報按發送的順序到達目的地,例如:當發送方先發送包含字元串【hello】的數據報,再發送包含字元串【everyone】的數據報時,接收方有可能先接收到字元串【everyone】,再接收到字元串【hello】,也有可能什麼數據也沒有接收到,因為發送方發送的數據有可能在傳輸途中都被丟失了
DatagramPacket 類
DatagramPacket 表示數據報,它的構造方法可以被分為兩種:
- 創建用來接收數據的 DatagramPacket 對象
- 創建用來發送數據的 DatagramPacket 對象
兩種構造方法的主要區別是:
- 用於發送數據的構造方法需要設定數據報到達的目的地的地址
- 用於接收數據的構造方法無須設定地址
用於接收數據的構造方法包括:
//data:用來存放接收到的數據
//length:指定要接收的位元組數
//offset:指定在data存放數據的起始位置,即data[offset],如果沒有設定,那麼初始位置為data[0]
public DatagramPacket(byte[] data, int length);
public DatagramPacket(bytel data, int offset, int length);
用於發送數據的構造方法包括:
//data:用來存放要發送的數據
//length:指定要發送的位元組數
//offset:指定在data要發送數據的起始位置,即data[offset],如果沒有設定,那麼初始位置為data[0]
//address和port:來指定目的地的地址
public DatagramPacket(byte[] data, int offset, int length, InetAddress address, int port);
public DatagramPacket(byte[] data, int offset, int length, SocketAddress address);
public DatagramPacket(byte[] data, int length, InetAddress address, int port);
public DatagramPacket(byte[] data, int length, SocketAddress address);
DatagramPacket 類包括以下屬性,提供了一系列 get/set 方法來獲取或設置這些屬性:
- data:表示數據報的數據緩衝區
- offset:表示數據報的數據緩衝區的起始位置
- length: 表示數據報的長度
- address:對於用於發送的數據報,address 屬性表示數據報的目標地址。對於用於接收的數據報,address 屬性表示發送者的地址
- port:對於用於發送的數據報,port 屬性表示數據報的目標 UDP 埠。對於用於接收的數據報,port 屬性表示發送者的 UDP 埠
DatagramSocket 類
DatagramSocket 類負責接收和發送數據報,每個 DatagramSocket 對象都會與一個本地埠綁定,在此埠監聽發送過來的數據報。在客戶程式中,一般由操作系統為 DatagramSocket 類分配本地埠,這種埠也被稱為匿名埠。在伺服器程式中,一般由程式顯式地為 DatagramSocket 類指定本地埠
DatagramSocket 的構造方法有以下幾種重載形式:
//由操作系統分配的任意的可用埠
DatagramSocket();
//顯式指定本地埠
DatagramSocket(int port);
//同時指定IP和埠
DatagramSocket(int port, InetAddress laddr);
DatagramSocket(SocketAddress bindaddr);
DatagramSocket 的 send 方法負責發送一個數據報,該方法的定義如下:
public void send(DatagramPacket dp) throws IOException
值得註意的是,UDP 提供不可靠的傳輸,如果數據報沒有到達目的地,那麼 send 方法不會拋出任何異常,發送方程式就無法知道數據報是否被接收方接收到,除非雙方通過應用層的特定協議來確保接收方未收到數據報時,發送方能重發數據報
DatagramSocket 的 receive 方法負責接收一個數據報,該方法的定義如下:
public void receive(DatagramPacket datagramPacket) throws IOException
此方法從網路上接收一個數據報,如果網路上沒有數據報,執行該方法的線程就會進入阻塞狀態,直到收到數據報為止。參數 datagramPacket 用來存放接收到的數據報,如果接收的數據報太大,receive 方法會在 datagramPacket 的數據緩衝區記憶體放儘可能多的數據,其餘的數據則丟棄
兩個 TCP Socket 之間存在固定的連接關係,而一個 DatagramSocket 可以與其他任意一個 DatagramSocket 交換數據報。在某些場合,一個 DatagramSocket 可能只希望與固定的另一個遠程 DatagramSocket 通信,從 JDK1.2 開始,DatagramSocket 添加了一些方法,利用這些方法,可以使一個 DatagramSocket 只能與另一個固定的 DatagramSocket 交換數據報
//該方法實際上不建立TCP意義上的連接,但能限制當前DataramSocket只對參數指定的遠程主機和UDP埠收發數據報
public void connect(InetAddress host, int port);
//中止當前DatagramSocket已經建立的連接
public void disconnect();
//當且僅當DatagramSocket已經建立連接時返回所連接的遠程UDP埠,否則返回“-1”
public int getPort();
//當且僅當DatagramSocket已經建立連接時返回所連接的遠程主機的IP地址,否則返回null
public InetAddress getlnetAddress();
//當且僅當DatagramSocket已經建立連接時返回所連接的SocketAddress,否則返回null
public SocketAddress getRemoteSocketAddress();
DatagramSocket 的 close 方法會釋放所占用的本地 UDP 埠,在程式中及時關閉不再需要的 DatagramSocket,這是好的編程習慣
DatagramChannel 類
DatagramChannel 是 SelectableChannel 的子類,可以被註冊到一個 Selector。使用 DatagramChannel,可以使 UDP 伺服器只需用單個線程就能同時與多個客戶通信。DatagramChannel 在預設情況下採用阻塞模式,如果希望該為非阻塞模式,那麼可以調用 configureBlocking(false) 方法
DatagramChannel 類的靜態 open 方法返回一個 DatagramChannel 對象,每個 DatagramChannel 對象都關聯了一個 DatagramSocket 對象,DatagramChannel 對象的 socket 方法返回這個 DatagramSocket 對象
DatagramChannel 對象被創建後,與它關聯的 DatagramSocket 對象還沒有被綁定到任何地址,必須調用 DatagramSocket 對象的 bind 方法來與一個本地地址綁定
DatagramChannel channel = DatagramChannel.open();
DataqramSocket socket = channel.socket();
SocketAddress address = new InetSocketAddress(8000);
socket.bind(address);
與 DatagramSocket 一樣,DatagramChannel 的 connect(SocketAddress remote) 方法使得通道只能對特定的遠程地址收發數據報
DatagramChannel 使用 send 方法發送數據
//把參數src的剩餘數據作為一個數據報寫到通道中,參數target指定目標地址,該方法返回發送的位元組數
public int send(ByteBuffer src, SocketAddress target) throws IOException
DatagramChannel 使用 receive 方法從通道中讀取一個數據報,存放在參數指定的 ByteBufer,並返回數據報的發送方的地址
public SocketAddress receive(ByteBuffer dst) throws IOException
如果 DatagramChannel 工作於阻塞模式,那麼 receive 方法在讀取到數據報之前不會返回。如果 DatagramChannel 工作於非阻寒模式,那麼 receive 方法在沒有數據報可讀取的情況下立即返回 null
DatagramChannel 也可以使用 write 方法發送數據,與 send 方法區別在於:write 方法要求 DatagramChannel 已經建立連接
public int write(ByteBuffer src) throws IOException;
//依次發送ByteBuffer數組中每個ByteBuffer的數據
public long write(ByteBufferl] srcs) throws IOException;
//參數offse指定ByteBuffer數組的起始位置參數,length指定發送的ByteBuffer的數目
public long write(ByteBufferl] srcs, int offset, int length) throws IOException;
DatagramChannel 也可以使用 read 方法接收數據,同樣的,它也要求 DatagramChannel 已經建立連接
public int read(ByteBuffer src) throws IOException;
//把數據報的數據保存到 ByteBuffer 數組中
public long read(ByteBuffer[l srcs) throws IOException;
public long read(ByteBuffer[] srcs, int offset, int length) throws IOException;