scribble

466300750.github.io

Blog GitHub

14 Jul 2020
计算机网络与操作系统

TCP & UDP

TCP简介

  1. 面向连接的、可靠的、基于字节流的传输层通信协议
  2. 将应用层的数据流分割成报文段并发送给目标节点的TCP层
  3. 数据包都有序号,对方接收到则发送ack,未收到则重传。
  4. 使用校验和来校验数据在传输过程中是否有误

TCP和UDP的区别:

  1. 面向连接 vs 无连接
  2. 可靠性
  3. 有序性
  4. 速度
  5. 量级(头部大小,TCP是24个字节,UDP8个字节)

如何在UDP上实现可靠传输

在UDP协议之上,实现一个带超时的请求回应机制,让业务层负责超时重发,有可能取得比TCP通信更好的效果。但前提是单个请求或回应的包不应太大。

TCP连接如何保证安全可靠的:

TCP的可靠性是通过 1. 顺序编号和确认 + 2. 数据超时重传和数据应答机制来实现的。 提供的可靠性保证:

  • 能够处理数据在传输过程中被破坏的问题
  • 能够处理接收重复数据的问题
  • 能够处理数据丢失及进行有效解决
  • 能够处理接收端数据乱序到达问题

TCP 三次握手 四次挥手

三次握手

  1. SYN=1,seq=x
  2. SYN=1,ACK=1,seq=y,ack=x+1
  3. ACK=1,seq=x+1,ack=y+1

第一次握手:建立连接时,客户端发送SYN(seq=x)包到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手:服务器收到SYN包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(seq=y),即ACK+SYN包,此时服务器进入SYN_RECV状态
第三次握手:客户端收到服务器的ACK+SYN包,向服务器发送确认包ACK(ack=y+1),次包发送完毕,客户端和服务器端进入ESTABLISHED状态,完成三次握手。

为什么需要三次握手:为了初始化Sequence Number的初始值。

为什么需要3次握手,而不是2次?

第一次握手:Client 什么都不能确认;Server 确认了对方发送正常
第二次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己接收正常,对方发 送正常
第三次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己发送、接收正常, 对方发送接收正常
所以三次握手就能确认双发收发功能都正常,缺一不可。

防止已失效的连接请求报文段突然又传到了服务器,因而产生错误。

首次握手的隐患–SYN超时 – 造成SYN Flood攻击

原因:

  1. Server收到client的SYN, 回复ACK_SYN的时候未收到ACK确认
  2. Server不断重试直至超时,Linux默认等待63秒才断开(1+2+4+8+16+32)

防护措施:

  1. SYN队列满后,通过tcp_tcp_syncookie参数回发SYN Cookie
  2. 若为正常连接则Client会发回SYN Cookie,直接建立连接

四次挥手

  1. FIN,seq=x
  2. ACK=1,seq=y,ack=x+1,服务进入CLOSE_WAIT状态
  3. FIN=1,ACK=1,seq=z,ack=x+1–客户端收到后要等2MSL后才close
  4. ACK=1,seq=x+1,ack=z+1)

2MSL(Maxium Segment Lifetime)定时器的作用有2个:

  1. 防止最后ACK丢失服务器无法关闭连接–确保有足够的时间让对方收到ACK包
  2. 防止某个连接中的重复报文段可能会出现在下一个连接中。–避免新旧连接混淆

为什么需要四次挥手?

因为TCP是全双工通信的,发送方和接收方都需要FIN报文和ACK报文

  1. 第一次挥手
    因此当主动方发送断开连接的请求(即FIN报文)给被动方时,仅仅代表主动方不会再发送数据报文了,但主动方仍可以接收数据报文。
  2. 第二次挥手
    被动方此时有可能还有相应的数据报文需要发送,因此需要先发送ACK报文,告知主动方“我知道你想断开连接的请求了”。这样主动方便不会因为没有收到应答而继续发送断开连接的请求(即FIN报文)。
  3. 第三次挥手
    被动方在处理完数据报文后,便发送给主动方FIN报文;这样可以保证数据通信正常可靠地完成。发送完FIN报文后,被动方进入LAST_ACK阶段(超时等待)。
  4. 第四挥手
    如果主动方及时发送ACK报文进行连接中断的确认,这时被动方就直接释放连接,进入可用状态。

服务器出现大量CLOSE_WAIT状态的原因:对方关闭socket连接,我方忙于读或写,没有及时关闭连接

相应对措施:

  1. 检查代码,特别是资源释放的代码
  2. 检查配置,特别是处理请求的线程配置

服务器上的查看方式:

netstat -n | awk '/^tcp/{++S[$NF]}END{for(a in S) print a,S[a]}'

输出如下:
TIME_WAIT 1507
CLOSE_WAIT 588
FIN_WAIT2 5
ESTABLISHED 413

TCP的计时器:

  • 重传计时器RTO – 与RTT(发送一个数据包到收到对应的ACK,所花费的时间)有关
  • 持续计时器 – 避免死锁问题,大小是初始与重传计时器相同,每发送一次又没有应答加倍直到上限60s为止。
  • 保活计时器 – 防止连接长时间空闲
  • TIME-WAIT计时器 – MSL的常用值是30-60s

HTTP

####状态码:

  1. 1xx:指示信息–表示请求已接收,继续处理
  2. 2xx: 成功–表示请求已被成功接收、理解、接受
  3. 3xx: 重定向–要完成请求必须进行更进一步的操作
  4. 4xx: 客户端错误–请求有语法错误或请求无法实现
  5. 5xx: 服务器端错误–服务器未能实现合法的请求

401 vs 403 – 401 Unauthorized是请求未经授权,403 Forbidden是服务器收到请求,但是拒绝提供服务(比如IP被禁)

