java constructor_java select

java constructor_java select前面两篇文章介绍了NIO中的Buffer和Channel,有了之前的基础,这篇文章来介绍一下另一个比较重要的概念Selector。我们知道系统线程的切换是消耗系统资源的,如果我们每一个连接都用一个线程来管理,资源的开销会非常大,这个时候就可以用Selector。通过Se…

前面两篇文章介绍了NIO中的Buffer和Channel,有了之前的基础,这篇文章来介绍一下另一个比较重要的概念—-Selector。我们知道系统线程的切换是消耗系统资源的,如果我们每一个连接都用一个线程来管理,资源的开销会非常大,这个时候就可以用Selector。通过Selector可以实现一个线程管理多个Channel,如下图:

Selector

Selector使用

打开

使用之前获得一个Selector对象

Selector selector = Selector.open();

注册

要把Channel注册到Selector上,Channel必需是非阻塞的。因此FileChannel是无法注册到Selector的。如果注册的时候不调用configureBlocking方法就会抛出IllegalBlockingModeException异常。

SelectionKey

SelectionKey共有四种

  • OP_ACCEPT
  • OP_CONNECT
  • OP_WRITE
  • OP_READ

ServerSocketChannel注册

serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

ServerSocketChannel的Operation Set只能是OP_ACCEPT,如果在注册的时候添加了OP_CONNECT、OP_WRITE或OP_READ会报异常。例如按照以下写法

serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT | SelectionKey.OP_CONNECT);

就会抛出下面的异常

Exception in thread "main" java.lang.IllegalArgumentException
	at java.nio.channels.spi.AbstractSelectableChannel.register(AbstractSelectableChannel.java:199)
	at java.nio.channels.SelectableChannel.register(SelectableChannel.java:280)
	at com.nio.sample.selector.SelectorServerSocketChannelSample.main(SelectorServerSocketChannelSample.java:27)

ServerSocketChannel的validOps可以看到只有OP_ACCEPT是合法的

public final int validOps() {
    return SelectionKey.OP_ACCEPT;
}

SocketChannel注册

socketChannel.register(selector, SelectionKey.OP_CONNECT);

SocketChannel的Operation Set只能是OP_CONNECT、OP_WRITE和OP_READ,如果在注册的时候添加了OP_ACCEPT同样会报异常。

SocketChannel的validOps可以看到只有OP_READ、OP_WRITE、OP_CONNECT是合法的

public final int validOps() {
    return (SelectionKey.OP_READ
            | SelectionKey.OP_WRITE
            | SelectionKey.OP_CONNECT);
}

注册成功之后,我们通过一个demo实现,客户端和服务端交互:

服务端:

public static void main(String[] args) throws Exception {

	ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

	serverSocketChannel.socket().bind(new InetSocketAddress(9000));
	serverSocketChannel.configureBlocking(false);

	Selector selector = Selector.open();

	// configureBlocking 如果不设置非阻塞,register的时候会报异常
	// java.nio.channels.IllegalBlockingModeException
	serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

	while (true) {

		int selected = selector.select();
		
		if (selected > 0) {

			Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
			while (iterator.hasNext()) {

				SelectionKey selectionKey = iterator.next();
				iterator.remove();

				if (selectionKey.isAcceptable()) {
					System.err.println("Acceptable");
					SocketChannel socketChannel = serverSocketChannel.accept();
					socketChannel.configureBlocking(false);
					socketChannel.register(selector, SelectionKey.OP_READ);
				} else if (selectionKey.isReadable()) {
					System.err.println("Readable");
					SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
					ByteBuffer buffer = ByteBuffer.allocate(128);
					socketChannel.read(buffer);
					System.out.println("接收来自客户端的数据:" + new String(buffer.array()));
					selectionKey.interestOps(SelectionKey.OP_WRITE);
				} else if (selectionKey.isWritable()) {
					System.err.println("Writable");
					SocketChannel channel = (SocketChannel) selectionKey.channel();
					String content = "向客户端发送数据 : " + System.currentTimeMillis();
					ByteBuffer buffer = ByteBuffer.wrap(content.getBytes());
					channel.write(buffer);
					selectionKey.interestOps(SelectionKey.OP_READ);
				}
			}
		}
	}
}

我们来看一下服务端的逻辑

1、服务端注册到selector,然后interest set(ops)设置为SelectionKey.OP_ACCEPT等待客户端连接。

2、客户端连接到达,调用到selectionKey.isAcceptable()方法,接收客户端连接,然后获得一个channel,并把

interest set设置为SelectionKey.OP_READ等待从通道中读数据。

3、当客户端发送的数据到达,selectionKey.isReadable() 被触发,接收客户端的数据并打印,然后把selectionKey.interestOps 设置为SelectionKey.OP_WRITE,向客户端发送数据。

4、当可写之后selectionKey.isWritable()被触发,向客户端发送数据,同时selectionKey.interestOps再次设置为

