Skip to the content.

首页

计算机网络


二级制

补码

二进制整数都是以补码的形式出现的,正数的补码等于其原码和反码,负数的补码为其反码加上1的结果,这样只需要加法运算器就可以实现加法和减法运算(符号位也参与运算)

位移运算

符号位均参与移动,注意移动的位数是mod的结果,负数右移时会在最高位补1,无符号右移则补0。


浮点数

浮点数使用科学计数法表示,由符号位、有效数字、指数三部分组成,计算机实际储存的值与真实值可能是不一样的。


网络体系结构

五层协议

TCP/IP协议簇

它只有四层,相当于五层协议中数据链路层和物理层合并为网络接口层,应用层可能会直接使用 IP 层或者网络接口层。

过程

应用层按照既定的协议打包数据,由传输层加上双方通信的端口号,由网络层加上双方的IP地址,由链路层加上双方的MAC地址,并将数据拆分为数据帧,经过多个路由器和网关后,到达目的机器,接收数据时反过来操作。


TCP

报文头

TCP报文是封装在IP报文内的,每个IP头后紧接着是一个TCP头。

三次握手

  1. A 向 B 发送连接请求报文,将 SYN 设置为1,选择一个初始的序列号 x;
  2. B 收到连接请求报文后如果同意建立连接,则向 A 发送连接确认报文,将 SYN 和 ACK 都设置为1,确认号设置为 x+1,同时也选择一个初始的序列号 y;
  3. A 收到 B 的连接确认报文后,还要向 B 发出确认报文,ACK 设置为1,确认号设置为 y+1,序列号设置为 x+1。

四次挥手

  1. A 发送连接释放报文,设置 FIN 为 1;
  2. B 收到之后发出确认报文,设置 ACK 为1,然后 B 处于 CLOSE_WAIT 状态,B 能向 A 发送数据但是 A 不能向 B 发送数据;
  3. 当 B 不再需要连接时,发送连接释放报文,设置 FIN 和 ACK 为 1;
  4. A 收到后发出确认报文,设置 ACK 为1,进入 TIME-WAIT 状态,等待 2 MSL(最大报文存活时间)后释放连接,B 收到 A 的确认报文后释放连接。

Keep Alive

TCP连接的建立是基于文件描述符的,为了及时回收资源,支持Keep Alive功能,即隔段时间向对方发送心跳,一旦出现异常就会主动关闭连接,回收资源。

可靠传输

使用超时重传机制保证,如果一个已经发送的报文段在超时时间内没有收到确认,那么就重传这个报文段。

滑动窗口和流量控制

发送方和接收方都会在缓冲区上维护一个滑动窗口,只有在窗口内的字节序被确认后才会移动窗口,接收方通过报文头的窗口字段告诉发送方自己的窗口大小,发送方根据这个值和其它信息设置自己的窗口大小,通过这种方式影响发送方的发送速率,以保证接收方来的及接收数据。

拥塞控制

拥塞控制是为了降低整个网络的拥塞程度,发送方需要维护一个叫做拥塞窗口(cwnd)的状态变量,通过四个算法来进行拥塞控制:慢开始、拥塞避免、快重传、快恢复。

TTL

位于IP报文投中,是数据包可经过的最多路由器数,初始值由源主机设置后,数据包每经过一个路由器值TTL减一,为0时则被丢弃并发送一个ICMP报文通知源主机,目的是为了防止源主机无休止的发送报文

MTU & MSS

MTU一般用来说明数据链路层的最大传输单元MSS指TCP连接建立后双方约定的最大TCP报文长度

TCP在建立连接时,收发双方根据MTU计算出各自的MSS,通过三次握手互相确认彼此的MSS大小,取较小的MSS值作为双方在TCP层分段的最大payload,这样就避免了IP包分片。

如果底层的MTU是1500byte,则 MSS = 1500- 20(IP Header) -20 (TCP Header) = 1460 byte。

PMTUD (Path MTU Discovery)

动态探测Path MTU,发出一个试探性的报文,并设置该报文不允许被分片,一直尝试直到确定出路径上的最小MTU。

虽然TCP可以确认MSS,但是两台主机之间的路径上还有路由器和交换机,而它们都有自己的MTU,如果数据帧超过了它们的MTU还是需要进行IP包分片。检查IP包的DF位以判断是否支持分片,如果支持则分片并为后几份IP包添加20字节的IP请求头;如果不支持则丢弃此IP包,同时给源主机发送回复报文并附上自己的MTU值,源主机会更新MTU值并再次尝试,这就是PMTUD。

粘包和拆包

TCP不存在粘包和拆包,只是因为存在发送缓冲区和滑动窗口机制的限制,底层可能会合并发送(使用tcpdump命令可以抓包传送细节),故在应用端为了区分来自不同send命令的数据包,可以在应用数据包前添加当前数据包的长度(Netty有封装好的实现)。


HTTP

请求方式


信息安全

CORS协议(Cross-Origin Resource Sharing)