500 vs 503 – 500 Internal Server Error 是服务器发生不可预期的错误,503 Server Unavaliable 是服务器当前不能处理客户端的请求,一段时间后可能恢复正常(比如连接池占满了)

HTTPS协议

HTTPS协议就是基于SSL的HTTP协议

HTTPS使用与HTTP不同的端⼝(HTTPS80 , HTTPSS443)提供了身份验证与加密通信⽅法,被⼴泛⽤于互联⽹上安全敏感的通信。

  1. 客户端请求SSL连接,并将⾃己⽀持的加密规则发给⽹站。
  2. 服务器端将⾃己的身份信息以证书形式发回给客户端。证书⾥面包含了⽹站地址,加密公钥,以及证书的颁发机构。
  3. 获得证书后,客户要做以下工作验证书合法性,如果证书受信任,客户端会生成一串随机数的密码,并⽤证书提供的公钥进⾏加密。将加密好的随机数发给服务。
  4. 获得到客户端发的加密了的随机数之后,服务器器⽤⾃己的私钥进⾏解密,得到这个随机数,把这个随机数作为对称加密的密钥。(利用⾮对称加密传输对称加密的密钥)
  5. 之后服务器与客户之间就可以⽤随机数对各⾃的信息进⾏加密,解密。

GET 与 POST的区别:(我们从3个层面来回答)

  1. HTTP报文层面:GET 将请求信息放到URL,POST放置在报文体中
  2. 数据库层面:GET符合幂等性和安全性,POST不符合
  3. 其他层面:GET可以被缓存、被存储,post不行

cookie和session的区别

cookie简介:

  1. 是由服务器发给客户端的特殊信息,以文本的形式存放在客户端 Set-Cookie:xxx
  2. 客户端再次请求时,会把cookie回发
  3. 服务器接收到后,会解析Cookie生成和客户端相对应的内容

session简介:

  1. 服务器端的机制,在服务器上保存的信息
  2. 根据sessionID
  3. 它的实现方式有两种:
    1. 使用cookie来实现,Set-Cookie: JSESSIONID=XXXX
    2. 使用URL回写来实现

什么是WebSockets?

WebSocket是一种计算机通信协议,通过单个TCP连接提供全双工通信信道。

WebSocket是双向的 -使用WebSocket客户端或服务器可以发起消息发送。

WebSocket是全双工的 -客户端和服务器通信是相互独立的。

单个TCP连接 -初始连接使用HTTP,然后将此连接升级到基于套接字的连接。然后这个单一连接用于所有未来的通信

Light -与http相比,WebSocket消息数据交换要轻得多。

IO 多路复用

IO多路复用是一种同步IO模型,实现一个线程可以监视多个文件句柄;一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;没有文件句柄就绪时会阻塞应用程序,交出CPU。多路是指网络连接,复用指的是同一个线程。

为什么有IO多路复用机制

没有IO多路复用机制时,有BIO、NIO两种实现方式,但有一些问题。

BIO NIO IO多路复用
1. 服务端采用单线程,当accept一个请求后,在recv或send调用阻塞时,将无法accept其他请求,无法并发处理。
2. 服务器端采用多线程,当accept一个请求后,开启线程进行recv,可以完成并发处理,但随着请求数增加需要增加系统线程,而1W个线程真正发生读写事件的线程数不会超过20%,每次开启新线程是一种资源的浪费。
服务器端当accept一个请求后,加入fds集合,每次轮询一遍fds集合recv(非阻塞)数据,没有数据则立即返回错误,每次轮询所有的fd,也会浪费CPU。 服务器端采用单线程通过select/epoll等系统调用获取fd列表,遍历有事件的fd进行accept/recv/send,使其能支持更多的并发连接请求。

IO复用的三种实现方式

比较 select poll epoll
支持一个进程能打开的最大连接数 单个进程所打开的FD是有限制的,通过FD_SETSIZE设置,其大小是32个整数的大小,32位机器上为32*32。 与select相比,只是没有fd的限制,其它基本一样。 epoll只能在Linux下。虽然FD的大小有限制,但是很大,1G内存的机器上可以打开10W左右的连接
FD剧增后带来的IO效率问题 对socket扫描是线性扫描,采用轮询的方式,效率较低。所以随着FD的增加会造成遍历速度的”线性下降“的性能问题 同左 由于epoll是根据每个FD上面的callback函数来实现的,只有活跃的socket才会主动调用callback,所以在活跃socket较少的情况下,使用epoll不会有线性下降的性能问题,但是所有socket都很活跃的情况下,可能会有性能问题
消息传递方式 每次调用select,都需要把fd集合从用户态拷贝到内核态。 同左 通过内核和用户空间共享一块内存来实现,性能较高
数据结构 bitmap array 红黑树
最大连接数 1024 无上限 无上限
fd拷贝 每次调用select拷贝 每次调用poll拷贝 fd首次调用epoll_ctl拷贝,每次调用epoll_wait不拷贝
工作效率 轮询O(n) 轮询O(n) 回调O(1)
  • select适合少量活跃连接,一般几千。
  • epoll适合大量不太活跃的连接。

epoll两种模式

epoll LT epoll ET
默认模式 高速模式
只要这个fd还有数据可读,每次epoll_wait都会返回它的事件,提醒用户程序去操作 它只会提示一次,直到下次再有数据流入之前都不会再提示了,无论fd中是否还有数据可读。所以在ET模式下,read一个fd的时候一定要把它的buffer读完,或者遇到EAGAIN错误。

操作系统命令

  1. 切忌vim直接打开大日志文件,因为会直接加载到内存的

Til next time,
at 13:14

scribble