SelectionKey.OP_READ等待客户端数据到达。

客户端:

public static void main(String[] args) throws IOException {

	SocketChannel socketChannel = SocketChannel.open();

	socketChannel.configureBlocking(false);
	Selector selector = Selector.open();
	socketChannel.register(selector, SelectionKey.OP_CONNECT);

	socketChannel.connect(new InetSocketAddress("127.0.0.1", 9000));

	while (true) {
		int select = selector.select();

		if (select > 0) {

			Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
			while (iterator.hasNext()) {

				SelectionKey selectionKey = iterator.next();

				if (selectionKey.isConnectable()) {
					System.err.println("Connectable");
					SocketChannel clientChannel = (SocketChannel) selectionKey.channel();
					clientChannel.finishConnect();
					selectionKey.interestOps(SelectionKey.OP_WRITE);

				} else if (selectionKey.isReadable()) {
					System.out.println("Readable");
					SocketChannel channel = (SocketChannel) selectionKey.channel();
					ByteBuffer buffer = ByteBuffer.allocate(128);
					channel.read(buffer);
					selectionKey.interestOps(SelectionKey.OP_WRITE);
					System.out.println("收到服务端数据" + new String(buffer.array()));

				} else if (selectionKey.isWritable()) {
					SocketChannel clientChannel = (SocketChannel) selectionKey.channel();
					String str = "qiwoo mobile";
					ByteBuffer buffer = ByteBuffer.wrap(str.getBytes());
					clientChannel.write(buffer);
					selectionKey.interestOps(SelectionKey.OP_READ);
					System.out.println("向服务端发送数据" + new String(buffer.array()));
				}

				iterator.remove();
			}
		}
	}
}

再来看一下服务端的逻辑

1、向服务端发起连接请求。

2、selectionKey.isConnectable()被触发,连接成功之后,selectionKey.interestOps设置为SelectionKey.OP_WRITE,准备向服务端发送数据。

3、channel可写之后selectionKey.isWritable()被触发,向服务端发送数据,之后selectionKey.interestOps设置为SelectionKey.OP_READ,等待服务端过来的数据。

4、服务端数据发过来之后,selectionKey.isReadable()被触发,读取服务端数据之后selectionKey.interestOps设置为SelectionKey.OP_WRITE向服务端写数据。

关注微信公众号,最新技术干货实时推送

image

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/13317.html

(0)

相关推荐

  • 英雄联盟3d视角(英雄联盟3D视角怎么没了)

    英雄联盟3d视角(英雄联盟3D视角怎么没了)

    2023-08-28
    169
  • Python Tkinter布局管理器 – Place 控制组件的位置和大小

    Python Tkinter布局管理器 – Place 控制组件的位置和大小Python Tkinter是一个用于构建图形用户界面的标准Tk GUI工具包,它提供了多种布局管理器来控制组件的布局。其中,Place布局管理器是一种非常灵活的布局管理器,它允许你直接指定组件的坐标和大小。

    2024-02-08
    95
  • 如何在Python中安装tkinter模块

    如何在Python中安装tkinter模块在Python中,Tkinter是最常用的用户图形界面(GUI)编程模块之一。Tkinter是Python自带的一个GUI模块,它提供了用户界面上常用的组件。Tkinter是一个跨平台的图形用户界面(GUI)模块。如果您正在学习Python的GUI编程,那么Tkinter将是您的良好选择。在本文中,我们将学习关于如何在Python中安装Tkinter模块的方法。

    2024-08-19
    27
  • Nature:神经网络“举一反三”能力甚至超人类

    Nature:神经网络“举一反三”能力甚至超人类神经网络具有类似人的“举一反三”能力,甚至超过人类水平??? 最近刊于Nature的一篇论文表示找到了证据。 “举一反三”、系统概括的能力更专业点叫做系统性泛化能力。像小孩子一样,一旦学会了如何“跳”

    2023-11-18
    197
  • mysql – 查看表结构命令

    mysql – 查看表结构命令
    — 查看表结构desc 表名; — 查看表中字段的结构信息select table_name,column_name,column_comment fr…

    2023-04-09
    149
  • MySQL同步机制

    MySQL同步机制复制步骤: Slave 上面的IO线程连接上 Master,并请求从指定日志文件的指定位置(或者从最开始的日志)之后的日志内容 Master 接收到来自 Slave 的 IO 线程的请求后,通过负责…

    2023-03-30
    132
  • 部署python应用(python 应用)

    部署python应用(python 应用)所需工具:

    2023-11-27
    142
  • 魔法讲堂_js instanceof

    魔法讲堂_js instanceof大家都知道 instanceof 一般就是用来检查 A 对象是否为 B 类或子类的实例。那问题是 JS 中没有类的概念更没有类继承的概念(虽然有构造函数),那么 instanceof 到底是怎样判断 A 对象是 B 构造函数的实例呢?本文将对此作分析记录,以便日后查阅。

    2023-07-30
    149

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注