操作系统
- 线程和进程的区别和相同之处。
相同之处:
- 都是操作系统来进行管理的。
- 都需要陷入到操作系统的内核态进行操作。
- 临时变量都会放入到操作系统的内核栈中。
不同之处:
- 进程是资源分配(文件,内存等)的基本单位,线程是程序执行(占用CPU)的基本单位。
- 切换的时候,进程需要保存和切换页表,栈,寄存器,文件句柄等信息,而线程只需要保存和切换一部分寄存器和栈,页表不会切换。
- 开销:进程创建和销毁需要创建和销毁PCB(进程控制块)而线程创建销毁只用处理PC,状态码,部分寄存器值和栈即可。
- 进程之间通信需要操作系统协同,而线程之间通信可以访问进程数据段(如全局变量)来进行通信。
- 进程的并发性是通过不同进程间切换实现的,而线程的并发是一个进程多个内部线程并发执行。(意思是同一时刻只有一个进程占用CPU资源,但是一个进程附属的若干个线程会占用CPU的每个核)
两者之间的关系:
- 一个程序的运行会创建一个或若干个进程。进程是运行的程序。
- 线程是轻量级的进程,一个进程会创建若干个线程(其中包括一个主线程)。可以认为线程是进程的从属。
- 进程结束也就是主线程结束,子线程也会结束。(pthread_exit()和_exit()的区别)
- 一个进程可以创建多少线程?
如果是32位系统,那么虚拟空间只有3G,如果创建线程时分配空间为aMB,那么可以创建$\frac{3000}{a}$个线程
如果是64为系统,那么虚拟空间有128T,线程的创建不受虚拟空间的限制,而会受系统的参数和性能限制。
- 外中断、异常和自陷之区别。
外中断是由CPU执行以外的时间引起,例如IO中断,表示设备的输入或者输出完成;时钟中断等。
异常是由于CPU执行导致的错误,比如说除以0等。
自陷是自己引发中断,比如说syscall指令,早期汇编的inv指令等。
- 进程调度算法?
- 先来先服务(FCFS)顾名思义,不利于短作业。
- 短作业优先(SJF)顾名思义,但是长作业可能会饿死。
- 最短剩余时间优先(SRTN)看谁剩的短谁先做,会抢占。
- 时间片轮转,构建一个就绪队列,每次调度时,从就绪队列中取一个开始,过一个固定的时间后换一个。
- 优先级调度,可以认为是把上面的就绪队列改成优先队列,优先级高的先调度。
- 多级反馈队列,有n个就绪队列,从i号就绪队列取出来之后执行$S_i$时间,然后进入i+1号就绪队列。(如果i = n,就进入i号就绪队列)
- Linux进程通信方式?
- 管道:分成无名和有名两种,管道的数据是单向流动的,无名管道通常只能在父子进程间使用而有名管道就没有这种限制。
- 共享内存:几个进程都可以同时访问一段内存。
- 消息队列:有消息的链表,存放在内核中并由消息队列标识符标记。
- 套接字:Socket
- 信号
- 信号量(PV操作)
- 进程同步机制?
临界资源就是多个进程都会用到的资源,使用临界资源的代码块是临界区。
同步就是多个进程因为合作产生制约关系,使得进程有一定的先后执行关系,。与同步对应的就是互斥,就是多个进程在同一时刻只有一个进程能进入临界区。由于同步和互斥的情况存在,所以就需要同步机制来对进程进行管理。
- 信号量:P-V操作,可以用于进程同步。如果信号量只可能是0或者1,那就是互斥量mutex(可以认为是锁)
- 锁:可以有很多种锁,除了互斥锁以外还有睡眠锁,读写锁等等。只能用于线程同步。
- 管程:把做同步操作(操作)的代码独立出来.这就是管程
锁有两种,一种是自旋锁,一种是互斥锁。互斥锁就是遇到锁了,直接把进程放入等待队列。当解锁的时候,进程从等待队列进入就绪队列。还有一种是自旋锁,自旋锁不会让进程进入等待状态,而进程会持续的询问锁的状态,直到解锁。
- 快表是什么,有什么用?
快表(TLB)是页表的cache。作用是在访问页表的时候起到一次cache的作用。
如果TLB命中,那么访存只需要一次TLB + 一次读内存。如果TLB没有命中,那么访存就是一次TLB + 一次读页表 + 一次读内存,如果读页表也miss了,还要承担一次缺页异常的损耗。
- 动态分区算法
- 首次适应,从低地址查找,找到第一个能满足的空闲区。
- 最佳适应,空闲分区按照容量递增顺序连接,找到大小能满足要求的第一个空闲分区。会有太多小碎片了,算法开销大。
- 最坏适应,空闲分区按照容量递减顺序连接,找到大小能满足要求的第一个空闲分区。大分区会很快用完,算法开销大。
- 邻近适应,从上一次分配的地址开始找(递增地址),找到第一个能满足要求的空闲分区。
- 操作系统的虚拟技术。
- 时分复用:进程时间片技术,多线程和多进程。
- 空分复用:虚拟内存技术。两个进程的逻辑使用空间一致,物理使用空间不一致。
- 进程状态
分为就绪态,等待态和运行态。在一个操作系统中只能存在一个运行态的进程。
- 就绪->运行,运行->就绪:时间片到了,从就绪队列取一个进程进行运行,原本运行的进程,进入就绪队列。
- 运行->等待,就绪->运行:进程运行到某一步被PV操作、IO或者wait卡住了,进入等待状态。
- 等待->就绪:某一个被卡住的进程要等待的东西来了,就可以进入就绪状态。
只存在这四种转移模式。
- C/C++编译过程。
- 预编译:处理所有宏定义,条件预编译指令(#if等),include,注释,#pragma等(.i)
- 编译:词法分析,语法分析,语义分析,前端优化(中间代码生成),目标代码生成(.s)
- 汇编:将生成的汇编代码生成中间文件(.o)
- 链接:分为静态链接(直接把静态库的代码复制到二进制文件里,运行快)和动态链接(程序运行的时候再连接成一个完成的程序,在链接的时候只标记位置,占用空间少)
- va to pa?
将va$(V|O)$拆成两部分,页内偏移$O$和页号$V$。然后查页表,一般来说操作系统会管理一个页表基寄存器PTR,然后查$PTR + Vk$(k为一个页表项的长度)这个地址对应的页表项,找到对应的物理块号$P$,则物理地址为$(P|O)$。
目的:
- 一个程序不需要全部调入内存就可以运行,动态管理占用内存。让物理内存扩充成更大的逻辑内存,从而让程序获得更多的可用内存。
- 程序的内存占用由页表来管理,可以不需要使用连续的物理内存。
- 内存
内存是存放数据(数据和代码)的硬件,程序及其数据要先放到内存中才能被CPU处理。
内存给每个存储单元编了一个地址,每个地址对应了一个独一无二的存储单元。如果计算机按照字节编址,那么一个存储单元就是一个字节。如果计算机按照字来编址,那么存储单元就是一个字。
- 一些进程同步问题。
(1)哲学家就餐问题:
将问题分成三个原子问题,拿筷子,吃饭,放筷子。每一个哲学家都有一个信号量,姑且称为$S_i$,然后所有的原子问题都有一个互斥量mutex控制,意思是在一个时间点上,只允许一个哲学家做三个原子问题中的一个。
首先是拿筷子,首先需要检查自己左右有没有人在吃,如果没人就可以$V(S_i)$。接着做$P(S_i)$,如果上一步做了V操作,这一步P操作就可以执行。如果上一步没做V操作,就需要等待别人放筷子。
最后是放筷子,放筷子的时候需要通知左右进程检查一下自己是不是可以做$V(S_i)$操作。
为什么不能把拿筷子拆成两部分:是因为会出现死锁。
(2)读写问题:
对于读进程,需要两个互斥量来进行管理,一个是对读进程的数量进行管理的,一个是对读的内容进行管理。当第一个读进程执行的时候,要对读的内容进行封锁(防止写进程这个时候来写)。最后一个读进程结束的时候,要对读的内容进行解锁。
1 | P(count) |
对于写进程,就只用对data进行加锁,开始写的时候加锁,结束写的时候解锁。(因为可以允许多个进程读但是不能允许多个进程写)
1 | P(data) |
- 线程回收
- 等待线程结束(join)
- 结束进程
- 分离线程(detached):即说明主线程不会等待子线程运行结束才结束。
- 内存的覆盖与交换。
内存覆盖基于程序运行时并非任何时候都要访问程序和数据的所有部分。然后可以把用户空间分为固定区和覆盖区。活跃的区域放入固定区,即将要访问的在覆盖区,剩下的在外存。(基础是页表,通过修改页表来修改覆盖区)
内存交换:内存空间紧张的时候,将某些进程暂时换到外存,把外存中某些已经具备运行条件的进程换入内存。这些只会在空间紧张的时候出现。(和普通的页表替换的差距就是 不是正在运行的进程进行外内替换)。如果发生了内存交换,被交换的内存放到外存的对换区(除了文件区的部分)。一般来说内存交换优先考虑被阻塞的进程;优先级的进程。
如果刚换出的页面马上又要换入内存,那么就称这种现象叫做抖动,抖动的现象本质就是进程频繁访问的页数超过了可用的物理块数。
- Command Line环境相关
Terminal退出了之后,首先SIGHUP信号会发给bash进程,bash进程把信号发给正在Bash执行的进程。如果这个进程没有对SIGHUP信号进行处理,那么就会退出。那么怎么样让进程后台运行呢?可以做下面的操作:
- Ctrl+Z
- 命令后面加上 & (放到作业队列)
- setsid + command(忽略HUP信号),command + nohup + &和(command + &)同理。
- 守护进程
在后台运行,没有控制终端与之相连的进程。它独立于控制终端并且周期性地执行某种任务或循环等待处理某些事件的发生。
- 创建子进程:使用fork()函数创建一个子进程,父进程退出,使子进程成为孤儿进程,接下来将子进程设置为守护进程。
- 创建新会话:该子进程调用setsid()函数创建一个新的会话,使子进程成为新会话的会话领头进程。
- 更改工作目录:为了避免守护进程与其他程序在同一目录下运行造成混淆,需要将当前工作目录切换到根目录下。
- 关闭文件描述符:子进程继承了父进程打开的文件描述符,而这些文件描述符可能会影响守护进程的正常运行,因此需要关闭这些文件描述符,只保留标准输入、输出和错误输出文件描述符。
- 锁定文件:为了避免多个守护进程同时运行相同的任务,需要锁定某个文件,以保证只有一个守护进程运行某个任务。
- 僵尸进程
通常情况下,子进程退出后,父进程会使用 wait 或 waitpid 函数进行回收子进程的资源,并获得子进程的终止状态。
但是,如果父进程先于子进程结束,则子进程成为孤儿进程。孤儿进程将被 init 进程(进程号为1)领养,并由 init 进程对孤儿进程完成状态收集工作。
而如果子进程先于父进程退出,同时父进程太忙了,无瑕回收子进程的资源,子进程残留资源(PCB)存放于内核中,变成僵尸(Zombie)进程
- 让父进程调用 wait() 或 waitpid():这是防止僵尸进程出现的最直接方法。当父进程通过这些调用获取子进程的退出状态后,子进程的进程描述符会被系统释放。
- 结束父进程:当父进程结束时,其所有子进程(包括僵尸进程)都会被 init 进程(其进程 ID 通常为1)接管。init 进程会为它接管的任何子进程调用 wait(),从而清除任何僵尸进程。
- 发送 SIGCHLD 信号到父进程:在某些情况下,发送 SIGCHLD 信号到父进程可能会促使它调用 wait()。
- 利用信号处理器:父进程可以设置一个信号处理器来捕获 SIGCHLD 信号。当子进程终止时,这个信号会发送给父进程。在这个信号处理器中,父进程可以调用 wait() 或 waitpid() 来收集子进程的退出状态。
- fork
- 父进程fork(),注意fork是调用一遍返回两遍,在父进程中返回子进程pid,在子线程中返回0.
- fork之后,子进程从父进程继承堆栈、内存、进程组和一些相关的环境。
- 进程组和前后台
进程组是多个进程的组合。Bash分前后台控制的是作业(jobs)或者你可以认为是进程组。在Bash运行的时候只能有一个前台和若干个后台。
当你在Bash提交了一次作业的执行申请时,作业(进程组)进入了前台,Bash进入了后台(直到作业结束为止)
- 进程退出。
- exit() C函数库
- _exit() 系统调用(较exit()少了终止处理程序和IO缓冲清除)
- abort() 发送SIGABRT信号
- 或者发送能让进程终止的信号(^C、SIGINT等)
- 内存分布情况
从低到高分别是:
- 程序文件段(.text)
- 已初始化的数据(.data),例如静态常量,已初始化的静态变量
- 未初始化的数据(.bss),例如未初始化的静态变量
- 堆,程序员动态分配的内存
- 文件映射,动态库和共享内存
- 栈,局部变量和函数调用的栈帧等
- 磁盘调度算法
- 先来先服务
- 最短寻道时间(离得近优先)
- 电梯调度算法(按照当前运行方向离得近优先)
- 如何实现原子操作
- 总线锁:使用处理器提供的LOCK#信号,当一个处理器在总线上输出LOCK#信号时,其他处理器的请求将会阻塞。(在总线上处理)
- 缓存锁:使用cache的缓存一致性性质,缓存一致性机制会组织两个以上的处理器修改缓存的内存区域数据。当其他处理器回写数据时会使得缓存无效。
(华科高级计算机系统结构,MSI三段缓存机制,S状态下的缓存,P1写就放到M态,P2尝试写缓存就放到I态)
- 页面置换算法
- FIFO,每次淘汰最早进入内存的。
- LRU,每次淘汰最久没使用的。
- NRU,最近没使用算法,给每个页面安排一个访问位。访问到了访问位为1,发生缺页,如果访问为位1,则置0,如果访问位为0,则被淘汰。这个访问位的检查是通过循环队列实现的。
- 并行和并发
并发是宏观上有若干个任务同时执行,并行是并发的微观基础,比如说进程级并行,线程级并行,指令级并行等。
- 死锁
是什么?两个进程互相等待对方释放锁(互相等待对方的数据)。{有个笑话:校园网没钱了怎么办?充钱啊!但是但充钱要校园网。这个就是一个死锁的例子}
死锁的条件:
- 互斥条件:进程对所需求的资源有排他性,如果有其他进程请求这个资源只能等待(锁)
- 不剥夺条件:资源未释放,只能老老实实等着她释放
- 请求和保持条件:进程拥有一个资源A,请求了资源B也能保有资源A。
- 循环等待条件:存在循环等待链。(校园网等着充钱,充钱要校园网)
死锁处理办法
- 不处理。。
- 死锁检测:构建四个矩阵,EACR。E是资源总量,A是资源剩余量,C是拥有资源量,R是请求量。然后执行这样的循环。首先找一个没有被标记的进程$P_i$,它请求的资源小于等于$A_i(A_i < R_i)$,令$A_i = R_i + C_i$,如果执行完了存在有没被标记的进程,则出现了死锁。
- 死锁恢复:抢占、回滚、杀死出现死锁的进程。
- 死锁预防:破坏死锁的条件。
- 死锁避免:利用银行家算法保证安全条件。
- 碎片
内碎片:分配给进程的内存区域有部分没用上。(一般在固定分配模式【页表】中出现)内部碎片是进程分配内存给内部数据出现的。
外碎片:内存中某些空闲区因为比较小,难以分配给进程,就没用上。(一般在分段分配模式中出现【例如分区技术】)外部碎片是操作系统分配内存给进程时出现的。
对于外部碎片可以用紧凑技术,时不时移动一下进程占用的内存。
- 服务器加速
- 应用数据和静态资源分离。
- 客户端缓存
- 集群和分布式
- 反代理。
计算机网络
- OSI七层模型分别是?
- 应用层:各种应用软件
- 表示层:数据格式标识,基本压缩加密功能
- 会话层:负责复用,将不同的软件数据分发给不同软件
- 传输层:端到端传输数据的基本功能,可靠消息传递和拥塞控制。
- 网络层:IP地址,定义路由功能,找路
- 链路层:定义了MAC地址,定义了数据以什么格式进行什么传输
- 物理层
- HTTP请求需要做什么内容?
- 如果本机没有获得IP,就要先通过DHCP服务器获得一个IP。
- 使用ARP协议获得目的主机的Mac地址,使用DNS解析域名获得目的主机的IP地址。
- 建立TCP三次的握手
- 客户端向服务器发送HTTP请求。
- 服务器回应请求,客户端的浏览器将请求渲染出来。
- TCP四次挥手。
- DNS
DNS是一个域名系统,DNS本质上是一种分布式的数据库,这个数据库存储的是域名和IP地址的映射。通过域名获取IP的过程叫做域名解析。
DNS是一种应用层协议,使用的是UDP传输(因为UDP比较快,而且UDP的传输内容不超过512字节,刚好用于传输DNS信息)
DNS的过程类似于层次内存系统(找到了就ok,找不到向上一级内存系统寻找)寻找的顺序是浏览器缓存,操作系统缓存,路由器缓存,ISP(本地网络提供商)服务器缓存,根域名服务器缓存,顶级域名服务器缓存,主域名服务器缓存。
DNS负载均衡就是,使用多个服务器来进行负载均衡,然后在做DNS查询的时候,随机返回一个服务器的IP地址。
- HTTP长连接和短连接
HTTP/1.0使用短连接,就是每进行一次HTTP操作,就建立一次连接,这一次任务结束就中断连接。HTTP/1.1之后的版本都使用长连接。长连接就是对于多个连续的请求,只建立和销毁一次连接。
- TCP拆包和粘包
应用层的数据可能会被TCP拆成多个包发送,也可能会把多个包封装成一个大的包发送。因为TCP会根据MSS的大小来进行TCP分段,到了网络层,还会根据MTU(在网络上能通过的最大数据包的大小)进行IP分片
可以让消息定长,或者在包尾部加上特殊字符进行分割,加上消息尾等。
- HTTPS的请求方式有几种?
- GET:获取页面信息
- HEAD:类似于GET,但是返回的响应不拥有具体的内容,用于获取报头。
- POST:P向指定资源提交数据进行处理请求(提交表单、上传文件,数据在请求体中)
- PUT:从客户端向服务器传送的数据取代指定的文档的内容
- DELETE:请求服务器删除页面
- CONNECT
- OPTION
- TRACE
- PATCH
get和post一般用的最多,那这两个之间有什么区别呢?
- get获取数据,post修改数据
- get幂等,post不幂等(做多次相同的get服务器返回应该一样,但是做多次相同的post)
- get请求一次(头+data),post请求两次,首先请求一次头,服务器返回100;接着请求一次data
- get的请求选项在url里(?xxx=xxx),post的请求在data主体里。
get和post能传递数据的上限是多少呢?
- get通过url提交数据,因此GET可提交的数据量就和URL所能达到的最大长度有关系。HTTP协议没有对URL长度做约束。这取决于浏览器和服务器。
- post理论上也是没有大小限制的,HTTP也没对数据量进行大小限制。也是取决于服务器的设置和内存大小。
- 浏览器对同一Host建立TCP连接到的数量有没有限制?
- 如果是HTTP/1.1的话,这个看浏览器的设置。
- 如果是HTTP2以后,就可以使用Multiplexing技术进行多路传输,就可以只试使用一个TCP链接。
- HTTP和HTTPS的差距?
- HTTP非加密,HTTPS=SSL+HTTP时一种可进行加密传输,身份认证的网络协议。
- HTTPS需要CA证书
- HTTP80端口,HTTPS443端口
- SSL
SSL是安全套接字层,是一种加密和验证应用程序的协议,其加密机制是一种共享密钥加密和公开密钥加密并用的混合加密机制。
- 客户端向服务器发送SSL连接请求
- 服务器将公钥发给客户端
- 客户端用公钥先对双方通信的对称密钥进行加密,发给服务端。
- 服务端利用自己唯一的私钥对客户端发来的对称密钥进行解密。
- 服务端和客户端用公有的相同的对称密钥进行数据的加密解密。
如何保证公钥不会被篡改?只要证书是可信的,那么公钥是可信的,服务器将公钥放到证书里面。那么可以这样验证证书的可信性:将证书的内容做一次hash,生成一个“摘要”,通过比较收到的证书的hash值和摘要就可以进行可信验证。
对称加密是公钥和私钥一致的加密,这种加密方法不安全,在传递密钥的时候会有危险。不对称加密是公钥和私钥不一致的加密,这种加密方式较为安全,但是计算会比较慢。
- Cookie和Session
Cookie可以简单地认为是浏览器缓存。是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器之后向同一服务器再次发送请求的时候被携带上,用于告知两个请求来自同一服务器,之后每次请求都会携带Cookie数据。Cookie的作用就好比服务器给你贴了个标签,你每次向服务器发送请求的时候,服务器就可以通过Cookie认出你。
它可以进行
- 会话状态管理(登陆)
- 个性化设置
- 浏览器行为跟踪
除了将用户信息存储在Cookie中,当然也可以使用Session存储在服务器端。Session可以是服务器上的文件、内存、也可以是服务器上的数据库。
Session的服务逻辑:
- 用户登录,提交一个包含用户名和密码的表单,放入HTTP请求报文中。
- 服务器验证用户名和密码,如果正确,就把用户信息放入KV数据库中,K是Session ID。
- 服务器返回的响应报文的Set-Cookie字段就包括了这个Session ID。客户端收到了响应报文就把这个Cookie值存到浏览器中。
- 客户端之后对同一服务器进行请求时就会包含这个Cookie值,服务器收到了之后就提取出Session ID获取到用户信息。
Session和Cookie的区别
- Cookie只能存储ASCII码,Session可以存各种类型的数据。
- Cookie存储在浏览器中,有可能会被恶意查看。Session存储在服务器。
- Cookie存储大小受浏览器限制,而Session受服务器内存的限制
- Cookie的生命周期是累计的,从创建时就开始计时,Session的生命周期是间隔的,当使用Session了之后间隔会被重置。
- 对于大型网站,如果用户所有的信息存储在Session中,开销是很大的
- 一些网络漏洞
(1) SQL注入漏洞
攻击者在HTTP请求中注入恶意的SQL代码,服务器使用参数构造SQL命令时,恶意SQL被一起构造,并在数据库中执行
防范办法:
- 有效性检验
- 限制字符串输入的长度
- 不用拼接SQL字符串
- 使用预编译的Preparestatement
- 过滤SQL需要的参数中的特殊字符
(2) CSRF攻击
跨站点请求伪造。就是攻击者通过跨站请求,以合法的用户的身份进行非法操作。攻击者盗用你的身份,以你的名义向第三方网站发送恶意请求。
怎么防范?安全框架,token机制,验证码,referer识别。
(3) 文件上传漏洞
用户上传一个可执行的脚本文件,并用过此脚本文件获得了执行服务端命令的能力。
防范方法:
- 判断文件类型
- 文件上传的目录设置为不可行
- 上传的文件进行重复命名
- 单独设置文件服务器的域名
- RARP
给定一个Mac地址,希望能收到一个IP地址(可以认为是DHCP服务器)
- 每一个设备都有一个独一无二的Mac地址,然后在网络上发送一个RARP广播数据包,请求RARP服务器回复IP地址。
- RARP服务器收到了RARP的请求数据包,为其分配IP地址,并将RARP回应给主机。
- 协议栈分成5层有什么用?
- 各层之间是独立的
- 灵活性好
- 结构上可以分割开
- 易于实现和维护
- 能促进标准化工作
- MSS和MTU
- MSS是最大分节大小,是MTU - IP包头 - TCP包头
- MTU是最大传输单元,由硬件决定。
- HTTP缓存机制
- HTTP的Cache-Control管理资源的位置,可以是用户浏览器,也可以是代理服务器中。
- HTTPS的Cache-Control还可以告知服务器资源过期的时间和保存的时间。
- TCP
这一个东西太重要了!!!!!!!!!!!!!!!!!!!!!
TCP是一个双工协议,双方可以同时通信,发送方和接收方各自维护一个发送窗(限制发送方可以发送的数据大小)和接受窗
(1) 头部
- 序号(32位) 传输方向上字节流的字节编号。序号值 = ISN(一个随机的初始值)+ 数据在整个字节流中的偏移。
- ACK号(32位)对TCP的响应,收到的序号值 + 1
- 首部长(4位)
- 标记位(6位):URG,ACK【ACK号字段是否有效】,PSH,RST【要求对方重新建立连接】,SYN(表示请求建议一个连接),FIN(表示请求断开一个连接)
- 窗口(16位):告知对方接收窗口的大小
- 校验和(16位)
(2) TCP连接状态
- CLOSED:初始状态
- LISTEN:服务器进行监听
- SYN_SEND:客户端socket执行连接操作,给服务端发送了一个SYN包
- SYN_RECV:服务端收到了客户端发送的SYN包
- ESTABLISH:链接建立:客户端发送了最后一个ACK包,服务端接收到了ACK包
- FIN_WAIT_1:终止连接的一方(一般是客户)发送FIN报文,等待对方FIN
- CLOSE_WAIT:(假设服务器)接受客户的FIN包等待关闭的阶段,接受到对方的FIN包要发送ACK包,表示已经知道断开请求。但本方是否立即断开连接取决于本方是否还要有数据发送给对方。
- FIN_WAIT_2:客户端接收到服务器针对第一个FIN的ACK,等待服务器发送来的FIN。
- LAST_ACK:服务器发送最后的FIN包,等待客户端的ACK响应,进入状态
- TIME_WAIT:客户端收到服务端的FIN包,立即发出ACK包做最后的确认,在此之后的2MSL时间称为TIME_WAIT状态。
(3) 三次握手
- 初始状态:服务器LISTEN,客户端CLOSE。
- 第一次握手:客户端发送一个SYN报文,指明客户端的初始化序列号ISN。这个报文SYN=1,初始序号seq=x,客户端进入SYN_SEND状态。(这个SYN报文没有任何数据,但是还是会占用一个序号。)
- 第二次握手:服务器收到了客户端的SYN报文,会以自己的SYN报文作为应答(并且也是初始化了一个服务端的序列号ISN),这个报文SYN=1,ACK=1,ack=x+1,seq=y,服务器进入SYN_RECV状态。
- 第三次握手,客户端收到服务器的SYN报文之后,会发送一个ACK报文,表示收到了服务器的SYN,这个报文,ACK=1,seq=x+1(第一个SYN报文占用一个序号),ack=y+1(应答服务器的ack)。发送之后客户端进入ESTABLISHED阶段,服务器收到客户端的ack之后也进入ESTABLISHED阶段。
半连接队列:服务器端会把所有进行了一次握手的连接放入半连接队列(服务器收到客户端的第一个SYN中)。如果服务器在进行二次握手的时候丢包了就会进行重传,重传次数达到了上限就会把这个连接从半连接队列里删除掉。
在第三次握手的时候可以携带数据,就是在第三次握手的时候客户端可以顺便把请求发送给服务端了,在第一次第二次握手的时候不能携带数据。
SYN攻击是一种常见的Dos/DDoS网络攻击,攻击者会伪造多个不存在的IP地址,然后向服务器发送SYN包但是不发送对服务器发送来的SYN包的ACK。常见的防御SYN攻击的方法是:缩短超时时间,增大最大半连接数,过滤网关防护等。
为什么要三次握手?第三次握手之前,服务器并不能确定客户端的收到信息的能力,所以要三次握手。
(4) 四次挥手
- 初始状态:双方都是ESTABLISHED
- 第一次挥手:客户端发送一个FIN报文,报文FIN=1,seq=u,客户端进入FIN_WAIT_1状态。停止发送数据,主动关闭TCP连接。
- 第二次挥手:服务器收到了客户端的FIN报文,服务器会发送一个确认报文(ACK=1,ack=u+1,seq=v),服务器进入CLOSE_WAIT状态,此时是半关闭状态,客户端没东西发给服务端了,但是服务端还有点东西要发给客户端。客户端收到服务端的ACK之后,进入FIN_WAIT_2状态
- 第三次挥手:服务端也想断开连接了,那就给客户端发送一个FIN报文,(FIN=1,ACK=1,seq=w,ACK=u+1)表示结束连接,服务器进入LAST_ACK阶段。
- 第四次挥手:客户端收到了服务端的FIN报文,发送一个ACK报文给服务端(ACK=1,seq=u+1,ack=w+1).服务器端收到了报文,就进入了CLOSED状态,客户端进入到TIME_WAIT状态,过了2MSL时间侯客户端进入CLOSED状态。
为什么要四次挥手?第一,在握手中,服务端收到客户端的SYN之后,可以立即发送SYN+ACK,但是挥手中,FIN和ACK是分开来的。这是因为客户端请求关闭的时候服务器端不一定请求关闭。
为什么要2MSL?首先MSL你可以认为是TCP报文段从一个主机到另外一个主机的传递时间,假设第四次挥手的时候客户端发送的ACK报文丢包了,那么服务器会因为丢包再执行一次第三次挥手。如果过了2MSL时间服务器没有任何反应,那么就代表服务器的确收到了这个ACK,客户端可以放心地断开连接。
(5) 拥塞控制
拥塞窗口cwnd(表示可以传多少数据$C$),ssthresh(门限值,后面为$S$)
A. 慢启动:
- 一开始$C=1$
- 每收到一个ACK$C=C+1$(相当于每次过了一个往返延迟时间RTT,$C$直接翻倍,呈指数上升)
- $C >= S$时,进入拥塞避免。
B. 拥塞避免
- 每收到一个ACK,$C = C + \frac{1}{C}$
- 每过一个RTT,$C = C + 1$
C. 超时出现
- TCP会重传数据包
- $S = \frac{C}{2}$
- $C = 1$
- 进入慢启动
D. 三个冗余ACK(快速恢复算法)
- 重传冗余ACK对应的包
- $C = \frac{C}{2} + 3$
- $S = \frac{C}{2}$
- 如果收到新的ACK,就退出快速恢复。
为什么是三个冗余ACK?因为如果是两个冗余ACK的话可能是乱序,三个冗余ACK很可能是丢包导致的,这是实践经验。
(6) 建立TCP服务器的系统调用过程
客户端:
- 建立socket
- 建立连接
- 请求数据
- 关闭
服务端:
- 建立socket
- 绑定IP和
- 监听直到客户端发送请求
- 回应数据
- 关闭
(7) TCP如何保持可靠传输?
- 确认和重传
- 数据校验
- 数据合理分片和排序
- 流量控制和拥塞控制
- UDP
无连接的,尽最大努力的数据传输服务
(1)TCP和UDP的区别
- TCP面向连接,UDP无连接
- TCP提供可靠的服务,UDP尽最大努力交付,即不保证可靠交付
- TCP面向字节流,UDP面向报文
- TCP连接是点对点的,UDP支持一对多,多对一,多对多
- TCP首部20字节,UDP首部8字节
- TCP是全双工的可靠信道,UDP是不可靠信道
(2)UDP一次发送多少byte好?
1472 (1500【网络层MTU】-20【IP包包头】-8【UDP包包头】)
- 应用层协议
- HTTP 超文本传输协议 80 TCP
- HTTPS 超文本传输安全协议 443 TCP
- Telnet 远程登陆服务的标准协议 23 TCP
- FTP 文件传输协议 20传输21连接 TCP
- TFTP 简单文件传输协议 69 UDP
- SMTP 简单邮件传输协议 25 TCP
- POP 邮局协议 110 TCP
- DNS 域名解析服务 53 服务期间传输TCP,C-S传输UDP
- 网络层协议
- IP 网际协议(定义数据传输基本单元和格式,定义了数据包的递交方法和路由选择)
- ICMP Internet控制报文协议(错误检测与回报机制,检测网络的连线状态),Ping就是基于ICMP协议的。
- RIP 路由信息协议(用跳数衡量到达目标地址的路由距离)
- IGMP Internet组管理协议
- 链路层协议
- ARP 根据IP获得Mac
- RARP 根据Mac获得IP
- PPP 点对点协议
- HTTP状态码
下面是常见的 HTTP 状态码:
1xx(信息性状态码):表示接收的请求正在处理。
2xx(成功状态码):表示请求正常处理完毕。
3xx(重定向状态码):需要后续操作才能完成这一请求。
4xx(客户端错误状态码):表示请求包含语法错误或无法完成。
5xx(服务器错误状态码):服务器在处理请求的过程中发生了错误。
https://www.runoob.com/http/http-status-codes.html
What’s More
MySQL(数据库)
https://javaguide.cn/database/basis.html
Java
Java Guide:https://javaguide.cn/home.html