• 这是一个nio网络通信服务端的demo,主要就学习了selector的一些用法,以及它里面的事件类型
  • selector是对nio的一个优化,它能保证既能高效处理线程中的事件,又能保证线程不会一直占用cpu
其中我认为最重要的是selector的执行流程,机制
  • 将 channel和 selector建立联系。(联系就是指客户端向服务端发送的某个事件会被selector给监听到)。

  • 当客户端给服务器发送了相应事件后,selector就会将这个事件的 selectionKey加入到集合 selectionKeys中,并且处理该事件。

  • 在处理事件的时候,可以根据事件类型来进行处理。比如说使用 channel.accept()、channel.read(xx)来处理。

  • 对一些客户端异常关闭,或者说正常关闭要特殊处理。客户端关闭,可以直接调 key.cancel()将该key从 selector中删除。

服务端
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set; import static com.pzistart.utils.ByteBufferUtil.debugRead; /**
* @author Pzi
* @create 2022-09-12 17:47
*/
@Slf4j
public class ServerUp { public static void main(String[] args) throws IOException {
ServerSocketChannel ssc = ServerSocketChannel
.open()
.bind(new InetSocketAddress(8080)); // 1.创建 selector,管理多个 channel
Selector selector = Selector.open(); // 2.开启非阻塞模式,并且将ssc注册到selector中
ssc.configureBlocking(false); // 3.建立 ssc(channel) 和 selector 的联系
SelectionKey sscKey = ssc.register(selector, SelectionKey.OP_ACCEPT);
log.debug("regist key:{}", sscKey); while (true) {
// 如果selector监听到有事件发生,那么就向 selectionKeys 这个集合中加入key。
// 并且执行下面的操作,否则就阻塞线程
int count = selector.select(); // selectionKeys中包含所有的事件
Set<SelectionKey> selectionKeys = selector.selectedKeys(); // 4.处理事件
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
// 在selectionKeys中即使是处理完毕事件,也不会自动移除key。
// 导致下一次处理事件的时候,还会找到事件类型为 accept 的那个 SelectionKey
// 但是由于accept事件已经被消耗,所以获取不到对应的 ServerSocketChannel。所以在用完该key,就要立马移除集合中该key
iterator.remove(); // 如果客户端发过来的是 accept相关的事件,那么就会被 selector 识别并且将该时间加入到 selectionKeys集合中。从而在下面的事件处理分支进行处理。
// 如果客户端发过来的是 read相关的之间,同样的事件处理方式
if (key.isAcceptable()) {
log.debug("key{}", key);
ServerSocketChannel c = (ServerSocketChannel) key.channel();
// 调用accept()方法处理事件
SocketChannel sc = c.accept();
sc.configureBlocking(false);
// 将sc注册到selector中,建立sc(channel)和selector的联系
SelectionKey scKey = sc.register(selector, SelectionKey.OP_READ);
log.debug("connected... {}", sc);
// 也可以调用cancel()方法处理事件 key.cancel();
} else if (key.isReadable()) {
try {
SocketChannel channel = (SocketChannel) key.channel();
// 将sc注册到selector中
ByteBuffer buffer = ByteBuffer.allocate(16);
int read = channel.read(buffer);
if (read == -1) {
key.cancel();
} else {
buffer.flip();
debugRead(buffer);
buffer.clear();
}
} catch (Exception e) {
e.printStackTrace();
// 在客户端异常断开连接时,客户端会向服务器发送一个 read()事件
// read()事件没有得到处理,那么就会多次被 selector监听到,从而循环抛异常。
// 解决方法就是将该 key删除,就是直接从 selector中反注册,那么 selector自然不会监听到该 channel的相应事件
// key.cancel();
}
}
}
}
}
}
客户端
package com.pzistart.netcoding.bio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel; /**
* @author Pzi
* @create 2022-09-12 15:04
*/
public class Client { public static void main(String[] args) throws IOException {
SocketChannel sc = SocketChannel.open();
sc.connect(new InetSocketAddress("localhost", 8080));
System.out.println("ccc");
} }

最新文章

  1. Android开发之基本控件和详解四种布局方式
  2. Scrum项目1.0
  3. [转]World Wind学习总结一
  4. C#扩展方法入门
  5. JAVA中获取工程路径的方法
  6. php foreach 使用&amp;(与运算符)引用赋值要注意的问题
  7. Mediator 中介者 协调者模式
  8. 【转载】laravel的MVC
  9. linux下文件和目录
  10. ajax成功返回结果字符串,对比不成功的解决办法
  11. 人人中的 shiro权限管理 简单说明
  12. Django 的 orm 查询
  13. 阐述:SIP协议是什么
  14. JS算法之A*(A星)寻路算法
  15. P4551 最长异或路径
  16. Codeforces Beta Round #10 B. Cinema Cashier 暴力
  17. CSRF攻击演示
  18. Spring的常用下载地址
  19. shader cycles静态分析
  20. Vue.js:组件

热门文章

  1. 使用Tapdata一步搞定关系型数据库到MongoDB的战略迁移
  2. MySQL查询为什么没走索引?这篇文章带你全面解析
  3. java -jar -Xbootclasspath/a:/xxx/config xxx .jar 和 java -jar xxx .jar 的区别
  4. 5-21 拦截器 Interceptor
  5. Educational Codeforces Round 132 (C,D) 题解 cf#1709
  6. 队列Q_via牛客网
  7. csdn 不可复制代码的解决方法
  8. 一文搞定Vue2组件通信
  9. 题解【AtCoder - CODE FESTIVAL 2017 qual B - D - 101 to 010】
  10. qbxt数学五一Day4