NIO之Channel通道(三)-DatagramChannel
用於UDP協議的數據讀寫
DatagramSocket是對UDP的封裝,DatagramSocket本身不維護連接的狀態,因爲UDP協議面向非連接,所以也不會產生IO流,只是用來發送與接收數據報。在java中數據報使用DatagramPacket來表示,所以最有用的方法是send與receive,表示發送與接收報文。可以使用DatagramSocket來收發數據報,也可以使用DatagramChannel來收發數據。
UDP的這種方式,定義服務端與客戶端都是DatagramSocket,該類作爲兩個端點,只是用來接收報文與發送報文。兩個DatagramSocket之間的交互使用DatagramPacker來交換信息。由於UDP面向無連接,所以兩個端點端不需要持有另一端的地址以及port信息。而兩個端點進行交互時使用的DatagramPacker含有該報文發送方的信息。
1重要方法
1.1open()
打開數據報通道。
通過調用系統級默認SelectorProvider對象的openDatagramChannel方法來創建新的通道。該通道不會被連接。
- 返回:新的數據報通道
- 拋出:IOException-如果發生I/O錯誤
1.2validOps()
返回一個操作集,標識此通道所支持的操作。
數據報通道支持讀取和寫入操作,所以此方法返回(SelectionKey.OP_READ|SelectionKey.OP_WRITE)。
- 指定者:類SelectableChannel中的validOps
- 返回:有效操作集
1.3socket()
檢索與此通道關聯的數據報套接字。
返回的對象不會聲明任何在DatagramSocket類中未聲明的公共方法。
- 返回:與此通道關聯的數據報套接字
1.4isConnected()
判斷是否已連接此通道的套接字。
- 返回:當且僅當已連接此通道的套接字時才返回 true
1.5connect(SocketAddress remote)
連接此通道的套接字。
配置該通道的套接字,以便該套接字僅和給定的遠程同位體地址進行數據報的接收和發送。一旦連接後,就無法和任何其他地址進行數據報的接收或發送。在顯式地斷開數據報套接字的連接或將其關閉之前,該套接字始終保持連接狀態。
此方法執行的安全檢查與DatagramSocket類的connect方法執行的安全檢查完全相同。也就是說,如果已安裝了安全管理器,則此方法驗證其checkAccept和checkConnect方法是否分別允許接收來自給定遠程地址的數據報和向其發送數據報。
可在任意時間調用此方法。此方法對調用它時正在進行的讀取或寫入操作沒有任何影響。
- 參數:remote-與此通道連接的遠程地址
- 返回:此數據報通道
- 拋出:
- ClosedChannelException-如果此通道已關閉
- AsynchronousCloseException-如果正在進行連接操作時另一個線程關閉了此通道
- ClosedByInterruptException-如果正在進行連接操作時另一個線程中斷了當前線程,因此關閉了該通道並將當前線程設置爲中斷狀態
- SecurityException-如果已安裝安全管理器並且它不允許對給定遠程地址進行訪問
- IOException-如果發生其他I/O錯誤
1.6disconnect()
斷開此通道套接字的連接。
配置該通道的套接字,只要安全管理器允許(如果已安裝),該套接字就可和任何遠程地址進行數據報的接收和發送。
可在任意時間調用此方法。此方法對調用它時正在進行的讀取或寫入操作沒有任何影響。
如果未連接此通道的套接字,或者通道已關閉,則調用此方法無效。
- 返回:此數據報通道
- 拋出:IOException-如果發生其他I/O錯誤
1.7receive(ByteBuffer dst)
通過此通道接收數據報。
如果數據報直接可用,並且此通道處於阻塞模式但最終會變得可用,則將數據報復制到給定的字節緩衝區中並返回數據報的源地址。如果此通道處於非阻塞模式並且沒有直接可用的數據報,則此方法直接返回null。
該數據報被傳輸到給定的字節緩衝區中,並從緩衝區的當前位置開始存儲,如同正規的read操作一樣。如果緩衝區中的剩餘字節空間小於保存數據報所需的空間,則丟棄餘下的數據報。
此方法執行的安全檢查與DatagramSocket類的receive方法執行的安全檢查完全相同。也就是說,如果該套接字未連接到特定的遠程地址,並且已安裝了安全管理器,則對於接收到的每個數據報,此方法都會驗證安全管理器的checkAccept方法是否允許使用該數據報的源地址和端口號。避免此項安全檢查開銷的方法是首先通過connect方法連接該套接字。
可在任意時間調用此方法。但是如果另一個線程已經在此通道上發起了一個讀取操作,則在該操作完成前此方法的調用被阻塞。
- 參數:dst-要向其中傳輸數據報的緩衝區
- 返回:數據報的源地址,或者如果此通道處於非阻塞模式並且沒有直接可用的數據報,則返回null
- 拋出:
- ClosedChannelException-如果此通道已關閉
- AsynchronousCloseException-如果正在進行讀取操作時另一個線程關閉了此通道
- ClosedByInterruptException-如果正在進行讀取操作時另一個線程中斷了當前線程,因此關閉了該通道並將當前線程設置爲中斷狀態
- SecurityException-如果已安裝安全管理器並且它不允許接受該數據報發送者所發送的數據報
- IOException-如果發生其他I/O錯誤
1.8send(ByteBuffer src,SocketAddress target)
通過此通道發送數據報。
如果此通道處於非阻塞模式並且基礎輸出緩衝區中沒有足夠的空間,或者如果此通道處於阻塞模式並且緩衝區中有足夠的空間,則將給定緩衝區中的剩餘字節以單個數據報的形式傳送到給定的目標地址。
從字節緩衝區傳輸數據報如同通過正規的write操作一樣。
此方法執行的安全檢查與DatagramSocket類的send方法執行的安全檢查完全相同。也就是說,如果該套接字未連接到指定的遠程地址,並且已安裝了安全管理器,則對於每個發送的數據報,此方法都會驗證安全管理器的checkConnect方法是否允許使用該數據報的目標地址和端口號。避免此項安全檢查開銷的方法是首先通過connect方法連接該套接字。
可在任意時間調用此方法。但是如果另一個線程已經在此通道上發起了一個寫入操作,則在該操作完成前此方法的調用被阻塞。
- 參數:
- src-包含要發送的數據報的緩衝區
- target-要將數據報發送到的地址
- 返回:發送的字節數,可能是調用此方法時源緩衝區中剩餘的字節數,或者如果此通道處於非阻塞模式並且基礎輸出緩衝區中沒有足夠的空間供數據報使用,則可能爲零
- 拋出:
- ClosedChannelException-如果此通道已關閉
- AsynchronousCloseException-如果正在進行讀取操作時另一個線程關閉了此通道
- ClosedByInterruptException-如果正在進行讀取操作時另一個線程中斷了當前線程,因此關閉了該通道並將當前線程設置爲中斷狀態
- SecurityException-如果已安裝安全管理器並且它不允許將數據報發送到給定地址
- IOException-如果發生其他I/O錯誤
1.9read()
從此通道讀取數據報。
僅在此通道的套接字已連接時才調用此方法,並且此方法僅接受來自該套接字同位體的數據報。如果數據報中的字節數大於給定緩衝區中的剩餘空間,則丟棄餘下的數據報。否則此方法的行爲與ReadableByteChannel接口中指定的行爲完全相同。
- 指定者:接口ReadableByteChannel中的read
- 參數:dst-要向其中傳輸字節的緩衝區
- 返回:讀取的字節數,可能爲零,如果該通道已到達流的末尾,則返回-1
- 拋出:
- NotYetConnectedException-如果未連接此通道的套接字
- ClosedChannelException-如果此通道已關閉
- AsynchronousCloseException-如果正在進行讀取操作時另一個線程關閉了此通道
- ClosedByInterruptException-如果正在進行讀取操作時另一個線程中斷了當前線程,因此關閉了該通道並將當前線程設置爲中斷狀態
- IOException-如果發生其他I/O錯誤
重載的方法:
- read(ByteBuffer dst)
- read(ByteBuffer[] dsts,int offset,int length)
- read(ByteBuffer[] dsts)
1.10write()
將數據報寫入此通道。
僅在此通道的套接字已連接時才調用此方法,在這種情況下,此方法將數據報直接發送到套接字的同位體。否則此方法的行爲與WritableByteChannel接口中指定的行爲完全相同。
- 指定者:接口WritableByteChannel中的write
- 參數:src-要從中檢索字節的緩衝區
- 返回:寫入的字節數,可能爲零
- 拋出:
- NotYetConnectedException-如果未連接此通道的套接字
- ClosedChannelException-如果此通道已關閉
- AsynchronousCloseException-如果正在進行寫入操作時另一個線程關閉了此通道
- ClosedByInterruptException-如果正在進行寫入操作時另一個線程中斷了當前線程,因此關閉了該通道並將當前線程的狀態設置爲中斷
- IOException-如果發生其他I/O錯誤
重載方法
- write(ByteBuffer src)
- write(ByteBuffer[] srcs,int offset,int length)
- write(ByteBuffer[] srcs)
2案例
服務端:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class DatagramSocketServerTest {
private static byte[] buf = new byte[1024];
@SuppressWarnings("resource")
public static void main(String[] args) {
try {
// 綁定8090端口,監聽數據
DatagramSocket ds = new DatagramSocket(8090);
// 定義接收的數據包
DatagramPacket dp = new DatagramPacket(buf, buf.length);
// 阻塞,等待數據
ds.receive(dp);
System.out.println("接收的數據:" + new String(buf, 0, dp.getLength()));
// 定義要發送的數據包
DatagramPacket sendpacket = new DatagramPacket(buf, buf.length, dp.getAddress(), dp.getPort());
ds.send(sendpacket);
} catch (Exception e) {
e.printStackTrace();
}
}
}
客戶端:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
public class DatagramSocketClient {
public static void main(String[] args) {
try {
DatagramSocket ds = new DatagramSocket();
byte[] buf = new byte[1024];
DatagramPacket sdp = new DatagramPacket(new byte[0], 0, InetAddress.getByName("127.0.0.1"), 8090);
DatagramPacket rdp = new DatagramPacket(buf, buf.length);
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()) {
byte[] bytes = scanner.nextLine().getBytes();
sdp.setData(bytes);
ds.send(sdp);
ds.receive(rdp);
System.out.println(new String(buf, 0, rdp.getLength()));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}