学习完nio的一个小笔记吧
2024-09-08 17:22:18
- 这是一个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");
}
}
最新文章
- Android开发之基本控件和详解四种布局方式
- Scrum项目1.0
- [转]World Wind学习总结一
- C#扩展方法入门
- JAVA中获取工程路径的方法
- php foreach 使用&;(与运算符)引用赋值要注意的问题
- Mediator 中介者 协调者模式
- 【转载】laravel的MVC
- linux下文件和目录
- ajax成功返回结果字符串,对比不成功的解决办法
- 人人中的 shiro权限管理 简单说明
- Django 的 orm 查询
- 阐述:SIP协议是什么
- JS算法之A*(A星)寻路算法
- P4551 最长异或路径
- Codeforces Beta Round #10 B. Cinema Cashier 暴力
- CSRF攻击演示
- Spring的常用下载地址
- shader cycles静态分析
- Vue.js:组件
热门文章
- 使用Tapdata一步搞定关系型数据库到MongoDB的战略迁移
- MySQL查询为什么没走索引?这篇文章带你全面解析
- java -jar -Xbootclasspath/a:/xxx/config xxx .jar 和 java -jar xxx .jar 的区别
- 5-21 拦截器 Interceptor
- Educational Codeforces Round 132 (C,D) 题解 cf#1709
- 队列Q_via牛客网
- csdn 不可复制代码的解决方法
- 一文搞定Vue2组件通信
- 题解【AtCoder - CODE FESTIVAL 2017 qual B - D - 101 to 010】
- qbxt数学五一Day4