NIO之Channel通道(三)-DatagramChannel

2022-04-25 14:21:04 浏览数 (1)

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案例

服务端:

代码语言:javascript复制
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();
		}
	}
}

客户端:

代码语言:javascript复制
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();
		}
	}
}

0 人点赞