浏览器的跨域保护机制是只有在地址的协议名、域名、端口名均一样的情况下,才允许访问相同的cookie、localStorage或是发送Ajax请求等等。由于前后端分离存在合理的跨域要求,应用比较广泛的是CORS协议(Cross-Origin Resource Sharing),其核心思路是:在HTTP的请求头中设置相应的字段,浏览器在发现HTTP请求的相关字段被设置后,则会正常发起请求,由服务器判断此请求是否是合理的跨域请求

XSS & CSRF

SSL

安全套接字层,工作于传输层和应用层之间,为应用提供数据加密传输。

HTTPS

是让 HTTP 先和 SSL 通信,再由 SSL 和 TCP 通信。

请求流程:

  1. 浏览器向服务器发送https请求;
  2. 服务器向CA请求获取证书,然后向浏览器发送证书,附带非对称加密公钥,以及双方都支持的对称加密算法;
  3. 浏览器验证CA证书,生成对称加密密钥,使用服务器的公钥加密密钥,然后发送给服务器;
  4. 服务器使用自己的非对称加密私钥解密,得到对称加密密钥;
  5. 服务器和浏览器使用此加密密钥进行通信。

浏览器请求流程

  1. 解析URL;
  2. 封装HTTP请求报文;
  3. DNS域名解析获取IP地址,一直向上请求,先浏览器缓存然后操作系统缓存然后是本地域名服务器然后是上级域名服务器,获取到后向下传递并缓存;
  4. 浏览器获取到IP后与对应IP建立TCP连接;
  5. 浏览器发送http报文,添加TCP首部、IP首部、封装为以太网帧;
  6. 利用 ARP 协议根据 IP 地址获取作为通信目的地的 MAC 地址后转发给链路层;
  7. 服务器在链路层接收到报文后向上传递,并去除首部,处理HTTP请求,并以相同的方式发送HTTP响应报文;
  8. 最后客户端通知服务器断开TCP连接。

IO模型

阻塞和非阻塞:读写没有就绪或者读写没有完成,函数是否要一直等待还是采用轮询; 同步和异步:同步是读写由应用程序完成。异步是读写由操作系统来完成,并通过回调的机制通知应用程序。

BIO

同步阻塞IO,面向流,传统的模型,典型应用如Java的BIO。

应用进程使用recvfrom发起IO系统调用后,会一直阻塞直到内核缓存区的数据准备好,并将数据拷贝至用户空间,阻塞期间不消化CPU资源。

NIO

同步非阻塞IO,面向缓冲区,核心是Buffer(缓冲区)、Channel(通道)和Selector(多路复用器)。

应用进程仍然是使用recvfrom发起IO系统调用,但如果内核缓存区数据未准备好则立即返回错误标识,准备好了则将数据拷贝到用户空间,因此需要应用程序不断发起询问内核数据是否准备好。

多路复用IO

即Reactor模型(主动模式),使用fd(文件句柄)绑定一个socket,典型的如Linux的select、poll和epoll以及Java的NIO

多个应用进程的IO事件注册到一个复用器(select)上,然后使用一个进程调用该select,select会监听所有注册进来的IO事件,会一直阻塞直到任一个IO的数据在内核缓冲区中可用,select调用进程可以自己或通知注册进程来再次发起recvfrom读取内核缓冲区中准备好的数据。

信号驱动IO

应用进程发起一个IO操作,信号处理程序通过系统调用sigaction,往内核注册一个信号处理函数,然后请求即刻返回,当内核数据准备就绪后,就生成对应进程的SIGIO信号,通过信号处理程序通知应用线程可以调用recvfrom来读取数据。

异步IO

Proactor模型(被动模式),属于异步操作,因为只有异步IO不需要应用进程自己调用recvfrom来读取数据,典型如Java的AIO。

应用进程发起一个aio_read请求之后直接返回,如果内核缓存区数据准备好了,内核主动拷贝数据到用户空间,完成后通过aio_read中指定的信号通知到应用进程。


零拷贝和直接内存映射

NIO的零拷贝由transferTo方法实现,transferTo方法将数据从FileChannel对象传送到可写的字节通道(如Socket Channel等),将将数据直接在从一个通道传输到另一个通道,而不需要借助缓冲区。需要借助操作系统,在linux系统中会引起sendfile()系统调用,实现了数据直接从内核的读缓冲区传输到套接字缓冲区,避免了用户态与内核态之间的数据拷贝。

流程:

  1. transferTo方法调用触发DMA引擎将文件上下文信息拷贝到内核读缓冲区,接着内核将数据从内核缓冲区拷贝到与套接字相关联的缓冲区。
  2. DMA引擎将数据从内核套接字缓冲区传输到协议引擎(第三次数据拷贝)。

mmap

通过内存映射,将文件直接映射到内核缓存区,这样就免去了拷贝,同时用户空间可以共享内核空间的数据。

sendFile

首先还是DMA拷贝到内核buffer,然后再通过CPU拷贝到socket buffer,最后DMA拷贝到协议栈。但这次的CPU拷贝,拷贝的内容很少,只拷贝内核buffer的长度、偏移量等信息,消耗很低,可以忽略,即就是零拷贝。