一、文件系统IO
举个例子
在Linux系统中运行java代码
package com.softeem.wolf.IO;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* Created by 苍狼
* Time on 2022-10-31
*/
public class test {
public static void main(String[] args) throws IOException {
File file = new File("test.txt");
FileOutputStream fos = new FileOutputStream(file);
String str = "123456789\n";
byte[] bytes = str.getBytes();
for (int i = 0; i < 100000; i++) {
fos.write(bytes);
fos.flush();
}
fos.close();
}
}
javac编译一下test.java文件, 生成test.class文件
[root@CentOS-7-64 ~]# ll
total 4
-rw-r--r-- 1 root root 498 Nov 1 20:41 test.java
[root@CentOS-7-64 ~]# javac test.java
[root@CentOS-7-64 ~]# ll
total 8
-rw-r--r-- 1 root root 694 Nov 1 20:41 test.class
-rw-r--r-- 1 root root 498 Nov 1 20:41 test.java
然后运行java test, 会生成一个test.txt文件, 这个时候用ll命令可以看到三个文件, test.java, test.class, test.txt, 且这三个文件都有内容.
[root@CentOS-7-64 ~]# java test
[root@CentOS-7-64 ~]# ll
total 988
-rw-r--r-- 1 root root 694 Nov 1 20:41 test.class
-rw-r--r-- 1 root root 498 Nov 1 20:41 test.java
-rw-r--r-- 1 root root 1000000 Nov 1 20:43 test.txt
这个时候立即关掉服务器(记住是关掉服务器不是关掉远程客户端连接, 是立即关闭服务器), 然后再重新启动服务器, 查看这三个文件, 你会发现这个时候test.txt文件内容为空.这是为什么呢?
这里说一下FileOutputStream这个类, 其中flush()并没有实现, 写不写都无所谓. 如果把它换成BufferedOutputStream, (虽然这个类对flush()进行了实现)结果也是一样的.
[root@CentOS-7-64 ~]# ll
total 4
-rw-r--r-- 1 root root 0 Nov 1 20:43 test.class
-rw-r--r-- 1 root root 498 Nov 1 20:43 test.java
-rw-r--r-- 1 root root 0 Nov 1 20:43 test.txt
明明在关掉服务器之前test.txt有数据的啊, 为什么重启一下服务器数据反而为空?
这里再看一个例子
package com.softeem.wolf.IO;
import java.io.*;
/**
* Created by 苍狼
* Time on 2022-11-01
*/
public class Test15 {
public static String fileUrl = "C:\\Users\\Lenovo\\Desktop\\test.txt";
public static byte[] bytes = "123456789\n".getBytes();
public static void testRandomAccessFilewrite01() throws Exception {
RandomAccessFile raf = new RandomAccessFile(fileUrl, "rw");
for (int i = 0; i<100000; i++){
raf.write(bytes);
raf.getChannel().force(true);
}
raf.close();
}
public static void main(String[] args) throws Exception {
testRandomAccessFilewrite01();
}
}
这个时候我们重复上述操作, 发现并不会出现这种问题, 这又是为什么呢?
大家看看这咋图
我们所有对于Linux的操作都是在内核中完成的, 包括Linux命令ll, vim等, 所以我们之前用ll, vim查看文件的时候其实是操作内核查看内存中的数据, 而这时内存中的内容并没有写到硬盘中, 所以才导致我们在运行完java代码之后立即关掉服务器会导致数据丢失. 而第二个例子的代码因为一个方法raf.getChannel().force(true);这个方法表示内核会让每次写到内存中的数据都强制写入硬盘当中, 所以我们运行完了之后立即关掉服务器并不会导致数据丢失, 因为此时数据已经写入了硬盘当中了. 但是因为每一次内存写操作都需要写入硬盘所以运行速度相当之慢, 这就跟操作mysql数据库和redis数据库一样, 内存操作永远比硬盘操作快得多. 所以不同的项目技术选型就尤为重要了.
二、网络IO
2.1、计算机网络模型
TCP/IP 与 OSI 都是为了使网络中的两台计算机能够互相连接并实现通信与回应,但他们最大的不同在于,OSI 是一个理论上的网络通信模型,而 TCP/IP 则是实际上的网络通信标准。
2.1.1、OSI七层模型
1、物理层:实现计算机节点之间比特流的透明传输,规定传输媒体接口的标准,屏蔽掉具体传输介质和物理设备的差异,使数据链路层不必关心网络的具体传输介质,按照物理层规定的标准传输数据就行
2、数据链路层:通过差错控制、流量控制等方法,使有差错的物理线路变为无差错的数据链路。
数据链路层的几个基本方法:数据封装成桢、透明传输、差错控制、流量控制。
封装成桢:把网络层数据报加头和尾,封装成帧,帧头中包括源MAC地址和目的MAC地址。
透明传输:零比特填充、转义字符。
差错控制:接收者检测错误,如果发现差错,丢弃该帧,差错控制方法有 CRC 循环冗余码
流量控制:控制发送的传输速度,使得接收方来得及接收。传输层TCP也有流量控制功能,但TCP是端到端的流量控制,链路层是点到点(比如一个路由器到下一个路由器)
3、网络层:实现网络地址与物理地址的转换,并通过路由选择算法为分组通过通信子网选择最适当的路径
网络层最重要的一个功能就是:路由选择。路由一般包括路由表和路由算法两个方面。每个路由器都必须建立和维护自身的路由表,一种是静态维护,也就是人工设置,适用于小型网络;另一种就是动态维护,是在运行过程中根据网络情况自动地动态维护路由表。
4、传输层:提供源端与目的端之间提供可靠的透明数据传输,传输层协议为不同主机上运行的进程提供逻辑通信。
- 网络层协议负责的是提供主机间的逻辑通信;
- 传输层协议负责的是提供进程间的逻辑通信。
5、会话层:是用户应用程序和网络之间的接口,负责在网络中的两节点之间建立、维持、终止通信。
6、表示层:处理用户数据的表示问题,如数据的编码、格式转换、加密和解密、压缩和解压缩。
7、应用层:为用户的应用进程提供网络通信服务,完成和实现用户请求的各种服务。
2.1.2、TCP/IP模型
TCP/IP协议模型(Transmission Control Protocol/Internet Protocol),包含了一系列构成互联网基础的网络协议,是Internet的核心协议。TCP/IP协议族按照层次由上到下,层层包装。
上图表示了TCP/IP协议中每个层的作用,而TCP/IP协议通信的过程其实就对应着数据入栈与出栈的过程。入栈的过程,数据发送方每层不断地封装首部与尾部,添加一些传输的信息,确保能传输到目的地。出栈的过程,数据接收方每层不断地拆除首部与尾部,得到最终传输的数据。
2.2、应用程序通信的大致过程
正常情况下两个应用之间的通信直观的感受是这样的
真实的详细的通信过程
APP(应用程序) 实现的协议主要是应用层, 表示层, 会话层, 也就是开发者可以自己自定义的内容, 而传输层, 网络层中的协议是通过操作系统内核实现的, 不同的操作系统是有区别的. 这个一般是改不了的, 而数据链路层, 物理层协议是通过网卡等硬件设备来实现的.
这里APP1(应用程序1)向APP2(应用程序2)传输数据, 先通过APP1将数据传给操作系统内核, 然后操作系统再将数据传给网卡等硬件设备, 通过无线网络(基站)传输(通过ip地址)给APP2中的主机B的网卡等硬件设备. 然后传给操作系统内核, 操作系统内核最后给到APP2, 这个时候用户就可以在APP2看到APP1传来的数据信息了.这是APP1传输给APP2的数据, 同样APP2传输给APP1的数据也是同样的道理.
2.3、传输控制层(TCP/UDP协议)
这里主要分析一下TCP协议, TCP协议是面向连接的可靠的传输控制层协议, 它是以三次握手来实现的(这里的连接可以理解为资源的创建)
三次握手的大致过程
分析
当c(客户端)开始向s(服务端)发送数据包时(第一次握手), 如果s收到了的话, 两端就开辟了资源, 只不过这时的资源状态是不通的. 只要三次握手都能正常执行, 则资源状态就是可以流通的. 当资源正常开辟使用时, APP就可以读取资源中的数据了.
如果最后一次握手失败了, 则资源的状态仍然是不可用的(这个时候资源已经开辟了), 那么如果这个时候c向s发送数据, 则会导致数据包的丢失, 不能发送到s的开辟的资源中, 那么这个时候怎么解决呢?
重复第二次握手, 就是s再向c发送syn+ack, 如果c收到了, 则向s发送ack, 如果第三次握手成功, 则开辟资源的状态就是可以正常操作的. 就可以实现正常的通信了.
这里有人会问, 这个开辟的资源是什么?
其实就是socket.
[root@CentOS-7-64 ~]# netstat -natp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 906/sshd
tcp 0 0 192.168.101.102:22 192.168.101.1:62234 ESTABLISHED 1265/sshd: root@pts
tcp6 0 0 :::33060 :::* LISTEN 944/mysqld
tcp6 0 0 :::3306 :::* LISTEN 944/mysqld
tcp6 0 0 :::22 :::* LISTEN 906/sshd
看这个State为ESTABLISHED就代表的是资源可以正常执行的状态.而状态为LISTEN则代表目前资源处于监听状态, 不可以正常数据操作的.
2.4、socket通信原理
大家看这张图
在Linux中演示三次握手
命令: tcpdump -nn -i any port 80(抓取端口号为80端口的数据包)
命令: curl www.baidu.com(抓取百度主页的内容)
当监听到了80端口有数据包传输, 则可以看到数据包中的详细内容.
分析一下抓取的包的内容. 首先前面三行, 代表三次握手的控制包.
注: 凡是有.代表确认上一个信息.
[S]代表syn, 且length为0. 主机(192.168.101.102.39430)向百度(14.215.177.38.80)发送控制包(第一次握手).
[S.]中的S代表syn, .代表ack, length为0. 百度(14.215.177.38.80)向主机(192.168.101.102.39430)发送控制包(第二次握手).
[.]带表ack, 且length也为0. 主机(192.168.101.102.39430)向百度(14.215.177.38.80)发送控制包(第三次握手).确认收到.
在完成三次握手时, 后面就是进行进行数据传输.
- 首先客户端向百度服务器的内核发送length为77的请求头, 发送[p.], 这个p代表不要缓冲, 让百度服务器内核立即知道.
- 然后百度服务器向客户端发送确认信息[.]
- 接着百度服务器向客户端发送length为1460大小的数据包,
- 客户端回复收到[.]
- 接着百度服务器又向客户端发送length为1321大小的数据包
- 客户端回复收到[.]
这里为什么百度服务器向客户端发送数据时第一次为什么大小是1460? 而第二次为1321?
这里解释一下1460怎么来的, 首先在三次握手中规定了数据包的最大传输大小为1460(mss 1460)那第二次1321是因为你用的这个命令curl www.baidu.com 是为了获取百度的主页信息, 而百度的主页信息数据大小就是2781, 第一次百度最大只能传输的数据包大小是1460, 所以一个数据包是传输不完的, 那么第二次还剩下1321, 这也没有超过数据包的最大量, 所以就将剩余的数据传输给客户端.
如果要深究的的话
输入ifconfig可以查看对应的网卡信息, 可以看到这里有一个mtu 1500, 这个代表最大传输单元, 其中其实是包括TCP首部的20和IP首部的20, 减掉40就得到了1460, 代表HTTP数据可以最大传输的大小为1460. 下面的这个图已经有展示了.
最后四个数据包的信息就是四次挥手的过程了, 我就不详细展开说了, 大家应该都可以看得懂.
[F.]: 客户端开始发送信息给百度服务端, 表示开始断开连接了.
[.]: 表示百度服务端向客户端发起收到数据包.
[FP.]: 百度服务端也开始向客户端发送断开数据包.
[.]: 客户端向百度服务端发起收到数据包.