NIO学习之Selector,SelectionKey与客户端与服务端通信简单实现(1)
来源:互联网 发布:怎么通过网络挣钱 编辑:程序博客网 时间:2024/05/17 19:58
选择器(Selector)的 作用:将通道感兴趣的事件放入队列中,而不是马上提交给应用程序,等已注册的通道自己来请求处理这些事件。换句话说,就是选择器将会随时报告已经准备好了的通道,而且是按照先进先出的顺序。
open()
方法,静态方法,用于获取1个Selector对象keys()
方法,用于获取所有注册到Selector对象上的SelectionKey
selectedKeys()
方法,用于获取已注册通道发出的准备好了的SelectionKey
select()
方法,调用此方法的线程会一直阻塞,直到注册的通道中关注的事件发生了才会返回。 例:服务端的ServerSocketChannel
注册了OP_ACCEPT
事件,如果没有客户端连接服务端,那么服务端将一直阻塞在调用select()
方法的地方
select(long)
方法,此方法与不带参数的差不多,参数的含义是当阻塞的时间超过给定的时间参数,该方法将返回继续执行
wakeup()
方法,会唤醒当前被阻塞的线程,使其 select() 立即返回;如果当前线程没有阻塞,那么执行了wakeUp() 方法之后,下一个线程的 select() 方法会被立即返回,不再被阻塞下去。
close()
方法,能够关闭当前的选择器。当一个线程当前呈阻塞状态,那么中止这种状态需要先调用Selector的 wakeUp()
方法。close()方法在实现类中先调用wakeUp()
方法唤醒被阻塞的线程,然后置空所有的通道、所有就绪的SelectionKey,让这个选择器上的轮询组件也闲置下来。
选择键(SelectionKey)的作用是表明哪个通道已经做好了相应的准备,相应的准备指SelectionKey
类里定义的OP_READ
,OP_WRITE
,OP_CONNECT
,OP_ACCEPT
中某个某个事件发生了。Selector不断轮询是否有事件准备好了,如果有事件准备好了则获取事件相应的SelectionKey,进入事件处理
SelectionKey主要说两点:
attach(Object)
主要用于绑定一个对象(对象可以任意类型),绑定后可以在不同线程间共享,对应的attachment()
方法用于获取绑定的对象interestOps()
方法用于获取通道“感兴趣”的操作或事件
说了Selector 与 SelectionKey 的基础知识及主要方法的使用,现在就来实践一下。
服务端
伪代码
1.打开1个ServerSocketChannel通道2.把ServerSocketChannel设置为非阻塞的3.为ServerSocketChannel通道绑定ip地址与端口4.获取Selector对象5.把ServerSocketChannel通道感兴趣的事件注册到Selector对象里6.监听端口,等待已注册的通道关注的事件触发7.获取事件触发的通道进行相应的处理
public class NIOServer { private Selector selector; public void initServer(int port) throws IOException{ // 打开ServerSocket通道 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.configureBlocking(false); serverSocketChannel.socket().bind(new InetSocketAddress(port)); // 获取一个选择器 this.selector = Selector.open(); // 将通道管理器与该通道进行绑定,并为该通道注册SelectionKey.OP_ACCEPT事件 // 注册事件后,当该事件触发时会使selector.select()返回, // 否则selector.select()一直阻塞 serverSocketChannel.register(this.selector, SelectionKey.OP_ACCEPT); } public void listen() throws IOException{ System.out.println("启动服务器!"); while (true) { // select()方法一直阻塞直到有注册的通道准备好了才会返回 selector.select(); Iterator<?> iterator = selector.selectedKeys().iterator(); while (iterator.hasNext()) { SelectionKey key = (SelectionKey) iterator.next(); // 删除已选的key,防止重复处理 iterator.remove(); handler(key); } } } public void handler(SelectionKey key)throws IOException{ if (key.isAcceptable()) { handlerAccept(key); }else if (key.isReadable()){ handlerRead(key); }else if (key.isWritable()){ System.out.println("can write!"); }else if (key.isConnectable()){ System.out.println("is connectable"); } } public void handlerAccept(SelectionKey key) throws IOException{ // 从SelectionKey中获取ServerSocketChannel ServerSocketChannel server = (ServerSocketChannel) key.channel(); // 获取SocketChannel SocketChannel socketChannel = server.accept(); // 设置成非阻塞 socketChannel.configureBlocking(false); System.out.println("与客户端建立连接"); // 为socketChannel通道建立 OP_READ 读操作,使客户端发送的内容可以被读到 socketChannel.register(selector, SelectionKey.OP_READ); // 往客户端发送发送信息 socketChannel.write(ByteBuffer.wrap("connected\n".getBytes())); } public void handlerRead(SelectionKey key)throws IOException{ SocketChannel socketChannel = (SocketChannel) key.channel(); // 创建读取缓冲区 ByteBuffer byteBuffer = ByteBuffer.allocate(512); // 从通道读取可读取的字节数 try { int readcount = socketChannel.read(byteBuffer); if (readcount > 0) { byte[] data = byteBuffer.array(); String msg = new String(data); System.out.println("服务端收到的信息为:\n" + msg); ByteBuffer outBuffer = ByteBuffer.wrap("收到\n".getBytes()); socketChannel.write(outBuffer); } else { System.out.println("客户端异常退出"); } } catch (IOException e) { System.out.println("异常信息:\n" + e.getMessage()); key.cancel(); } } public static void main(String[] args) throws IOException { NIOServer server = new NIOServer(); server.initServer(8888); server.listen(); }}
客户端
public class NIOClient1 { private Selector selector; private SocketChannel socketChannel; private SocketAddress address; public NIOClient1(String host, int port) throws IOException { address = new InetSocketAddress(host, port); selector = Selector.open(); socketChannel = SocketChannel.open(address); socketChannel.configureBlocking(false); socketChannel.register(selector, SelectionKey.OP_READ); } public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("输入服务器ip地址:\t"); String host = scanner.next(); System.out.print("输入端口号:\t"); int port = scanner.nextInt(); try { NIOClient client = new NIOClient(host, port); Selector selector = client.getSelector(); String msg = scanner.next(); while (true) { if (msg.equals("q")) { break; } client.getSocketChannel().write(ByteBuffer.wrap(msg.getBytes())); if (selector.select() > 0) { Set<SelectionKey> selectionKeys = selector.selectedKeys(); Iterator<SelectionKey> iterator = selectionKeys.iterator(); while (iterator.hasNext()) { SelectionKey key = iterator.next(); if (key.isReadable()) { SocketChannel socketChannel = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); int size = socketChannel.read(buffer); if (size > 0) { buffer.flip(); byte[] data = new byte[size]; buffer.get(data); System.out.println("来自服务端的消息:\t" + new String(data)); } } iterator.remove(); } } msg = scanner.next(); } } catch (IOException e) { e.printStackTrace(); } } public Selector getSelector() { return selector; } public SocketChannel getSocketChannel() { return socketChannel; } public SocketAddress getAddress() { return address; } public void setSelector(Selector selector) { this.selector = selector; } public void setSocketChannel(SocketChannel socketChannel) { this.socketChannel = socketChannel; } public void setAddress(SocketAddress address) { this.address = address; }}
运行效果
1. 先启动服务端
2. 启动客户端
参考博客
- NIO学习之Selector,SelectionKey与客户端与服务端通信简单实现(1)
- NIO实现的简单的客户端与服务端通信(非阻塞)
- NIO应用实现多客户端与服务端通信
- NIO 客户端与服务端通信demo
- java nio 实现客户端与服务端读写
- Mina学习(一):mina实现简单服务端与客户端
- socket实现客户端与服务端通信(一)服务端
- socket实现客户端与服务端通信(三)服务端升级
- Java Socket简单实现客户端与服务端通信
- 使用简单的ServiceSockt实现服务端与客户端的通信
- Socket编程基础之服务端与客户端简单通信
- JAVA NIO实现服务端与客户端简单数据传输 JAVA NIO 之一
- socket实现客户端与服务端通信(二)客户端
- 简单的服务端与客户端通信代码
- nio/mina(四)客户端socket与mina服务端通信
- [疯狂Java]NIO多路复用网络通信:Selector、SelectionKey、SelectableChannel(SocketChannel、ServerSocketChannel)
- Java中利用socket实现简单的服务端与客户端的通信(入门级)
- Java中利用socket实现简单的服务端与客户端的通信(基础级)
- 解决Error:Bootstrap dropdown require Popper.js (https://popper.js.org)
- Flask零基础到项目实战(七)请求方法、g对象和钩子函数
- Hadoop学习笔记 1
- 不同领域的人们是怎样使用思维导图的(下)
- HDU
- NIO学习之Selector,SelectionKey与客户端与服务端通信简单实现(1)
- mongoose
- Hdu 1231 最大连续子序列
- Android 根据Uri删除文件
- 使用ViewPager实现顶部tabbar切换界面
- 中电投-镇宁3号集合资产管理计划
- Android之bitmap的使用
- 一、storm基础概念
- 节点操作