首先声明,这个任务并没有实现,我暂时没找到可以用来解决kotlin中实时监听键盘动作,并且不会阻塞的回调方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import kotlinx.coroutines.*
import java.io.File
import java.io.FileReader

fun main()= runBlocking{
val file = File("src/main/resources/a.txt")
var i = 0
while (true){
println("第${i++}次访问文件")
delay(2000)
println(file.readText())
println("读取完成")
// if(readln()=="chat"){
// var message = readln()
// file.writeText(message)
// }
}
}

以上代码可以实现每隔两秒钟对文件进行一次读取操作,期间我可以通过文本编辑器对文件进行写操作,然后他能够实时更新到我写的内容,于是,考虑能否通过文件读写进行聊天对话?
** 因为,在不同的操作系统中,换行符的表示方式是不一样的。在UNIX系统中,换行符使用”\n” , 在 windows 系统中换行使用 “\r\n”; 在旧版macOS中换行使用回车符”/r”,在新版macOS中使用与unix系统相同的换行方式。**
然后\r:13;\n:10,这就是为什么会在文件读取中会读取到则会两个ascii码的原因

javaIO读写原理

接下来这部分内容引用自某位知乎大佬,
无论是Socket的读写还是文件的读写,在Java层面的应用开发或者是linux系统底层开发,都属于输入input和输出output的处理,简称为IO读写。在原理上和处理流程上,都是一致的。区别在于参数的不同。

先强调一个基础知识:read系统调用,并不是把数据直接从物理设备,读数据到内存。write系统调用,也不是直接把数据,写入到物理设备。

read系统调用,是把数据从内核缓冲区复制到进程缓冲区;而write系统调用,是把数据从进程缓冲区复制到内核缓冲区。这个两个系统调用,都不负责数据在内核缓冲区和磁盘之间的交换。底层的读写交换,是由操作系统kernel内核完成的。

内核缓冲与进程缓冲区

缓冲区的目的,是为了减少频繁的系统IO调用。大家都知道,系统调用需要保存之前的进程数据和状态等信息,而结束调用之后回来还需要恢复之前的信息,为了减少这种损耗时间、也损耗性能的系统调用,于是出现了缓冲区。

有了缓冲区,操作系统使用read函数把数据从内核缓冲区复制到进程缓冲区,write把数据从进程缓冲区复制到内核缓冲区中。等待缓冲区达到一定数量的时候,再进行IO的调用,提升性能。至于什么时候读取和存储则由内核来决定,用户程序不需要关心。

在linux系统中,系统内核也有个缓冲区叫做内核缓冲区。每个进程有自己独立的缓冲区,叫做进程缓冲区。

所以,用户程序的IO读写程序,大多数情况下,并没有进行实际的IO操作,而是在读写自己的进程缓冲区。

四种主要的IO模型

(1)同步阻塞IO(Blocking IO)

首先,解释一下这里的阻塞与非阻塞:

阻塞IO,指的是需要内核IO操作彻底完成后,才返回到用户空间,执行用户的操作。阻塞指的是用户空间程序的执行状态,用户空间程序需等到IO操作彻底完成。传统的IO模型都是同步阻塞IO。在java中,默认创建的socket都是阻塞的。

其次,解释一下同步与异步:

同步IO,是一种用户空间与内核空间的调用发起方式。同步IO是指用户空间线程是主动发起IO请求的一方,内核空间是被动接受方。异步IO则反过来,是指内核kernel是主动发起IO请求的一方,用户线程是被动接受方。

(4)同步非阻塞IO(Non-blocking IO)

非阻塞IO,指的是用户程序不需要等待内核IO操作完成后,内核立即返回给用户一个状态值,用户空间无需等到内核的IO操作彻底完成,可以立即返回用户空间,执行用户的操作,处于非阻塞的状态。

简单的说:阻塞是指用户空间(调用线程)一直在等待,而且别的事情什么都不做;非阻塞是指用户空间(调用线程)拿到状态就返回,IO操作可以干就干,不可以干,就去干的事情。

非阻塞IO要求socket被设置为NONBLOCK。

强调一下,这里所说的NIO(同步非阻塞IO)模型,并非Java的NIO(New IO)库。

(3)IO多路复用(IO Multiplexing)

即经典的Reactor设计模式,有时也称为异步阻塞IO,Java中的Selector和Linux中的epoll都是这种模型。

(5)异步IO(Asynchronous IO)

异步IO,指的是用户空间与内核空间的调用方式反过来。用户空间线程是变成被动接受的,内核空间是主动调用者。

这一点,有点类似于Java中比较典型的模式是回调模式,用户空间线程向内核空间注册各种IO事件的回调函数,由内核去主动调用。