复习及面试题总结——计算机网络
- 网络编程部分
- 计算机网络部分
- 网络字节序
- 网络分层模型
- 为什么 TCP/IP 去除了表示层和会话层
- 传输过程
- 二层转发和三层路由
- TCP包头格式
- UDP包头格式
- IP报头
- MAC头部
- TCP、UDP协议的区别
- 请你说说传递到IP层怎么知道报文该给哪个应用程序,它怎么区分UDP报文还是TCP报文
- UDP的特点及应用场景
- UDP如何实现可靠性传输?
- TCP中如何判断异步的connect是否连接成功
- TCP三次握手
- TCP四次挥手
- CLOSE_WAIT的作用
- 半关闭状态
- TIME_WAIT
- 为什么连接的时候是三次握手,关闭的时候却是四次握手?
- TCP状态转换图
- TCP 是如何保证可靠性的
- TCP 超时重传的原理
- TCP 的停止等待协议(ARQ)是什么
- TCP滑动窗口
- TCP阻塞控制
- 拥塞控制和流量控制的区别
- 高并发服务器客户端主动关闭连接和服务端主动关闭连接的区别
- 心跳包的作用
- TCP 粘包问题
- SYN FLOOD 是什么
- 为什么服务端易受到 SYN 攻击
- 在浏览器中输入url地址->>显示主页的过程
- 搜索baidu,会用到计算机网络中的什么层?每层是干什么的
- 你能试着解释一下在浏览器里点击页面链接后发生了哪些事情吗?
- Web 页面请求过程(URL 请求过程)
- 什么是HTTP
- HTTP报文
- HTTP响应报文
- HTTP的状态码
- HTTP请求方法
- HTTP的流程图
- HTTP有哪些特点
- HTTP大文件传输的方法
- HTTP的优缺点
- HTTP的连接管理
- HTTP传输大文件的方法
- HTTP 是不保存状态的协议,如何保存用户状态
- HTTP1.1 和 1.0 的区别
- HTTP和HTTPS的区别
- HTTP2.0 的特点
- HTTPS是怎么实现安全加密的
- HTTPS 的加密方式
- 非对称加密为什么慢
- 对称加密和非对称的区别,非对称加密有哪些
- 数字签名与证书
- TLS1.2连接过程
- SSL握手
- HTTPS的优化
- 什么是P2P协议以及其优点
- 怎么知道哪些 peer 有这个文件呢?
- 什么是去中心化网络DHT?
- 如何维护DHT网络
- 什么是DNS服务器
- DNS的解析流程
- 域名解析查询的两种方式
- DNS有哪些作用或者应用
- 什么是负载均衡
- 全局负载均衡器会失灵(故障)吗?应该怎么解决?
- RPC知道吗?一般怎么设计?
- 如果是一个不存在的域名,那么浏览器的工作流程会是怎么样的呢?
- CDN 在对待浏览器和爬虫时会有差异吗?为什么?
- IP地址和MAC
- IP 协议的定义和作用
- IPV4 地址不够如何解决
- 路由器和交换机的区别
- 路由器的分组转发流程
- ICMP 协议概念/作用
- ICMP 的应用
- ARP头部
- ARP 协议
- ARP 的位置
- ARP 地址解析协议的原理和地址解析过程
- ARP 协议的解析过程
- TCP/IP数据链路层的交互过程
- 两台电脑连起来后 ping 不通,你觉得可能存在哪些问题?
- 运输层协议和网络层协议的区别
- TTL 是什么?有什么作用
- ping
- MAC 地址和 IP 地址分别有什么作用
- 为什么有了 MAC 地址还需要 IP 地址
- 为什么有了 IP 地址还需要 MAC 地址
- 数据链路层上的三个基本问题
- 私网地址和公网地址之间进行转换
- 物理层主要做什么事情
- 主机之间的通信方式
- 通道复用技术
- ARP 攻击
- 路由器与交换机
- 如何设计server,使得能够接收多个客户端的请求
网络编程部分
客户端与服务器工作的核心逻辑
右侧的图显示的是服务器端初始化的过程,首先初始化 socket,之后服务器端需要执行 bind 函数,将自己的服务能力绑定在一个地址和端口上,紧接着,服务器端执行 listen 操作,将原先的 socket 转化为服务端的 socket,服务端最后阻塞在 accept 上等待客户端请求的到来。
客户端需要先初始化 socket,再执行 connect 向服务器端的地址和端口发起连接请求,也就是 TCP 三次握手。一旦三次握手完成,客户端和服务器端建立连接,就进入了数据传输过程。
网络编程的一般步骤
TCP:
服务端:socket -> bind -> listen -> accept -> recv/send -> close。
socket:创建套接字
bind:绑定到ip和port
listen:将套接字由主动变为被动,等待连接;创建连接队列,分为已完成连接队列和未完成连接队列,总和最大值为128。此时为监听套接字;
accept:从已完成连接队列中提取已连接套接字。(三次握手发生在已连接之后,因为只有完成连接之后才去提取)
recv:ssize_t recv(int sockfd, void *buf, size_t len, int flags);//flags==MSG_PEEK 读数据不会删除缓冲区的数据
ssize_t send(int sockfd, const void *buf, size_t len, int flags);//flags=1 紧急数据
客户端:socket -> connect -> send/recv -> close。
UDP:
服务端:socket -> bind -> recvfrom/sendto -> close。
客户端:socket -> sendto/recvfrom -> close。
发数据:
sendto:ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
dest_addr: 目的地的地址信息
addrlen: 结构体大小
收数据:
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
src_addr: 对方的地址信息
addrlen: 结构体大小的地址
TCP创建的是流套接字,UDP创建的是数据报套接字。
socket
socket是应用层与传输层的一个中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,将复杂的TCP/IP协议隐藏在Socket接口之后,只对应用层暴露简单的接口。Socket屏蔽了各个协议的通信细节,使得程序员无需关注协议本身,直接使用socket提供的接口来进行互联的不同主机间的进程的通信,当然也用于本地进程间的通信。
套接字(Socket)是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象,网络进程通信的一端就是一个套接字,不同主机上的进程便是通过套接字发送报文来进行通信。例如 TCP 用主机的 IP 地址 + 端口号作为 TCP 连接的端点,这个端点就叫做套接字。
如上图的客户端与服务器工作的核心逻辑,无论是客户端的 connect,还是服务端的 accept,或者 read/write 操作等,都是通过socket来完成的。socket 是我们用来建立连接,传输数据的唯一途径。
套接字的格式
本地套接字
为什么本地套接字格式不需要端口号,而 IPv4 和 IPv6 套接字格式却需要端口号呢?
socket主要是为了进程间通信,本地套接字主要用于本地IPC,本质上是访问本地的文件系统,所以自然就不需要端口。网络套接字用于跨机器通信,远程socket是直接将一段字节流发送到远程计算机的一个进程,而远程计算机可能同时有多个进程在监听,所以用端口号标定要发给哪一个进程。
创建本地套接字用于TCP通信
1.创建本地套接字
int socket(int domain, int type, int protocol);
参数:
domain : AF_UNIX // 本地通信
type :SOCK_STREAM
protocol : 0
2.绑定
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
sockfd: 本地套接字
addr: 本地套接字结构体地址
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX /
char sun_path[108]; / pathname *///文件的路径名
};
addrlen: sockaddr_un大小
3.监听
int listen(int sockfd, int backlog);
4.提取
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
addr: struct sockaddr_un 结构体地址
注意:客户端可以隐式绑定,但是服务器不可以;绑定指定文件时m,这个文件必须不存在,如果存在绑定失败。
socket() 套接字有哪些
套接字主要有以下三种类型:
流套接字(SOCK_STREAM):流套接字基于 TCP 传输协议,主要用于提供面向连接、可靠的数据传输服务。值得注意的是,在基于TCP协议的socket程序函数调用过程中,服务端有两个Socket,一个监听Socket,一个是已连接Socket。
监听Socket是Socket函数创建的,已连接Socket是accpet函数返回的,此socket代表网络中的端对端连接,所以用这个socket进行通信,而监听socket在整个服务生命周期内只有这一个。 为什么需要两个Socket? 如果使用一个Socket的话,那么它的功能太多,使得使用很不直观,同时在内核确实产生了一个这样的新的Socket描述字。
数据报套接字(SOCK_DGRAM):数据报套接字基于 UDP 传输协议,对应于无连接的 UDP 服务应用。UDP 是没有连接的,所以不需要三次握手,也就不需要调用 listen 和 connect,但是,UDP 的交互仍然需要 IP 和端口号,因而也需要 bind。UDP 是没有维护连接状态的,因而不需要每对连接建立一组 Socket,而是只要有一个 Socket,就能够和多个客户端通信。
原始套接字(SOCK_RAW):由于流套接字和数据报套接字只能读取 TCP 和 UDP 协议的数据,当需要传送非传输层数据包(例如 Ping 命令时用的 ICMP 协议数据包)或者遇到操作系统无法处理的数据包时,此时就需要建立原始套接字来发送。
ICMP 全称 Internet Control Message Protocol,就是互联网控制报文协议。
详细见趣谈网络协议第七讲
Unix/Linux 的五种I/O模型
1.blocking I/O 【阻塞I/O】:调用者调用了某个函数,等待这个函数返回,期间什么也不做,不停的去检查这个函数有没有返回,必须等这个函数返回才能进行下一步动作。
2.nonblocking I/O 【非阻塞I/O】:非阻塞等待,每隔一段时间就去检测IO事件是否就绪。没有就绪就可以做其他事。
3.I/O multiplexing (select and poll) 【多路复用I/O】:linux用select/poll函数实现IO复用模型,这两个函数也会使进程阻塞,但是和阻塞IO所不同的是这两个函数可以同时阻塞多个IO操作。而且可以同时对多个读操作、写操作的IO函数进行检测。知道有数据可读或可写时,才真正调用IO操作函数。
- signal driven I/O (SIGIO) 【信号驱动】:信号驱动IO:linux用套接口进行信号驱动IO,安装一个信号处理函数,进程继续运行并不阻塞,当IO时间就绪,进程收到SIGIO信号。然后处理IO事件。
- asynchronous I/O (the POSIX aio_ functions) 【异步I/O,由POSIX规范定义的】:linux中,可以调用aio_read函数告诉内核描述字缓冲区指针和缓冲区的大小、文件偏移及通知的方式,然后立即返回,当内核将数据拷贝到缓冲区后,再通知应用程序。
零拷贝
传统的 Linux 操作系统的标准 I/O 接口是基于数据拷贝操作的,即 I/O 操作会导致数据在操作系统内核地址空间的缓冲区和应用程序地址空间定义的缓冲区之间进行传输。这样做最大的好处是可以减少磁盘 I/O 的操作,因为如果所请求的数据已经存放在操作系统的高速缓冲存储器中,那么就不需要再进行实际的物理磁盘 I/O 操作。但是数据传输过程中的数据拷贝操作却导致了极大的 CPU 开销,限制了操作系统有效进行数据传输操作的能力。
零拷贝就是一种避免 CPU 将数据从一块存储拷贝到另外一块存储的技术。针对操作系统中的设备驱动程序、文件系统以及网络协议堆栈而出现的各种零拷贝技术极大地提升了特定应用程序的性能,并且使得这些应用程序可以更加有效地利用系统资源。这种性能的提升就是通过在数据拷贝进行的同时,允许 CPU 执行其他的任务来实现的。
避免数据拷贝
- 避免操作系统内核缓冲区之间进行数据拷贝操作。
- 避免操作系统内核和用户应用程序地址空间这两者之间进行数据拷贝操作。
- 用户应用程序可以避开操作系统直接访问硬件存储。
- 数据传输尽量让 DMA 来做。
将多种操作结合在一起 - 避免不必要的系统调用和上下文切换。
- 需要拷贝的数据可以先被缓存起来。
- 对数据进行处理尽量让硬件来做
四种I/O模型:阻塞 / 非阻塞 / 同步 / 异步
阻塞I/O(blocking I/O)
阻塞I/O:当应用程序调用阻塞 I/O 完成某个操作时,应用程序会被挂起,等待内核完成操作,只有在得到结果之后再返回,感觉上应用程序像是被“阻塞”了一样。实际上,内核所做的事情是将 CPU 时间切换给其他有需要的进程,网络应用程序在这种情况下就会得不到 CPU 时间做该做的事情。
例如阻塞 I/O 发起的 read 请求,线程会被挂起,一直等到内核数据准备好,并把数据从内核区域拷贝到应用程序的缓冲区中,当拷贝过程完成,read 请求调用才返回。接下来,应用程序就可以对缓冲区的数据进行数据解析。
非阻塞I/O(nonblocking I/O)
非阻塞I/O:当应用程序调用非阻塞 I/O 完成某个操作时,内核立即返回,不会把 CPU 时间切换给其他进程,应用程序在返回后,可以得到足够的 CPU 时间继续完成其他事情。
例如非阻塞的 read 请求,在数据未准备好的情况下立即返回,应用程序可以不断轮询内核,直到数据准备好,内核将数据拷贝到应用程序缓冲,并完成这次 read 调用。注意,这里最后一次 read 调用,获取数据的过程,是一个同步的过程。这里的同步指的是内核区域的数据拷贝到缓存区这个过程。
阻塞I/O与非阻塞I/O的区别
阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态。
对于读数据:
如果内核缓冲没有数据可读时,read()系统调用会一直等待有数据到来后才从阻塞态中返回,这就是阻塞 I/O。
非阻塞 I/O 在遇到上述情况时会立即返回给用户态进程一个返回值-1,并有 EWOULDBLOCK 或 EAGAIN 错误。
对于往缓冲区写的操作同理。
read 和 write 在阻塞模式和非阻塞模式下的不同行为特性:
非阻塞 I/O 的多路复用
对于非阻塞I/O,每次让应用程序去轮询内核的 I/O 是否准备好,是一个不经济的做法,因为在轮询的过程中应用进程啥也不能干。于是,像 select、poll 这样的 I/O 多路复用技术就隆重登场了。通过 I/O 事件分发,当内核数据准备好时,再通知应用程序进行操作。这个做法大大改善了应用进程对 CPU 的利用率,在没有被通知的情况下,应用进程可以使用 CPU 做其他的事情。
同步调用技术
无论是第一种阻塞 I/O,还是第二种非阻塞 I/O,第三种基于非阻塞 I/O 的多路复用都是同步调用技术。为什么这么说呢?因为同步调用、异步调用的说法,是对于获取数据的过程而言的,前面几种最后获取数据的 read 操作调用,都是同步的,在 read 调用时,内核将数据从内核空间拷贝到应用程序空间,这个过程是在 read 函数中同步进行的,如果内核实现的拷贝效率很差,read 调用就会在这个同步过程中消耗比较长的时间。
异步I/O(asynchronous I/O )
异步I/O由POSIX规范定义的。当我们发起 aio_read 之后,就立即返回,内核自动将数据从内核空间拷贝到应用程序空间,这个拷贝过程是异步的,内核自动完成的,和前面的同步操作不一样,应用程序并不需要主动发起拷贝动作。
同步和异步
调用者必须循环自去查看事件有没有发生,这种情况是同步。调用者不用自己去查看事件有没有发生,而是等待着注册在事件上的回调函数通知自己,这种情况是异步。
I/O 多路复用技术select、poll、epoll
I/O模型以及多路复用三种实现方式select、poll和epoll比较及区别
I/O多路复用
参考地址
IO多路复用:I/O是指网络I/O,多路指多个TCP连接(即socket或者channel),复用指复用一个或少量线程。意思是说多个网络I/O复用一个或少量线程处理多个TCP连接。
I/O多路复用技术详解
select
函数描述
int select(int maxfd, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);
返回:若有就绪描述符则为其数目,若超时则为0,若出错则为-1
maxfd 表示的是待测试的描述符基数,它的值是待测试的最大描述符加 1。
紧接着的是三个描述符集合,分别是读描述符集合 readset、写描述符集合 writeset 和异常描述符集合 exceptset,这三个分别通知内核,在哪些描述符上检测数据可以读,可以写和有异常发生。
select能监控的描述符个数由内核中的FD_SETSIZE限制,仅为1024,这也是select最大的缺点,因为现在的服务器并发量远远不止1024。即使能重新编译内核改变FD_SETSIZE的值,但这并不能提高select的性能。
最后一个参数是 timeval 结构体时间,struct timeval用来代表时间值,有两个成员,一个是秒数,另一个是毫秒数。 若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回。
返回值:变化的文件描述符的个数。
基本原理
每次调用select都会线性扫描所有描述符的状态:writefds(写)、readfds(读)、和exceptfds(异常)。select会阻塞住监视3类文件描述符,等有数据、可读、可写、出异常或超时、就会返回;.在select结束后,用户也要线性扫描fd_set数组才知道哪些描述符准备就绪,然后进行对应的I/O操作。
select模型的特点:
(1) 文件描述符个数有限,一般来说这个数目和系统内存关系很大。select使用位域的方式来传递关心的文件描述符,位域就有最大长度。select使用位域的方式传回就绪的文件描述符,调用者需要循环遍历每一个位判断是否就绪,当文件描述符个数很多,但是空闲的文件描述符大大多于就绪的文件描述符的时候,效率很低。
(2) 将fd加入select监控集的同时,还要再使用一个数据结构array保存放到select监控集中的fd,一是用于在select 返回后,array作为源数据和fd_set进行FD_ISSET判断。二是select返回后会把以前加入的但并无事件发生的fd清空,则每次开始 select前都要重新从array取得fd逐一加入(FD_ZERO最先),扫描array的同时取得fd最大值maxfd,用于select的第一个 参数。
优点:几乎在所有的平台上支持,跨平台支持性好。
缺点:
- 由于是采用轮询方式全盘扫描,会随着文件描述符FD数量增多而性能下降;
- 每次调用 select(),需要把 fd 数组从用户态拷贝到内核态,并进行遍历(消息传递都是从内核到用户空间);
- 所支持文件描述符的个数是有限的,select的默认最大值是1024,可修改宏定义,但是效率仍然慢。
优化:若中间有一个文件描述符关闭了,则将最后一个文件描述重定向至该关闭的文件描述符的位置。
问题集
假设现在 4-1023个文件描述符需要监听,但是5-1000这些文件描述符关闭了?
自定义数组,将未关闭的文件描述符加入到自定义数组中,然后遍历自定义数组。
假设现在 4-1023个文件描述符需要监听,但是只有 5,1002 发来消息- 无解
poll
函数描述
int poll(struct pollfd *fds, unsigned long nfds, int timeout);
返回值:若有就绪描述符则为其数目,若超时则为0,若出错则为-1
第一个参数是一个 pollfd 的数组。其中 pollfd 的结构如下:
struct pollfd {
int fd; /* file descriptor */
short events; /* events to look for */
short revents; /* events returned */
};
这个结构体由三个部分组成,首先是描述符 fd,然后是描述符上待检测的事件类型 events,注意这里的 events 可以表示多个不同的事件,具体的实现可以通过使用二进制掩码位操作来完成,例如,POLLIN 和 POLLOUT 可以表示读和写事件。
和 select 非常不同的地方在于,poll 每次检测之后的结果不会修改原来的传入值,而是将结果保留在 revents 字段中,这样就不需要每次检测完都得重置待检测的描述字和感兴趣的事件。我们可以把 revents 理解成“returned events”。
第二个参数 nfds 描述的是数组 fds 的大小,简单说,就是向 poll 申请的事件检测的个数。
最后一个参数 timeout,描述了 poll 的行为。
如果是一个 <0 的数,表示在有事件发生之前永远等待;如果是 0,表示不阻塞进程,立即返回;如果是一个 >0 的数,表示 poll 调用方等待指定的毫秒数后返回。
poll 函数有一点非常好,如果我们不想对某个 pollfd 结构进行事件检测,可以把它对应的 pollfd 结构的 fd 成员设置成一个负值。这样,poll 函数将忽略这样的 events 事件,检测完成以后,所对应的“returned events”的成员值也将设置为 0。
poll函数与select函数的对比
和 select 函数对比一下,我们发现 poll 函数和 select 不一样的地方就是,在 select 里面,文件描述符的个数已经随着 fd_set 的实现而固定,没有办法对此进行配置;而在 poll 函数里,我们可以控制 pollfd 结构的数组大小,这意味着我们可以突破原来 select 函数最大描述符的限制,在这种情况下,应用程序调用者需要分配 pollfd 数组并通知 poll 函数该数组的大小。
基本原理
基本原理与select一致,也是轮询+遍历,唯一的区别就是poll使用pollfd结构来存储fd,突破了select中描述符数目的限制。
与select的后两点类似,poll仍然需要将pollfd数组拷贝到内核空间,之后依次扫描fd的状态,整体复杂度依然是O(n)的,在并发量大的情况下服务器性能会快速下降。
优点:
- 突破了select中描述符数目的限制;
- 几乎在所有的平台上支持,跨平台支持性好。(同select)
缺点:
- 由于是采用轮询方式全盘扫描,会随着文件描述符FD数量增多而性能下降;(同select)
- 每次调用 poll,需要把 pollfd 数组从用户态拷贝到内核态,并进行遍历(消息传递都是从内核到用户空间);(同select)
epoll
epoll原理详解及epoll反应堆模型
函数描述
使用 epoll 进行网络程序的编写,需要三个步骤,分别是 epoll_create,epoll_ctl 和 epoll_wait。
int epoll_create(int size);
参数size是监听文件描述符的上限,Linux2.6版本之后大于0的整数即可。返回值是树的句柄。
执行epoll_create()时,内核除了帮我们在epoll文件系统里建了个file结点,在内核cache里建了个红黑树用于存储以后epoll_ctl传来的socket外,还会再建立一个rdllist双向链表,用于存储准备就绪的事件,当epoll_wait调用时,仅仅观察这个rdllist双向链表里有没有数据即可。有数据就返回,没有数据就sleep,等到timeout时间到后即使链表没数据也返回。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epfd: 树的句柄;op:EPOLL_CTL_ADD,EPOLL_CTL_DEL,EPOLL_CTL_MOD;fd:文件描述符;event:树上的结点,注册的事件类型。
执行epoll_ctl()时,如果增加socket句柄,则检查在红黑树中是否存在,存在立即返回,不存在则添加到树干上,然后向内核注册回调函数,用于当中断事件来临时向准备就绪链表中插入数据;
typedef union epoll_data {
void *ptr; int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
epfd: 树的句柄;events:接收变化的结点的数组首地址;maxevents:可以返回的最大事件值。
返回值: 成功返回的是一个大于0的数,表示事件的个数;返回0表示的是超时时间到;若出错返回-1.
执行epoll_wait()时立刻返回准备就绪链表里的数据即可。
总结:一棵红黑树,一张准备就绪句柄链表,少量的内核cache,就帮我们解决了大并发下的socket处理问题。
epoll实现原理
Linux epoll机制是通过红黑树和双向链表实现的。 首先通过epoll_create()系统调用在内核中创建一个eventpoll类型的句柄,其中包括红黑树根节点和双向链表头节点。然后通过epoll_ctl()系统调用,向epoll对象的红黑树结构中添加、删除、修改感兴趣的事件,返回0标识成功,返回-1表示失败。最后通过epoll_wait()系统调用判断双向链表是否为空,如果为空则阻塞。当文件描述符状态改变,fd上的回调函数被调用,该函数将fd加入到双向链表中,此时epoll_wait函数被唤醒,返回就绪好的事件。
epoll的优缺点
- epoll维护了一棵红黑树来跟踪所有待检测的文件描述字,红黑树的使用减少了内核和用户空间大量的数据拷贝和内存分配,大大提高了性能。
- epoll维护了一个链表俩记录就绪事件,内核在每个事件有事件发生时将自己登记到这个就绪事件列表中,通过内核自身的文件file-eventpoll直接的回调和唤醒机制,减少了对内核描述字的遍历,大大加速了事件通知和检测的效率。
epoll的工作模式
epoll的两种触发模式
epoll有EPOLLLT和EPOLLET两种触发模式,LT是默认的模式,ET是“高速”模式。
LT(水平触发)模式下,只要文件描述符可以非阻塞地执行 I/O ,就会触发通知。只要读缓冲区有数据,就会触发epoll_wait去读操作;如果写缓冲区还没满,可写。
ET(边缘触发)模式下,只有在文件描述符的状态发生改变(也就是 I/O 请求达到)时,才发送一次通知。在它检测到有 I/O 事件时,通过 epoll_wait 调用会得到有事件通知的文件描述符,对于每一个被通知的文件描述符,如可读,则必须将该文件描述符一直读到空,让 errno 返回 EAGAIN 为止,否则下次的 epoll_wait 不会返回余下的数据,会丢掉事件。如果ET模式不是非阻塞的,那这个一直读或一直写势必会在最后一次阻塞。如可写,当缓冲区从无到有时,就会触发。
总结:
因为设置为水平触发,只要缓存区有数据epoll_wait就会被触发,epoll_wait是一个系统调用,尽量少调用。
所以尽量使用边沿触发,边沿出触发数据来一次只触发一次,这个时候要求一次性将数据读完,所以while循环读,读到最后read默认带阻塞,不能让read阻塞,因为不能再去监听。设置cfd为非阻塞,read读到最后一次返回值为-1。判断errno的值为EAGAIN,代表数据读干净。
I/O多路复用适用场景
(1)当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/O复用。
(2)当一个客户同时处理多个套接口时,而这种情况是可能的,但很少出现。
(3)如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O复用。
(4)如果一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用。
(5)如果一个服务器要处理多个服务或多个协议,一般要使用I/O复用。
epoll反应堆模型
反应堆模型的思想:利用封装的思想,将文件描述符,对应的事件,和事件产生时对于的函数(回调函数)封装到一起,这样当某个文件描述符的事件发生了,回调函数会自动被触发,这就是所谓的反应堆思想。
epoll模型原来的流程
- epoll_create(); // 创建监听红黑树
- epoll_ctl(); // 向树上添加监听fd
- epoll_wait(); // 监听
- 有监听fd事件发送—>返回监听满足数组—>判断返回数组元素
- lfd满足accept—>返回cfd---->read()读数据—>write()给客户端回应。
epoll反应堆模型的流程
- epoll_create(); // 创建监听红黑树
- epoll_ctl(); // 向书上添加监听fd
- epoll_wait(); // 监听
- 有客户端连接上来—>lfd调用acceptconn()—>将cfd挂载到红黑树上监听其读事件
- epoll_wait()返回cfd—>cfd回调recvdata()—>将cfd摘下来监听写事件
- epoll_wait()返回cfd—>cfd回调senddata()—>将cfd摘下来监听读事件
server端监听端口,但还没有客户端连接进来,此时进程处于什么状态?
最普通的Server模型,则处于阻塞状态;如果使用IO复用中epoll、select等,则处于运行状态。
计算机网络部分
网络字节序
主机字节序
不同的主机有不同的字节序,如x86为小端字节序,Motorola 6800为大端字节序,ARM字节序是可配置的。
网络字节序
网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节顺序采用big endian排序方式。
为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。
网络分层模型
TCP/IP 网络分层模型
第一层叫“链接层”(link layer),负责在以太网、WiFi 这样的底层网络上发送原始数据包,工作在网卡这个层次,使用 MAC 地址来标记网络上的设备,所以有时候也叫 MAC 层。
第二层叫“网际层”或者“网络互连层”(internet layer),IP 协议就处在这一层。因为 IP 协议定义了“IP 地址”的概念,所以就可以在“链接层”的基础上,用 IP 地址取代 MAC 地址,把许许多多的局域网、广域网连接成一个虚拟的巨大网络,在这个网络里找设备时只要把 IP 地址再“翻译”成 MAC 地址就可以了。
第三层叫“传输层”(transport layer),这个层次协议的职责是保证数据在 IP 地址标记的两点之间“可靠”地传输,是 TCP 协议工作的层次,另外还有它的一个“小伙伴”UDP。
协议栈的第四层叫“应用层”(application layer)。
OSI 网络分层模型
OSI,全称是“开放式系统互联通信参考模型”(Open System Interconnection Reference Model)
第一层:物理层,网络的物理形式,例如电缆、光纤、网卡、集线器等等;
第二层:数据链路层,它基本相当于 TCP/IP 的链接层;
第三层:网络层,相当于 TCP/IP 里的网际层;
第四层:传输层,相当于 TCP/IP 里的传输层;
第五层:会话层,维护网络中的连接状态,即保持会话和同步;
第六层:表示层,把数据转换为合适、可理解的语法和语义;
第七层:应用层,面向具体的应用传输数据。
两个分层模型的映射关系
第一层:物理层,TCP/IP 里无对应;
第二层:数据链路层,对应 TCP/IP 的链接层;
第三层:网络层,对应 TCP/IP 的网际层;
第四层:传输层,对应 TCP/IP 的传输层;
第五、六、七层:统一对应到 TCP/IP 的应用层。
有一个辨别四层和七层比较好的(但不是绝对的)小窍门,“两个凡是”:凡是由操作系统负责处理的就是四层或四层以下,否则,凡是需要由应用程序(也就是你自己写代码)负责处理的就是七层。
为什么 TCP/IP 去除了表示层和会话层
OSI 参考模型在提出时,他们的理想是非常好的,但实际上,由于会话层、表示层、应用层都是在应用程序内部实现的,最终产出的是一个应用数据包,而应用程序之间是几乎无法实现代码的抽象共享的,这也就造成 OSI 设想中的应用程序维度的分层是无法实现的,例如,我们几乎不会认为数据的压缩、加密算法算是一种协议,而会话的概念则更为抽象,难以用协议来进行描述,所以在后来的 TCP/IP 协议框架的设计中,便将表示层和会话层与应用层整合在一起,让整个过程更为清晰明了。
传输过程
应用层:DNS、HTTP、HTTPS 。
经过应用层封装后,浏览器会将应用层的包交给下一层(传输层)去完成,通过 socket 编程来实现。
传输层:UDP、TCP。TCP 协议里面会有两个端口,一个是浏览器监听的端口,一个是电商的服务器监听的端口。操作系统往往通过端口来判断,它得到的包应该给哪个进程。(网络套接字中为什么需要端口?)
网络层:IP。在 IP 协议里面会有源 IP 地址,即浏览器所在机器的 IP 地址和目标 IP 地址。
操作系统如何将 IP 地址发给网关呢?在本地通信基本靠吼,于是操作系统大吼一声,谁是 192.168.1.1 啊?网关会回答它,我就是,我的本地地址在村东头。这个本地地址就是 MAC 地址,而大吼的那一声是 ARP 协议。操作系统将 IP 包交给了下一层,也就是 MAC 层。网卡再将包发出去。由于这个包里面是有 MAC 地址的,因而它能够到达网关。
网关收到包之后,会根据自己的知识,判断下一步应该怎么走。网关往往是一个路由器,到某个 IP 地址应该怎么走,这个叫作路由表。城关往往是知道这些“知识”的,因为城关和临近的城关也会经常沟通。到哪里应该怎么走,这种沟通的协议称为路由协议,常用的有 OSPF 和 BGP。
二层转发和三层路由
一个 HTTP 协议的包经过一个二层设备,二层设备收进去的是整个网络包。这里面 HTTP、TCP、 IP、 MAC 都有。
二层设备:只把 MAC 头摘下来,看看到底是丢弃、转发,还是自己留着。
三层设备:把 MAC 头摘下来之后,再把 IP 头摘下来,看看到底是丢弃、转发,还是自己留着。
二层转发:二层转发指的是设备处于数据链路层,工作在两层的设备,通过查找到目标MAC地址,进行数据转发和广播。
三层路由: 三层路由指的是设备工作在网络层,通过分析报文中的头部信息,找到目标IP地址,进行本地转发或下一个网关。
TCP包头格式
源端口号、目标端口号:TCP协议通过使用”端口”来标识源端和目标端的应用进程,确定数据发送给哪个应用。
序号:用来标识从TCP源端向TCP目标端发送的数据字节流,为解决乱序问题。
确认序号:为了确认当前的包是否收到,解决丢包的问题。
状态位:SYN 是发起一个连接,ACK 是确认序号有效,RST 是重新连接,FIN 是释放一个连接,URG:紧急指针(urgent pointer)有效,PSH:接收方应该尽快将这个报文段交给应用层。
窗口大小:此字段用来进行流量控制。单位为字节数,这个值是本机期望一次接收的字节数。
校验和:对整个TCP报文段,即TCP头部和TCP数据进行校验和计算,并由目标端进行验证。
紧急指针字段:占16比特。它是一个偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号。
选项字段:占32比特。可能包括”窗口扩大因子”、”时间戳”等选项。
UDP包头格式
源端口号、目标端口号:同TCP包头。
UDP长度:标明UDP头部和UDP数据的总长度字节。
UDP校验和:用来对UDP头部和UDP数据进行校验。和TCP不同的是,对UDP来说,此字段是可选项,而TCP数据段中的校验和字段是必须有的。
IP报头
MAC头部
TCP、UDP协议的区别
- TCP 是一个有状态的协议,需要先与对方建立连接然后才能发送数据,而且保证数据无差错、不丢失、不重复、按序到达。而 UDP 则比较简单,它无状态,不用事先建立连接就可以任意发送数据,但不保证数据一定会发到对方。
- TCP 面向字节流传输,因此可以被分割并在接收端重组;UDP 面向数据报传输。
- TCP 通过序号、重传、流量控制、拥塞控制实现可靠传输;UDP 不保障可靠传输,尽最大努力交付。
请你说说传递到IP层怎么知道报文该给哪个应用程序,它怎么区分UDP报文还是TCP报文
根据端口区分;
看ip头中的协议标识字段,17是udp,6是tcp
UDP的特点及应用场景
特点
UDP是无连接的,即发送数据之前不需要建立连接(当然,发送数据结束时也没有连接可以释放),因此减少了开销和发送数据之前的时延。
UDP使用尽最大努力交付,即不保证可靠交付,因此主机不需要维持复杂的连接状态表(这里面有很多参数)。
UDP没有拥塞控制,因此网络出现的拥塞不会使源主机的发送速率降低。但是不使用拥塞控制功能的UDP有可能会引起网络产生严重的拥塞问题。
UDP支持一对一、一对多、多对一和多对多的交互通信。
UDP是面向报文的。发送方的UDP对应用程序交下来的报文,在添加首部后就向下交付IP层。UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。这就是说,应用层交给UDP多长 的报文,UDP就照样发送,即一次发送一个报文。
应用场景
第一,需要资源少,在网络情况比较好的内网,或者对于丢包不敏感的应用
第二,不需要一对一沟通,建立连接,而是可以广播的应用。
第三,需要处理速度快,时延低,可以容忍少数丢包,但是要求即便网络拥塞,也毫不退缩,一往无前的时候。
UDP如何实现可靠性传输?
UDP它不属于连接型协议,因而具有资源消耗小,处理速度快的优点,所以通常音频、视频和普通数据在传送时使用UDP较多,因为它们即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。
传输层无法保证数据的可靠传输,只能通过应用层来实现了。实现的方式可以参照tcp可靠性传输的方式,只是实现不在传输层,实现转移到了应用层。
实现确认机制、重传机制、窗口确认机制。
如果你不利用Linux协议栈以及上层socket机制,自己通过抓包和发包的方式去实现可靠性传输,那么必须实现如下功能:
发送:包的分片、包确认、包的重发
接收:包的调序、包的序号确认
目前有如下开源程序利用udp实现了可靠的数据传输。分别为RUDP、RTP、UDT。
RUDP
RUDP 提供一组数据服务质量增强机制,如拥塞控制的改进、重发机制及淡化服务器算法等,从而在包丢失和网络拥塞的情况下, RTP 客户机(实时位置)面前呈现的就是一个高质量的 RTP 流。在不干扰协议的实时特性的同时,可靠 UDP 的拥塞控制机制允许 TCP 方式下的流控制行为。
RTP
实时传输协议(RTP)为数据提供了具有实时特征的端对端传送服务,如在组播或单播网络服务下的交互式视频音频或模拟数据。应用程序通常在 UDP 上运行 RTP 以便使用其多路结点和校验服务;这两种协议都提供了传输层协议的功能。但是 RTP 可以与其它适合的底层网络或传输协议一起使用。如果底层网络提供组播方式,那么 RTP 可以使用该组播表传输数据到多个目的地。
RTP 本身并没有提供按时发送机制或其它服务质量(QoS)保证,它依赖于底层服务去实现这一过程。 RTP 并不保证传送或防止无序传送,也不确定底层网络的可靠性。 RTP 实行有序传送, RTP 中的序列号允许接收方重组发送方的包序列,同时序列号也能用于决定适当的包位置,例如:在视频解码中,就不需要顺序解码。
UDT
基于UDP的数据传输协议(UDP-basedData Transfer Protocol,简称UDT)是一种互联网数据传输协议。UDT的主要目的是支持高速广域网上的海量数据传输,而互联网上的标准数据传输协议TCP在高带宽长距离网络上性能很差。顾名思义,UDT建于UDP之上,并引入新的拥塞控制和数据可靠性控制机制。UDT是面向连接的双向的应用层协议。它同时支持可靠的数据流传输和部分可靠的数据报传输。由于UDT完全在UDP上实现,它也可以应用在除了高速数据传输之外的其它应用领域,例如点到点技术(P2P),防火墙穿透,多媒体数据传输等等。
TCP中如何判断异步的connect是否连接成功
1.TCP socket未调用connect函数之前处于可读可写状态
2.调用connect函数后socket由于协议栈发包,变为不可写状态,当协议栈发包结束,变成可写状态
3.监听可写状态,判断socket上的错误状态,若无错误发生,则连接成功
TCP三次握手
联系网络编程中的网络编程的一般步骤。
服务器端通过创建 socket,bind,listen 完成初始化,通过 accept 完成连接的建立。
客户端通过创建 socket,connect 发起连接建立请求。
具体过程
- 客户端的协议栈向服务器端发送了 SYN 包,并告诉服务器端当前发送序列号 j,客户端进入 SYN_SENT 状态;
- 服务器端的协议栈收到这个包之后,和客户端进行 ACK 应答,应答的值为 j+1,表示对 SYN 包 j 的确认,同时服务器也发送一个 SYN 包,告诉客户端当前我的发送序列号为 k,服务器端进入 SYNC_RCVD 状态;
- 客户端协议栈收到 ACK 之后,使得应用程序从 connect 调用返回,表示客户端到服务器端的单向连接建立成功,客户端的状态为 ESTABLISHED,同时客户端协议栈也会对服务器端的 SYN 包进行应答,应答数据为 k+1;
- 应答包到达服务器端后,服务器端协议栈使得 accept 阻塞调用返回,这个时候服务器端到客户端的单向连接也建立成功,服务器端也进入 ESTABLISHED 状态。
三次握手确认两件事: 1.建立连接; 2. TCP包的序列号。
keepalive机制: 发送探活包,即使没有真实数据包也不会断开连接。
第一次握手的seq序号是随机产生的吗
seq的全称为sequence number:表示的是发送方这边,这个packet的数据部分的第一位应该在整个data stream中所在的位置。
- seq的初始值在不同系统实现不一样,一般为随时间增长的值。当seq超过4字节存储空间后从0开始。
- 在某个方向上传输N个字节的数据,序列号就+N,因此seq用于确认在某个方向上传输的字节数。
TCP为什么需要三次握手
三次握手的目的是确认双方数据包发送和接收的能力。首先假设客户端发送一个SYN包给服务端,这是第一次握手。假如此时服务端收到了该报文,说明服务端知道客户端“发送消息”没有问题。此时,服务端发送SYN+ACK消息给客户端,这是第二次握手。假如此时客户端收到了该报文,说明客户端知道服务端“发送消息”和“接收消息”没有问题。但是此时服务端只知道客户端“发送消息”没有问题,不知道它“接收能力”怎么样。此时,客户端再次发送新的ACK给服务端,这是第三次握手。假如服务端收到了,说明客户端“发送”和“接收”都没有问题。此后,可以正常通信。
TCP如果是两次握手会出现什么情况?
如果仅两次连接可能出现一种情况:客户端发送完连接报文(第一次握手)后由于网络不好,延时很久后报文到达服务端,服务端接收到报文后向客户端发起连接(第二次握手)。此时客户端会认定此报文为失效报文,但在两次握手情况下服务端会认为已经建立起了连接,服务端会一直等待客户端发送数据,但因为客户端会认为服务端第二次握手的回复是对失效请求的回复,不会去处理。这就造成了服务端一直等待客户端数据的情况,浪费资源。
第 2 次握手传回了 ACK,为什么还要传回 SYN
ACK 是为了告诉客户端发来的数据已经接收无误,而传回 SYN 是为了告诉客户端,服务端收到的消息确实是客户端发送的消息。
TCP四次挥手
首先,一方应用程序调用 close,我们称该方为主动关闭方,该端的 TCP 发送一个 FIN 包,表示需要关闭连接。之后主动关闭方进入 FIN_WAIT_1 状态。
接着,接收到这个 FIN 包的对端执行被动关闭。这个 FIN 由 TCP 协议栈处理,我们知道,TCP 协议栈为 FIN 包插入一个文件结束符 EOF 到接收缓冲区中,应用程序可以通过 read 调用来感知这个 FIN 包。一定要注意,这个 EOF 会被放在已排队等候的其他已接收的数据之后,这就意味着接收端应用程序需要处理这种异常情况,因为 EOF 表示在该连接上再无额外数据到达。此时,被动关闭方进入 CLOSE_WAIT 状态。
接下来,被动关闭方将读到这个 EOF,于是,应用程序也调用 close 关闭它的套接字,这导致它的 TCP 也发送一个 FIN 包。这样,被动关闭方将进入 LAST_ACK 状态。
最终,主动关闭方接收到对方的 FIN 包,并确认这个 FIN 包。主动关闭方进入 TIME_WAIT 状态,而接收到 ACK 的被动关闭方则进入 CLOSED 状态。经过 2MSL 时间之后,主动关闭方也进入 CLOSED 状态。
MSL 是 Maximum Segment Lifetime,报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。因为 TCP 报文基于是 IP 协议的,而 IP 头中有一个 TTL 域,是 IP 数据报可以经过的最大路由数,每经过一个处理他的路由器此值就减 1,当此值为 0 则数据报将被丢弃,同时发送 ICMP 报文通知源主机。
RFC793 中规定 MSL 的时间为 2 分钟,Linux 实际设置为 30 秒。
TCP 为什么挥手是四次而不是三次
TCP 是全双工的,它允许两个方向的数据传输被独立关闭。当主动发起关闭的一方关闭连接之后,TCP 进入半关闭状态,此时主动方可以只关闭输出流。
之所以不是三次而是四次主要是因为被动关闭方将"对主动关闭报文的确认"和"关闭连接"两个操作分两次进行。
对主动关闭报文的确认是为了快速告知主动关闭方,此关闭连接报文已经收到。此时被动方不立即关闭连接是为了将缓冲中剩下的数据从输出流发回主动关闭方(主动方接收到数据后同样要进行确认),因此要把"确认关闭"和"关闭连接"分两次进行。
CLOSE_WAIT的作用
在服务器收到客户端关闭连接的请求并告诉客户端自己已经成功收到了该请求之后,服务器进入了 CLOSE-WAIT 状态,然而此时有可能服务端还有一些数据没有传输完成,因此不能立即关闭连接,而 CLOSE-WAIT 状态就是为了保证服务器在关闭连接之前将待发送的数据发送完成。
半关闭状态
TIME_WAIT
TIME_WAIT 的作用
(1) 为实现TCP全双工连接的可靠释放,确保最后的 ACK 能让被动关闭方接收,从而帮助其正常关闭。由TCP状态变迁图可知,假设发起主动关闭的一方(client)最后发送的ACK在网络中丢失,由于TCP协议的重传机制,执行被动关闭的一方(server)将会重发其FIN,在该FIN到达client之前,client必须维护这条连接状态,也就说这条TCP连接所对应的资源(client方的local_ip,local_port)不能被立即释放或重新分配,直到另一方重发的FIN达到之后,client重发ACK后,经过2MSL时间周期没有再收到另一方的FIN之后,该TCP连接才能恢复初始的CLOSED状态。如果主动关闭一方不维护这样一个TIME_WAIT状态,那么当被动关闭一方重发的FIN到达时,主动关闭一方的TCP传输层会用RST包响应对方,这会被对方认为是有错误发生,然而这事实上只是正常的关闭连接过程,并非异常。
(2)为使旧的数据包在网络因过期而消失。为说明这个问题,我们先假设TCP协议中不存在TIME_WAIT状态的限制,再假设当前有一条TCP连接:(local_ip, local_port, remote_ip,remote_port),因某些原因,我们先关闭,接着很快以相同的四元组建立一条新连接。本文前面介绍过,TCP连接由四元组唯一标识,因此,在我们假设的情况中,TCP协议栈是无法区分前后两条TCP连接的不同的,在它看来,这根本就是同一条连接,中间先释放再建立的过程对其来说是“感知”不到的。这样就可能发生这样的情况:前一条TCP连接由local peer发送的数据到达remote peer后,会被该remot peer的TCP传输层当做当前TCP连接的正常数据接收并向上传递至应用层(而事实上,在我们假设的场景下,这些旧数据到达remote peer前,旧连接已断开且一条由相同四元组构成的新TCP连接已建立,因此,这些旧数据是不应该被向上传递至应用层的),从而引起数据错乱进而导致各种无法预知的诡异现象。作为一种可靠的传输协议,TCP必须在协议层面考虑并避免这种情况的发生,这正是TIME_WAIT状态存在的第2个原因。
原因1.实现TCP全双工连接的可靠释放,主动关闭的一方不去维护这个time_wait状态,那么当被关闭一方重发的FIN到达时,主动关闭一方的TCP传输层会用RST包响应对方,这会被对方认为是有错误发生,然而这些事实上只是正常的关闭连接的过程,并非异常。
2. 为了使旧的数据包在网络中因过期而消失:导致引起数据错乱,而引起无法预知的诡异现象。
简洁的说
- 确保最后一个确认报文段能够到达。如果 B 没收到 A 发送来的确认报文段,那么就会重新发送连接释放请求报文段,A 等待一段时间就是为了处理这种情况的发生。
- 可能存在“已失效的连接请求报文段”,为了防止这种报文段出现在本次连接之外,需要等待一段时间。防止串话。
1)这个ACK报文段有可能丢失,使得处于LAST-ACK状态的B收不到对已发送的FIN+ACK报文段的确认,B超时重传FIN+ACK报文段,而A能在2MSL时间内收到这个重传的FIN+ACK报文段,接着A重传一次确认,重新启动2MSL计时器,最后A和B都进入到CLOSED状态,若A在TIME-WAIT状态不等待一段时间,而是发送完ACK报文段后立即释放连接,则无法收到B重传的FIN+ACK报文段,所以不会再发送一次确认报文段,则B无法正常进入到CLOSED状态。
2)A在发送完最后一个ACK报文段后,再经过2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失,使下一个新的连接中不会出现这种旧的连接请求报文段。
TIME_WAIT 的危害
第一是内存资源占用,这个目前看来不是太严重,基本可以忽略。
第二是对端口资源的占用,一个 TCP 连接至少消耗一个本地端口。要知道,端口资源也是有限的,一般可以开启的端口为 32768~61000 ,也可以通过net.ipv4.ip_local_port_range指定,如果 TIME_WAIT 状态过多,会导致无法创建新连接。这个也是我们在一开始讲到的那个例子。
TIME-WAIT 为什么是 2MSL
当客户端发出最后的 ACK 确认报文时,并不能确定服务器端能够收到该段报文。所以客户端在发送完 ACK 确认报文之后,会设置一个时长为 2 MSL 的计时器。MSL(Maximum Segment Lifetime),指一段 TCP 报文在传输过程中的最大生命周期。2 MSL 即是服务器端发出 FIN 报文和客户端发出的 ACK 确认报文所能保持有效的最大时长。
若服务器在 1 MSL 内没有收到客户端发出的 ACK 确认报文,再次向客户端发出 FIN 报文。如果客户端在 2 MSL 内收到了服务器再次发来的 FIN 报文,说明服务器由于一些原因并没有收到客户端发出的 ACK 确认报文。客户端将再次向服务器发出 ACK 确认报文,并重新开始 2 MSL 的计时。
若客户端在 2MSL 内没有再次收到服务器发送的 FIN 报文,则说明服务器正常接收到客户端 ACK 确认报文,客户端可以进入 CLOSE 阶段,即完成四次挥手。
所以客户端要经历 2 MSL 时长的 TIME-WAIT 阶段,为的是确认服务器能否接收到客户端发出的 ACK 确认报文。
为什么连接的时候是三次握手,关闭的时候却是四次握手?
因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,“你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。
TCP状态转换图
TCP 是如何保证可靠性的
数据分块:应用数据被分割成 TCP 认为最适合发送的数据块。
序列号和确认应答:TCP 给发送的每一个包进行编号,在传输的过程中,每次接收方收到数据后,都会对发送方进行确认应答,即发送 ACK 报文,这个 ACK 报文当中带有对应的确认序列号,告诉发送方成功接收了哪些数据以及下一次的数据从哪里开始发。除此之外,接收方可以根据序列号对数据包进行排序,把有序数据传送给应用层,并丢弃重复的数据。
校验和: TCP 将保持它首部和数据部分的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的变化。如果收到报文段的检验和有差错,TCP 将丢弃这个报文段并且不确认收到此报文段。
流量控制: TCP 连接的双方都有一个固定大小的缓冲空间,发送方发送的数据量不能超过接收端缓冲区的大小。当接收方来不及处理发送方的数据,会提示发送方降低发送的速率,防止产生丢包。TCP 通过滑动窗口协议来支持流量控制机制。
拥塞控制: 当网络某个节点发生拥塞时,减少数据的发送。
ARQ协议: 也是为了实现可靠传输的,它的基本原理就是每发完一个分组就停止发送,等待对方确认。在收到确认后再发下一个分组。
超时重传: 当 TCP 发出一个报文段后,它启动一个定时器,等待接收端确认收到这个报文段。如果超过某个时间还没有收到确认,将重发这个报文段。
TCP 超时重传的原理
发送方在发送一次数据后就开启一个定时器,在一定时间内如果没有得到发送数据包的 ACK 报文,那么就重新发送数据,在达到一定次数还没有成功的话就放弃重传并发送一个复位信号。其中超时时间的计算是超时的核心,而定时时间的确定往往需要进行适当的权衡,因为当定时时间过长会造成网络利用率不高,定时太短会造成多次重传,使得网络阻塞。在 TCP 连接过程中,会参考当前的网络状况从而找到一个合适的超时时间。
TCP 的停止等待协议(ARQ)是什么
停止等待协议是为了实现 TCP 可靠传输而提出的一种相对简单的协议,该协议指的是发送方每发完一组数据后,直到收到接收方的确认信号才继续发送下一组数据。我们通过四种情形来帮助理解停等协议是如何实现可靠传输的:
- 无差错传输
- 出现差错。如上述右图所示,发送方发送的报文出现差错导致接收方不能正确接收数据,出现差错的情况主要分为两种:(1)发送方发送的 Msg 1 在中途丢失了,接收方完全没收到数据。(2)接收方收到 Msg 1 后检测出现了差错,直接丢弃 Msg 1。
- 确认丢失。当接收方回应的 Msg 1 确认报文在传输过程中丢失,发送方无法接收到确认报文。于是发送方等待一段时间后重传 Msg 1,接收方将收到重复的 Msg1 数据包,此时接收方会丢弃掉这个重复报文并向发送方再次发送 Msg1 的确认报文。
- 确认迟到 。当接收方回应的 Msg 1 确认报文由于网络各种原因导致发送方没有及时收到,此时发送方在超时重传机制的作用下再次发送了 Msg 数据包,接收方此时进行和确认丢失情形下相同的动作(丢弃重复的数据包并再次发送 Msg 1 确认报文)。发送方此时收到了接收方的确认数据包,于是继续进行数据发送。过了一段时间后,发送方收到了迟到的 Msg 1 确认包会直接丢弃。
TCP滑动窗口
TCP的滑动窗口主要有两个作用,一是提供TCP的可靠性,二是提供TCP的流控特性。同时滑动窗口机制还体现了TCP面向字节流的设计思路。
滑动窗口是衡量TCP缓冲区发送接收消息的能力。在Wiresahrk抓捕结果会看到Win字段,一般的意思就是告诉对方字节还能接收这么多字节。
滑动窗大小也是衡量双方接收和处理的能力。如果对方一直没能力处理,那么滑动窗口可能就是0。此时会引入其他机制(这里好像叫持续探测器,会一直给对方发探测请求,请求知道现在窗口大小)。
TCP流量控制(如果接收方滑动窗口满了,发送方会怎么做)
TCP的滑动窗口是衡量TCP缓冲区发送接收消息的能力,主要有两个作用,一是提供TCP的可靠性,二是提供TCP的流控特性。
流量控制是为了控制发送方发送速率,保证接收方来得及接收。
基于 TCP 流量控制中的滑动窗口协议,我们知道接收方返回给发送方的 ACK 包中会包含自己的接收窗口大小,若接收窗口已满,此时接收方返回给发送方的接收窗口大小为 0,则发送方不能发送数据,此时发送方会等待接收方发送的窗口大小直到变为非 0 为止。然而,接收方回应的 ACK 包是存在丢失的可能的,为了防止双方一直等待而出现死锁情况,此时就需要坚持计时器来辅助发送方周期性地向接收方查询,以便发现窗口是否变大【坚持计时器参考问题】, 当发现窗口大小变为非零时,发送方便继续发送数据。
TCP阻塞控制
参考地址
拥塞控制就是防止过多的数据注入网络中,这样可以使网络中的路由器或链路不致过载。
阻塞控制的四种方法:慢开始、阻塞避免、快重传、快恢复。
TCP 拥塞控制采用的四种算法
慢开始
当发送方开始发送数据时,由于一开始不知道网络负荷情况,如果立即将大量的数据字节传输到网络中,那么就有可能引起网络拥塞。一个较好的方法是在一开始发送少量的数据先探测一下网络状况,即由小到大的增大发送窗口(拥塞窗口 cwnd)。慢开始的慢指的是初始时令 cwnd为 1,即一开始发送一个报文段。如果收到确认,则 cwnd = 2,之后每收到一个确认报文,就令 cwnd = cwnd* 2。
但是,为了防止拥塞窗口增长过大而引起网络拥塞,另外设置了一个慢开始门限 ssthresh。
① 当 cwnd < ssthresh 时,使用上述的慢开始算法;
② 当 cwnd > ssthresh 时,停止使用慢开始,转而使用拥塞避免算法;
③ 当 cwnd == ssthresh 时,两者均可。
拥塞避免
拥塞控制是为了让拥塞窗口 cwnd 缓慢地增大,即每经过一个往返时间 RTT (往返时间定义为发送方发送数据到收到确认报文所经历的时间)就把发送方的 cwnd 值加 1,通过让 cwnd 线性增长,防止很快就遇到网络拥塞状态。
当网络拥塞发生时,让新的慢开始门限值变为发生拥塞时候的值的一半,并将拥塞窗口置为 1 ,然后再次重复两种算法(慢开始和拥塞避免),这时一瞬间会将网络中的数据量大量降低。
快重传
快重传算法要求接收方每收到一个失序的报文就立即发送重复确认,发送方只要连续收到三个重复确认就立即重传(尽早 重传未被确认的报文段)。由于发送方尽早重传未被确认的报文段,因此,快重传算法可以提高网络的吞吐量。
快恢复
当发送方连续收到了三个重复确认,就乘法减半(慢开始门限减半),将当前的cwnd设置为慢开始门限,并且采用拥塞避免算法(连续收到了三个重复请求,说明当前网络可能没有拥塞)。
采用慢开始和拥塞避免算法的时候
- 一旦cwnd>慢开始门限,就采用拥塞避免算法,减慢增长速度
- 一旦出现丢包的情况,就重新进行慢开始,减慢增长速度
采用快恢复和快重传算法的时候
- 一旦cwnd>慢开始门限,就采用拥塞避免算法,减慢增长速度
- 一旦发送方连续收到了三个重复确认,就采用拥塞避免算法,减慢增长速度
拥塞控制算法
TCP 拥塞控制算法发展的过程中出现了如下几种不同的思路:
基于丢包的拥塞控制:将丢包视为出现拥塞,采取缓慢探测的方式,逐渐增大拥塞窗口,当出现丢包时,将拥塞窗口减小,如 Reno、Cubic 等。
基于时延的拥塞控制:将时延增加视为出现拥塞,延时增加时增大拥塞窗口,延时减小时减小拥塞窗口,如 Vegas、FastTCP 等。
基于链路容量的拥塞控制:实时测量网络带宽和时延,认为网络上报文总量大于带宽时延乘积时出现了拥塞,如 BBR。
基于学习的拥塞控制:没有特定的拥塞信号,而是借助评价函数,基于训练数据,使用机器学习的方法形成一个控制策略,如 Remy。
拥塞控制和流量控制的区别
拥塞控制往往是一种全局的,防止过多的数据注入到网络之中,而TCP连接的端点只要不能收到对方的确认信息,猜想在网络中发生了拥塞,但并不知道发生在何处,因此,流量控制往往指点对点通信量的控制,是端到端的问题。
高并发服务器客户端主动关闭连接和服务端主动关闭连接的区别
以下是针对 TCP 服务来说的:
服务端主动关闭连接
在高并发场景下,当服务端主动关闭连接时,此时服务器上就会有大量的连接处于 TIME-WAIT 状态【详解见问题 7, 8, 9】
客户端主动关闭连接
当客户端主动关闭连接时,我们并不需要关心 TIME-WAIT 状态过多造成的问题,但是需要关注服务端保持大量的 CLOSE-WAIT 状态时会产生的问题【见问题 10 的解决方法】
无论是客户端还是服务器主动关闭连接,从本质上来说,在高并发场景下主要关心的就是服务端的资源占用问题,而这也是采用 TCP 传输协议必须要面对的问题,其问题解决的出发点也是如何处理好服务质量和资源消耗之间的关系。
心跳包的作用
- 如果对方异常断开,本机检测不到,一直等待,浪费资源
- 需要设置tcp的保持连接,作用就是每隔一定的时间间隔发送探测分节,如果连续发送多个探测分节对方还未回,就将次连接断开
TCP 粘包问题
为什么会发生TCP粘包和拆包?
- 发送方写入的数据大于套接字缓冲区的大小,此时将发生拆包。
- 发送方写入的数据小于套接字缓冲区大小,由于 TCP 默认使用 Nagle 算法,只有当收到一个确认后,才将分组发送给对端,当发送方收集了多个较小的分组,就会一起发送给对端,这将会发生粘包。
- 进行 MSS (最大报文长度)大小的 TCP 分段,当 TCP 报文的数据部分大于 MSS 的时候将发生拆包。
- 发送方发送的数据太快,接收方处理数据的速度赶不上发送端的速度,将发生粘包。
- 由于TCP协议在发送较小的数据包的时候,会将几个包合成一个包后发送。
常见解决方法
- 发送端给每个数据包添加包首部,首部中应该至少包含数据包的长度,这样接收端在接收到数据后,通过读取包首部的长度字段,便知道每一个数据包的实际长度了。
- 发送端将每个数据包封装为固定长度(不够的可以通过补0填充),这样接收端每次从接收缓冲区中读取固定长度的数据就自然而然的把每个数据包拆分开来。
- 可以在数据包之间设置边界,如添加特殊符号,这样,接收端通过这个边界就可以将不同的数据包拆分开。
什么时候需要处理粘包问题?
当接收端同时收到多个分组,并且这些分组之间毫无关系时,需要处理粘包;而当多个分组属于同一数据的不同部分时,并不需要处理粘包问题。
SYN FLOOD 是什么
SYN Flood 是种典型的 DoS(拒绝服务)攻击,其目的是通过消耗服务器所有可用资源使服务器无法用于处理合法请求。通过重复发送初始连接请求(SYN)数据包,攻击者能够压倒目标服务器上的所有可用端口,导致目标设备根本不响应合法请求。
为什么服务端易受到 SYN 攻击
在 TCP 建立连接的过程中,因为服务端不确定自己发给客户端的 SYN-ACK 消息或客户端反馈的 ACK 消息是否会丢在半路,所以会给每个待完成的半开连接状态设一个定时器,如果超过时间还没有收到客户端的 ACK 消息,则重新发送一次 SYN-ACK 消息给客户端,直到重试超过一定次数时才会放弃。
服务端为了维持半开连接状态,需要分配内核资源维护半开连接。当攻击者伪造海量的虚假 IP 向服务端发送 SYN 包时,就形成了 SYN FLOOD 攻击。攻击者故意不响应 ACK 消息,导致服务端被大量注定不能完成的半开连接占据,直到资源耗尽,停止响应正常的连接请求。
服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,所以服务器容易受到SYN洪泛攻击,SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server则回复确认包,并等待Client确认,由于源地址不存在,因此Server需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪。
解决方法:
- 降低主机的等待时间使主机尽快的释放半连接的占用,短时间受到某IP的重复SYN则丢弃后续请求;
- 部署能够辨别恶意 IP 的路由器,将伪造 IP 地址的发送方发送的 SYN 消息过滤掉,该方案作用一般不是太大;
上述两种方法虽然在一定程度上能够提高服务器的防御能力,但是没有从根本上解决服务器资源消耗殆尽的问题,而以下几种方法的出发点都是在发送方发送确认回复后才开始分配传输资源,从而避免服务器资源消耗殆尽。
SYN Cache:该方法首先构造一个全局 Hash Table,用来缓存系统当前所有的半开连接信息。在 Hash Table 中的每个桶的容量大小是有限制的,当桶满时,会主动丢掉早来的信息。当服务端收到一个 SYN 消息后,会通过一个映射函数生成一个相应的 Key 值,使得当前半连接信息存入相应的桶中。当收到客户端正确的确认报文后,服务端才开始分配传输资源块,并将相应的半开连接信息从表中删除。和服务器传输资源相比,维护表的开销要小得多。
SYN Cookies:该方案原理和 HTTP Cookies 技术类似,服务端通过特定的算法将半开连接信息编码成序列号或者时间戳,用作服务端给客户端的消息编号,随 SYN-ACK 消息一同返回给连接发起方,这样在连接建立完成前服务端不保存任何信息,直到发送方发送 ACK 确认报文并且服务端成功验证编码信息后,服务端才开始分配传输资源。若请求方是攻击者,则不会向服务端会 ACK 消息,由于未成功建立连接,因此服务端并没有花费任何额外的开销。
然而该方案也存在一些缺点,由于服务端并不保存半开连接状态,因此也就丧失了超时重传的能力,这在一定程度上降低了正常用户的连接成功率。此外,客户端发送给服务端的确认报文存在传输丢失的可能,当 ACK 确认报文丢失时,服务端和客户端会对连接的成功与否产生歧义,此时就需要上层应用采取相应的策略进行处理了。
SYN Proxy:在客户端和服务器之间部署一个代理服务器,类似于防火墙的作用。通过代理服务器与客户端进行建立连接的过程,之后代理服务器充当客户端将成功建立连接的客户端信息发送给服务器。这种方法基本不消耗服务器的资源,但是建立连接的时间变长了(总共需要 6 次握手)。
在浏览器中输入url地址->>显示主页的过程
- DNS解析。浏览器从地址栏的输入中获得服务器的 IP 地址和端口号;
- TCP连接。浏览器用 TCP 的三次握手与服务器建立连接;
- 发送HTTP请求。浏览器向服务器发送拼好的报文;
- 处理请求并返回。服务器收到报文后处理请求,同样拼好报文再发给浏览器;
- 浏览器渲染。浏览器解析报文,渲染输出页面。
- 断开连接。客户端和服务器通过四次挥手终止TCP连接。
搜索baidu,会用到计算机网络中的什么层?每层是干什么的
浏览器中输入URL
浏览器要将URL解析为IP地址,解析域名就要用到DNS协议,首先主机会查询DNS的缓存,如果没有就给本地DNS发送查询请求。DNS查询分为两种方式,一种是递归查询,一种是迭代查询。如果是迭代查询,本地的DNS服务器,向根域名服务器发送查询请求,根域名服务器告知该域名的一级域名服务器,然后本地服务器给该一级域名服务器发送查询请求,然后依次类推直到查询到该域名的IP地址。DNS服务器是基于UDP的,因此会用到UDP协议。
得到IP地址后,浏览器就要与服务器建立一个http连接。因此要用到http协议,http协议报文格式上面已经提到。http生成一个get请求报文,将该报文传给TCP层处理,所以还会用到TCP协议。如果采用https还会使用https协议先对http数据进行加密。TCP层如果有需要先将HTTP数据包分片,分片依据路径MTU和MSS。TCP的数据包然后会发送给IP层,用到IP协议。IP层通过路由选路,一跳一跳发送到目的地址。当然在一个网段内的寻址是通过以太网协议实现(也可以是其他物理层协议,比如PPP,SLIP),以太网协议需要直到目的IP地址的物理地址,有需要ARP协议。
你能试着解释一下在浏览器里点击页面链接后发生了哪些事情吗?
浏览器判断是不是ip地址,不是就进行域名解析,依次通过浏览器缓存,系统缓存,host文件,还是没找到的请求DNS服务器获取IP解析(解析失败的浏览器尝试换别的DNS服务器,最终失败的进入错误页面),有可能获取到CDN服务器IP地址,访问CDN时先看是否缓存了,缓存了响应用户,无法缓存,缓存失效或者无缓存,回源到服务器。经过防火墙外网网管路由到nginx接入层。ng缓存中存在的直接放回,不存在的负载到web服务器。web服务器接受到请后处理,路径不存在404。存在的返回结果(服务器中也会有redis,ehcache(堆内外缓存),disk等缓存策略)。原路返回,CDN加入缓存响应用户。
Web 页面请求过程(URL 请求过程)
浏览器中输入 URL,首先浏览器要将 URL 解析为 IP 地址,解析域名就要用到 DNS 协议,首先主机会查询 DNS 的缓存,如果没有就给本地 DNS 发送查询请求。DNS 查询分为两种方式,一种是递归查询,一种是迭代查询。如果是迭代查询,本地的 DNS 服务器,向根域名服务器发送查询请求,根域名服务器告知该域名的一级域名服务器,然后本地服务器给该一级域名服务器发送查询请求,然后依次类推直到查询到该域名的 IP 地址。DNS 服务器是基于 UDP 的,因此会用到 UDP 协议。
得到 IP 地址后,浏览器就要与服务器建立一个 http 连接。因此要用到 http 协议,http 协议报文格式上面已经ᨀ到。http 生成一个 get 请求报文,将该报文传给 TCP 层处理。如果采用 https 还会先对 http 数据进行加密。TCP 层如果有需要先将 HTTP 数据包分片,分片依据路径 MTU 和MSS。TCP 的数据包然后会发送给 IP 层,用到 IP 协议。IP 层通过路由选路,一跳一跳发送到目的地址。当然在一个网段内的寻址是通过以太网协议实现(也可以是其他物理层协议,比如PPP,SLIP),以太网协议需要直到目的 IP 地址的物理地址,有需要 ARP 协议。
什么是HTTP
HTTP(HyperText Transfer Protoco)
HTTP 是一个用在计算机世界里的协议, 专门用来在两点之间传输数据,不能用于广播、寻址或路由。 HTTP 传输的是文字、图片、音频、视频等超文本数据。
HTTP报文
HTTP 协议的请求报文和响应报文的结构基本相同,由三大部分组成:
- 请求行有三部分:请求方法,请求目标和版本号;
- 头部字段集合(header):使用 key-value 形式更详细地说明报文;例如,Content-Type 是指正文的格式。我们进行 POST 的请求,如果正文是 JSON,那么我们就应该将这个值设置为 JSON。
- 消息正文(entity):实际传输的数据,它不一定是纯文本,可以是图片、视频等二进制数据。
这其中前两部分起始行和头部字段经常又合称为**“请求头部”或“响应头部”,消息正文又称为“实体”**,但与“header”对应,很多时候就直接称为“body”。
HTTP 协议规定报文必须有 header,但可以没有 body,而且在 header 之后必须要有一个“空行”,也就是“CRLF”,十六进制的“0D0A”。
HTTP 的报文大概分为三大部分。第一部分是请求行,第二部分是请求的首部,第三部分才是请求的正文实体。
HTTP响应报文
- 状态行也有三部分:版本号,状态码和原因字符串;
- 头部字段是 key-value 的形式,用“:”分隔,不区分大小写,顺序任意,除了规定的标准头,也可以任意添加自定义字段,实现功能扩展;
HTTP的状态码
- 状态码在响应报文里表示了服务器对请求的处理结果; 状态码后的原因短语是简单的文字描述,可以自定义; 状态码是十进制的三位数,分为五类,从 100 到 599;
- 1××:信息性状态码,表示接收的请求正在处理。
- 2××:成功状态码,表示请求已被成功处理完毕。常用的有 200、204、206;
- 3××:重定向状态码,要完成请求需要进行附加操作。资源位置发生变动,需要客户端重新发送请求,常用的有 301、302、304;
- 4××:客户端错误状态码,请求有语法错误或者请求无法实现,服务器无法处理。常用的有 400、403、404;
- 5××:服务器错误状态码,服务器处理请出现错误。常用的有 500、501、502、503。
403状态码可能出现的原因:客户端证书已过期或尚未有效;来自同一客户端IP的请求太多;达到动态IP限制限制;服务器繁忙,同一IP地址发送请求过多,遭到服务器智能屏蔽;连接的用户过多,可以过后再试;DNS解析错误,手动更改DNS服务器地址;你的IP被列入黑名单
HTTP请求方法
GET/HEAD
GET是请求从服务器获取资源。这个资源既可以是静态的文本、页面、图片、视频,也可以是由 PHP、Java 动态生成的页面或者其他格式的数据。
HEAD 方法与 GET 方法类似,也是请求从服务器获取资源,但服务器不会返回请求的实体数据,只会传回响应头,也就是资源的“元信息”。
比如,想要检查一个文件是否存在,只要发个 HEAD 请求就可以了,没有必要用 GET 把整个文件都取下来。再比如,要检查文件是否有最新版本,同样也应该用 HEAD,服务器会在响应头里把文件的修改时间传回来。
POST/PUT
GET 和 HEAD 方法是从服务器获取数据,而 POST 和 PUT 方法则是相反操作,向指定的资源提交数据,数据就放在报文的 body 里。
PUT 的作用与 POST 类似,通常 POST 表示的是“新建”“create”的含义,而 PUT 则是“修改”“update”的含义。
最常用的请求方法是 GET 和 POST,分别是获取数据和发送数据;
增:POST 删:DELETE 改:PUT 查:GET
DELETE、CONNECT 、OPTIONS 、TRACE
DELETE 方法指示服务器删除指定的资源。
CONNECT 将服务器作为代理,让服务器代替用户进行访问。
OPTIONS 向服务器发送该方法,会返回对指定资源所支持的HTTP请求。
TRACE 回显服务器收到的请求数据,即服务器返回自己收到的数据,主要用于测试和诊断。方法多用于对 HTTP 链路的测试或诊断,可以显示出请求 - 响应的传输路径。它的本意是好的,但存在漏洞,会泄漏网站的信息,所以 Web 服务器通常也是禁止使用。
GET和POST的区别
- get 提交的数据会放在 URL 之后,并且请求参数会被完整的保留在浏览器的记录里,由于参数直接暴露在 URL 中,可能会存在安全问题,因此往往用于获取资源信息。而 post 参数放在请求主体中,并且参数不会被保留,相比 get 方法,post 方法更安全,主要用于修改服务器上的资源。
get 请求只支持 URL 编码,post 请求支持多种编码格式。
get 只支持 ASCII 字符格式的参数,而 post 方法没有限制。
get 提交的数据大小有限制(这里所说的限制是针对浏览器而言的),而 post 方法提交的数据没限制
get 方式需要使用 Request.QueryString 来取得变量的值,而 post 方式通过 Request.Form 来获取。
get 方法产生一个 TCP 数据包,post 方法产生两个(并不是所有的浏览器中都产生两个)。
安全与幂等
在 HTTP 协议里,所谓的**“安全”是指请求方法不会“破坏”服务器上的资源,即不会对服务器上的资源造成实质的修改**。
按照这个定义,只有 GET 和 HEAD 方法是“安全”的,因为它们是“只读”操作,只要服务器不故意曲解请求方法的处理方式,无论 GET 和 HEAD 操作多少次,服务器上的数据都是“安全的”。而 POST/PUT/DELETE 操作会修改服务器上的资源,增加或删除数据,所以是“不安全”的。
所谓的“幂等”实际上是一个数学用语,被借用到了 HTTP 协议里,意思是多次执行相同的操作,结果也都是相同的,即多次“幂”后结果“相等”。很显然,GET 和 HEAD 既是安全的也是幂等的,DELETE 可以多次删除同一个资源,效果都是“资源不存在”,所以也是幂等的。
按照 RFC 里的语义,POST 是“新增或提交数据”,多次提交数据会创建多个资源,所以不是幂等的;而 PUT 是“替换或更新数据”,多次更新一个资源,资源还是会第一次更新的状态,所以是幂等的。
HTTP的流程图
HTTP有哪些特点
- HTTP 是可靠传输协议,基于 TCP/IP 协议“尽量”保证数据的送达;
- HTTP 是应用层协议,比 FTP、SSH 等更通用功能更多,能够传输任意数据;
- HTTP 使用了请求 - 应答模式,客户端主动发起请求,服务器被动回复请求;
- HTTP 本质上是无状态的,每个请求都是互相独立、毫无关联的,协议不要求客户端或服务器记录请求相关的信息。
- HTTP 是灵活可扩展的,可以任意添加头字段实现任意功能;
HTTP大文件传输的方法
- 压缩 HTML 等文本文件是传输大文件最基本的方法;
- 分块传输可以流式收发数据,节约内存和带宽,使用响应头字段“Transfer-Encoding: chunked”来表示,分块的格式是 16 进制长度头 + 数据块;
- 范围请求可以只获取部分数据,即“分块请求”,实现视频拖拽或者断点续传,使用请求头字段“Range”和响应头字段“Content-Range”,响应状态码必须是206;
- 也可以一次请求多个范围,这时候响应报文的数据类型是“multipart/byteranges”,body 里的多个部分会用boundary 字符串分隔。
HTTP的优缺点
- HTTP 是无状态的,可以轻松实现集群化,扩展性能,但有时也需要用 Cookie 技术来实现“有状态”;
- HTTP 是明文传输,容易被窃听,是不安全的,无法验证通信双方的身份,也不能判断报文是否被窜改;
HTTP 的性能不算差,但不完全适应现在的互联网,还有很大的提升空间。
HTTP 最大的优点是简单、灵活和易于扩展;
HTTP 拥有成熟的软硬件环境,应用的非常广泛,是互联网的基础设施;
HTTP的连接管理
短连接
它底层的数据传输基于 TCP/IP,每次发送请求前需要先与服务器建立连接,收到响应报文后会立即关闭连接。因为客户端与服务器的整个连接过程很短暂,不会与服务器保持长时间的连接状态,所以就被称为“短连接”(short-lived connections)。早期的 HTTP 协议也被称为是“无连接”的协议。
短连接的缺点: 在 TCP 协议里,建立连接和关闭连接都是非常“昂贵”的操作,需要消耗资源和时间。TCP 建立连接要有“三次握手”,发送 3 个数据包,需要 1 个 RTT(往返延时);关闭连接是“四次挥手”,4 个数据包需要 2 个 RTT。即建立连接时浪费太多的时间。
长连接
长连接用的就是“成本均摊”的思路,既然 TCP 的连接和关闭非常耗时间,那么就把这个时间成本由原来的一个“请求 - 应答”均摊到多个“请求 - 应答”上。
不过不管客户端是否显式要求长连接,如果服务器支持长连接,它总会在响应报文里放一个“Connection: keep-alive”字段。
HTTP/1.1 中增加了持久连接的方法,它的特点是在一个 TCP 连接上可以传输多个 HTTP 请求,只要浏览器或者服务器没有明确断开连接,那么该 TCP 连接会一直保持。
Keep-Alive缺点:当长时间的保持TCP连接时容易导致系统资源被无效占用。
解决办法: 使用“keepalive_timeout”指令,设置长连接的超时时间,如果在一段时间内连接上没有任何数据收发就主动断开连接,避免空闲连接占用系统资源。
- 使用“keepalive_requests”指令,设置长连接上可发送的最大请求次数。比如设置成 1000,那么当 Nginx 在这个连接上处理了 1000 个请求后,也会主动断开连接。
队头阻塞
“队头阻塞”与短连接和长连接无关,而是由 HTTP 基本的“请求 - 应答”模型所导致的。因为 HTTP 规定报文必须是“一发一收”,这就形成了一个先进先出的“串行”队列。队列里的请求没有轻重缓急的优先级,只有入队的先后顺序,排在最前面的请求被最优先处理。
在HTTP1.1中的解决办法
解决方案
这在 HTTP 里就是“并发连接”(concurrent connections),也就是同时对一个域名发起多个长连接,用数量来解决质量的问题。 缺陷: 如果每个客户端都想自己快,建立很多个连接,用户数×并发数就会是个天文数字。服务器的资源根本就扛不住,或者被服务器认为是恶意攻击,反而会造成“拒绝服务”。
“域名分片”(domain sharding)技术,还是用数量来解决质量的思路。HTTP 协议和浏览器不是限制并发连接数量吗?好,那我就多开几个域名,比如 shard1.chrono.com、shard2.chrono.com,而这些域名都指向同一台服务器 www.chrono.com,这样实际长连接的数量就又上去了,真是“美滋滋”。不过实在是有点“上有政策,下有对策”的味道。
HTTP2
HTTP2不使用管道化的方式,而是引入了帧、消息和数据流等概念,每个请求/响应被称为消息,每个消息都被拆分成若干个帧进行传输,每个帧都分配一个序号。每个帧在传输是属于一个数据流,而一个连接上可以存在多个流,各个帧在流和连接上独立传输,到达之后在组装成消息,这样就避免了请求/响应阻塞。
HTTP 长连接短连接使用场景是什么
长连接:多用于操作频繁,点对点的通讯,而且客户端连接数目较少的情况。例如即时通讯、网络游戏等。
短连接:用户数目较多的Web网站的 HTTP 服务一般用短连接。例如京东,淘宝这样的大型网站一般客户端数量达到千万级甚至上亿,若采用长连接势必会使得服务端大量的资源被无效占用,所以一般使用的是短连接。
总结
- 早期的 HTTP 协议使用短连接,收到响应后就立即关闭连接,效率很低;
- HTTP/1.1 默认启用长连接,在一个连接上收发多个请求响应,提高了传输效率;
- 服务器会发送“Connection: keep-alive”字段表示启用了长连接;
- 报文头里如果有“Connection: close”就意味着长连接即将关闭;
- 过多的长连接会占用服务器资源,所以服务器会用一些策略有选择地关闭长连接;
- “队头阻塞”问题会导致性能下降,可以用“并发连接”和“域名分片”技术缓解。
HTTP传输大文件的方法
- 压缩 HTML 等文本文件是传输大文件最基本的方法;
- 分块传输可以流式收发数据,节约内存和带宽,使用响应头字段“Transfer-Encoding: chunked”来表示,分块的格式是 16
进制长度头 + 数据块; - 范围请求可以只获取部分数据,即“分块请求”,实现视频拖拽或者断点续传,使用请求头字段“Range”和响应头字段“Content-Range”,响应状态码必须是 206;
- 也可以一次请求多个范围,这时候响应报文的数据类型是“multipart/byteranges”,body 里的多个部分会用
boundary 字符串分隔。
HTTP 是不保存状态的协议,如何保存用户状态
假如某个特定的客户机在短时间内两次请求同一个对象,服务器并不会因为刚刚为该用户提供了该对象就不再做出反应,而是重新发送该对象,就像该服务器已经完全忘记不久之前所做过的是一样。因为一个 HTTP 服务器并不保存关于客户机的任何信息,所以我们说 HTTP 是一个无状态协议。
通常有两种解决方案:
(1) 基于 Session 实现的会话保持
在客户端第一次向服务器发送 HTTP 请求后,服务器会创建一个 Session 对象并将客户端的身份信息以键值对的形式存储下来,然后分配一个会话标识(SessionId)给客户端,这个会话标识一般保存在客户端 Cookie 中,之后每次该浏览器发送 HTTP 请求都会带上 Cookie 中的 SessionId 到服务器,服务器根据会话标识就可以将之前的状态信息与会话联系起来,从而实现会话保持。
优点:安全性高,因为状态信息保存在服务器端。
缺点:由于大型网站往往采用的是分布式服务器,浏览器发送的 HTTP 请求一般要先通过负载均衡器才能到达具体的后台服务器,倘若同一个浏览器两次 HTTP 请求分别落在不同的服务器上时,基于 Session 的方法就不能实现会话保持了。
【解决方法:采用中间件,例如 Redis,我们通过将 Session 的信息存储在 Redis 中,使得每个服务器都可以访问到之前的状态信息】
(2) 基于 Cookie 实现的会话保持
当服务器发送响应消息时,在 HTTP 响应头中设置 Set-Cookie 字段,用来存储客户端的状态信息。客户端解析出 HTTP 响应头中的字段信息,并根据其生命周期创建不同的 Cookie,这样一来每次浏览器发送 HTTP 请求的时候都会带上 Cookie 字段,从而实现状态保持。
优点:服务器不用保存状态信息, 减轻服务器存储压力,同时便于服务端做水平拓展。
缺点:该方式不够安全,因为状态信息存储在客户端,这意味着不能在会话中保存机密数据。除此之外,浏览器每次发起 HTTP 请求时都需要发送额外的 Cookie 到服务器端,会占用更多带宽。
拓展:Cookie被禁用了怎么办?
若遇到 Cookie 被禁用的情况,则可以通过重写 URL 的方式将会话标识放在 URL 的参数里,也可以实现会话保持。
Session 和 Cookie 区别
基于 Cookie 的会话保持与基于 Session 实现的会话保持最主要的区别是前者完全将会话状态信息存储在浏览器 Cookie 中。
Session 是服务器用来跟踪用户的一种手段,每个 Session 都有一个唯一标识:Session ID。当服务器创建了一个 Session 时,给客户端发送的响应报文就包含了 Set-Cookie 字段,其中有一个名为 sid 的键值对,这个键值对就是 Session ID。客户端收到后就把 Cookie 保存在浏览器中,并且之后发送的请求报文都包含 Session ID。HTTP 就是 Session 和 Cookie 这两种方式一起合作来实现跟踪用户状态的,而 Session 用于服务器端,Cookie 用于客户端。
HTTP1.1 和 1.0 的区别
长连接:HTTP/1.0 默认浏览器和服务器之间保持短暂连接,浏览器的每次请求都需要与服务器建立一个 TCP 连接,服务器完成后立即断开 TCP 连接。HTTP/1.1 默认使用的是持久连接,其支持在同一个 TCP 请求中传送多个 HTTP 请求和响应。此之前的 HTTP 版本的默认连接都是使用非持久连接,如果想要在旧版本的 HTTP 协议上维持持久连接,则需要指定 Connection 的首部字段的值为 Keep-Alive。
Host 请求头:早期 HTTP/1.0 中认为每台服务器都绑定一个唯一的 IP 地址并提供单一的服务,请求消息中的 URL 并没有传递主机名。而随着虚拟主机的出现,一台物理服务器上可以存在多个虚拟主机,并且它们共享同一个 IP 地址。为了支持虚拟主机,HTTP/1.1 中添加了 host 请求头,请求消息和响应消息中应声明这个字段,若请求消息中缺少该字段时服务端会响应一个 404 错误状态码。
节约带宽: 当客户端请求某个资源时,HTTP/1.0 默认将该资源相关的整个对象传送给请求方,但很多时候可能客户端并不需要对象的所有信息。而在 HTTP/1.1 的请求头中引入了 range 头域,它允许只请求部分资源,其使得开发者可以多线程请求某一资源,从而充分的利用带宽资源,实现高效并发。
缓存处理:在 HTTP/1.0 中主要使用 header 里的 if-modified-Since, Expries 来做缓存判断的标准。而 HTTP/1.1 请求头中添加了更多与缓存相关的字段,从而支持更为灵活的缓存策略,例如 Entity-tag, If-Unmodified-Since, If-Match, If-None-Match 等可供选择的缓存头来控制缓存策略。
错误通知的管理:HTTP/1.1 在 1.0 的基础上新增了 24 个错误状态响应码,例如 414 表示客户端请求中所包含的 URL 地址太长,以至于服务器无法处理;410 表示所请求的资源已经被永久删除。
HTTP和HTTPS的区别
- HTTP 协议以明文方式发送内容,数据都是未加密的,安全性较差。HTTPS 数据传输过程是加密的,安全性较好。
- HTTP 和 HTTPS 使用的是完全不同的连接方式,用的端口也不一样,前者是 80 端口,后者是 443 端口。
- HTTPS 协议需要到数字认证机构(Certificate Authority, CA)申请证书,一般需要一定的费用。
- HTTP 页面响应比 HTTPS 快,主要因为 HTTP 使用 3 次握手建立连接,客户端和服务器需要握手 3 次,而 HTTPS 除了 TCP 的 3 次握手,还需要经历一个 SSL 协商过程。
HTTP2.0 的特点
a、HTTP/2 采用二进制格式而非文本格式。
b、HTTP/2 是支持多路复用的。因为流 ID 的存在, 通过同一个 HTTP 请求可以实现多个 HTTP 请求传输,客户端和服务器可以通过流 ID 来标识究竟是哪个流从而定位到是哪个 HTTP 请求。
c、使用头部压缩,HTTP/2 降低了开销。
d、HTTP/2 支持服务器推送。让服务器可以将响应主动“推送”到客户端缓存中。
HTTPS是怎么实现安全加密的
“对称加密”很好理解,就是指加密和解密时使用的密钥都是同一个,是“对称”的。只要保证了密钥的安全,那整个通信过程就可以说具有了机密性。
非对称加密(也叫公钥加密算法)。它有两个密钥,一个叫“公钥”(public key),一个叫“私钥”(private key)。两个密钥是不同的,“不对称”,公钥可以公开给任何人使用,而私钥必须严格保密。
HTTPS 的加密方式
HTTPS 采用对称加密和非对称加密相结合的方式,首先使用 SSL/TLS 协议进行加密传输,为了弥补非对称加密的缺点,HTTPS 采用证书来进一步加强非对称加密的安全性,通过非对称加密,客户端和服务端协商好之后进行通信传输的对称密钥,后续的所有信息都通过该对称秘钥进行加密解密,完成整个 HTTPS 的流程。
非对称加密为什么慢
- 非对称加密基于大数运算,比如大素数或者椭圆曲线,是复杂的数学难题,所以消耗计算量,运算速度慢。
- 除了慢,可能还有一个缺点就是需要更多的位数,相同强度的对称密钥要比非对称密钥短。对称密钥一般都128位、256位,而rsa一般要2048位,不过椭圆曲线的会短一点。
对称加密和非对称的区别,非对称加密有哪些
加密和解密的过程不同:对称加密和解密过程使用同一个密钥;非对称加密中加密和解密采用公钥和私钥两个密钥,一般使用公钥进行加密,使用私钥进行解密。
加密和解密的速度不同:对称加密和解密速度较快,当数据量比较大时适合使用;非对称加密和解密时间较长,速度相对较慢,适合少量数据传输的场景。
传输的安全性不同:采用对称加密方式进行通信时,收发双方在数据传送前需要协定好密钥,而这个密钥还有可能被第三方窃听到的,一旦密钥泄漏,之后的通信就完全暴漏给攻击者了;非对称加密采用公钥加密和私钥解密的方式,其中私钥是基于不同的算法生成的随机数,公钥可以通过私钥通过一定的算法推导得出,并且私钥到公钥的推导过程是不可逆的,也就是说公钥无法反推导出私钥,即使攻击者窃听到传输的公钥,也无法正确解出数据,所以安全性较高。
常见的非对称加密算法主要有:RSA、Elgamal、背包算法、Rabin、D-H 算法等等。
数字签名与证书
摘要算法
实现完整性的手段主要是摘要算法(Digest Algorithm),也就是常说的散列函数、哈希函数(Hash Function)。你可以把摘要算法近似地理解成一种特殊的压缩算法,它能够把任意长度的数据“压缩”成固定长度、而且独一无二的“摘要”字符串,就好像是给这段数据生成了一个数字“指纹”。
数字签名
数字签名的原理其实很简单,就是把公钥私钥的用法反过来,之前是公钥加密、私钥解密,现在是私钥加密、公钥解密。
数字证书和CA
数字证书是数字证书在一个身份和该身份的持有者所拥有的公/私钥对之间建立了一种联系,由认证中心(CA)或者认证中心的下级认证中心颁发的。根证书是认证中心与用户建立信任关系的基础。在用户使用数字证书之前必须首先下载和安装。
公钥的分发需要使用数字证书,必须由 CA 的信任链来验证,否则就是不可信的;
CA 对公钥的签名认证也是有格式的,不是简单地把公钥绑定在持有者身份上就完事了,还要包含序列号、用途、颁发者、有效时间等等,把这些打成一个包再签名,完整地证明公钥关联的各种信息,形成“数字证书”(Certificate)。
这还是信任链的问题。小一点的 CA 可以让大 CA 签名认证,但链条的最后,也就是 Root CA,就只能自己证明自己了,这个就叫“自签名证书”(Self-Signed Certificate)或者“根证书”(Root Certificate)。
↓ 对称加密(有密钥交换的问题)
↓ 非对称加密(基于复杂的数学难题,运行速度很慢)
↓ 混合加密(怎么保证完整性?不被修改?)
↓ 摘要算法(无法保证是用户自己)
↓ 数字签名(公钥怎么保证安全正确的?)
↓ 数字证书、CA
TLS1.2连接过程
ECDHE 握手过程
RSA 握手过程
SSL握手
SSL握手有三个目的
- 客户端与服务器需要就一组用于保护数据的算法达成一致;
- 它们需要确立一组由那些算法所使用的加密密钥;
- 握手还可以选择对客户端进行认证。
TLS 连接建立原理
Step 1: Client Hello
客户端产生的随机数 A;
客户端支持的加密方法列表。
Step 2: Server Hello.
服务端产生的随机数 B,用作产生密钥;
服务端根据客户端的支持情况确定出的加密方法组合(Cipher Suite)。
Step 3: Certificate, Server Key Exchange, Server Hello Done.
Certificate,证书信息,证书包含了服务端生成的公钥。这个公钥有什么用呢?别急,后面会说到。客户端收到消息后,验证确认证书真实有效,那么这个证书里面的公钥也就是可信的了。
Server Hello Done 表示这一握手阶段的完成。
客户端对服务器的证书进行验证(有关验证证书,可以参考数字签名),并抽取服务器的公用密钥;然后,再产生一个称作pre_master_secret的随机密码串,并使用服务器的公用密钥对其进行加密(参考非对称加/解密),并将加密后的信息发送给服务器;
Step 4: Client Key Exchange, Change Cipher Spec, Encrypted Handshake Message.
Client Key Exchange RSA(ECDHE)公钥加密pre_master
** Change Cipher Spe** 之后改用会话密钥加密
** Encrypted Handshake Message**
Step 5: Change Cipher Spec, Encrypted Handshake Message.
Change Cipher Spec 服务端也同意改用会话密钥加密
Step6:Finished:
客户端将所有握手消息发送给服务器;
服务器将所有握手消息发送给客户端
HTTPS的优化
HTTPS慢的原因
HTTPS 比 HTTP 增加了一个 TLS 握手的步骤,这个步骤最长可以花费两个消息往返,也就是 2-RTT。
产生用于密钥交换的临时公私钥对(ECDHE);
验证证书时访问 CA 获取 CRL 或者 OCSP;
非对称加密解密处理“Pre-Master”。
可以有多种硬件和软件手段减少网络耗时和计算耗时,让 HTTPS 变得和 HTTP 一样快,最可行的是软件优化;
首先,你可以选择更快的 CPU,最好还内建 AES 优化,这样即可以加速握手,也可以加速传输。
其次,你可以选择“SSL 加速卡”,加解密时调用它的 API,让专门的硬件来做非对称加解密,分担 CPU 的计算压力。不过“SSL 加速卡”也有一些缺点,比如升级慢、支持算法有限,不能灵活定制解决方案等。
所以,就出现了第三种硬件加速方式:“SSL 加速服务器”,用专门的服务器集群来彻底“卸载”TLS 握手时的加密解密计算,性能自然要比单纯的“加速卡”要强大的多。
应当尽量采用 TLS1.3,它大幅度简化了握手的过程,完全握手只要 1-RTT,而且更加安全。
应当尽量使用 ECDHE 椭圆曲线密码套件,节约带宽和计算量,还能实现“False Start”;
服务器端应当开启“OCSP Stapling”(在线证书状态协议装订)功能,避免客户端访问 CA 去验证证书;
会话复用分两种,第一种叫“Session ID”,就是客户端和服务器首次连接后各自保存一个会话的 ID 号,内存里存储主密钥和其他相关的信息。当客户端再次连接时发一个 ID 过来,服务器就在内存里找,找到就直接用主密钥恢复会话状态,跳过证书验证和密钥交换,只用一个消息往返就可以建立安全通信。
会话复用的效果类似 Cache,前提是客户端必须之前成功建立连接,后面就可以用“Session ID”“Session Ticket”等凭据跳过密钥交换、证书验证等步骤,直接开始加密通信。
“Session Ticket”方案。
它有点类似 HTTP 的 Cookie,存储的责任由服务器转移到了客户端,服务器加密会话信息,用“New Session Ticket”消息发给客户端,让客户端保存。重连的时候,客户端使用扩展“session_ticket”发送“Ticket”而不是“Session ID”,服务器解密后验证有效期,就可以恢复会话,开始加密通信。
什么是P2P协议以及其优点
常用的下载文件协议为HTTP或FTP底层都是使用传统的服务器-客户端模式(底层基于TCP协议)。其面临着一个比较大的缺点就是服务器带宽压力。
P2P 就是 peer-to-peer。资源开始并不集中地存储在某些设备上,而是分散地存储在多台设备上。这些设备我们姑且称为 peer。
要下载一个文件的时候,你只要得到那些已经存在了文件的 peer,并和这些 peer 之间,建立点对点的连接,而不需要到中心服务器上,就可以就近下载文件。一旦下载了文件,你也就成为 peer 中的一员,你旁边的那些机器,也可能会选择从你这里下载文件,所以当你使用 P2P 软件的时候,例如 BitTorrent,往往能够看到,既有下载流量,也有上传的流量,也即你自己也加入了这个 P2P 的网络,自己从别人那里下载,同时也提供给其他人下载。可以想象,这种方式,参与的人越多,下载速度越快,一切完美。
怎么知道哪些 peer 有这个文件呢?
这就用到种子,也即咱们比较熟悉的.torrent 文件。.torrent 文件由两部分组成,分别是:announce(tracker URL)和文件信息。
info 区:这里指定的是该种子有几个文件、文件有多长、目录结构,以及目录和文件的名字。
Name 字段:指定顶层目录名字。每个段的大小:BitTorrent(简称 BT)协议把一个文件分成很多个小段,然后分段下载。
段哈希值:将整个种子中,每个段的 SHA-1 哈希值拼在一起,为了下载完成这一块文件进行验证是否正确。
下载过程:下载时,BT 客户端首先解析.torrent 文件,得到 tracker 地址,然后连接 tracker 服务器。tracker 服务器回应下载者的请求,将其他下载者(包括发布者)的 IP 提供给下载者。下载者再连接其他下载者,根据.torrent 文件,两者分别对方告知自己已经有的块,然后交换对方没有的数据。此时不需要其他服务器参与,并分散了单个线路上的数据流量,因此减轻了服务器的负担。下载者每得到一个块,需要算出下载块的 Hash 验证码,并与.torrent 文件中的对比。如果一样,则说明块正确,不一样则需要重新下载这个块。这种规定是为了解决下载内容的准确性问题。
什么是去中心化网络DHT?
从P2P的下载过程也可以看出,这种方式特别依赖 tracker。tracker 需要收集下载者信息的服务器,并将此信息提供给其他下载者,使下载者们相互连接起来,传输数据。虽然下载的过程是非中心化的,但是加入这个 P2P 网络的时候,都需要借助 tracker 中心服务器,这个服务器是用来登记有哪些用户在请求哪些资源。所以,这种工作方式有一个弊端,一旦 tracker 服务器出现故障或者线路遭到屏蔽,BT 工具就无法正常工作了。
DHT(Distributed Hash Table)的去中心化网络。每个加入这个 DHT 网络的人,都要负责存储这个网络里的资源信息和其他成员的联系信息,相当于所有人一起构成了一个庞大的分布式存储数据库。比较有名的DHT协议叫Kademlia协议。
在DHT网络里,每一个DHT node都有一个ID,ID是一个长串(160bit,20字节)用于保存某些文件在哪些节点上。每个DHT node都有一个哈希值。在DHT算法规定,每一个文件计算出的Hash值相近的DHT node都有责任知道从哪里下载这个文件,即使自己没有保存该文件。
其中,每个DHT node都有两个端口,其中绑定一个TCP端口用于上传和下载文件。另一个UDP端口用于加入DHT网络。
如何维护DHT网络
DHT网络按照node的距离远近进行分层管理(也就是前缀二叉树)。比如某个节点ID为01010,如果另一个节点ID为01011,只有最后一位不同,则这样的节点归为“k-bucket 1”。同样,如果倒数第二位不同,这个ID则归属为“k-bucket2”。
随着加入的节点越来越多,每一层只能放置k个节点,这个是可以配置的。当然,每个节点的加入和退出都会动态的更新其他节点的每一层维护列表。
什么是DNS服务器
简单的说,DNS服务器的作用是将浏览器输入的www.baidu.com这样的网址解析成某一个IP地址。
DNS是一个树状结构。具体网址的DNS服务器,这种一般叫做权威DNS,因为他自己知道自己的IP地址,所以权威。当然了,一般DNS是基于UDP协议进行实现的。
- 根 DNS 服务器 :返回顶级域 DNS 服务器的 IP 地址
- 顶级域 DNS 服务器:返回权威 DNS 服务器的 IP 地址
- 权威 DNS 服务器 :返回相应主机的 IP 地址
DNS服务器的特点:高可用、高并发和分布式的。
DNS 是网络世界的地址簿,可以通过域名查地址,因为域名服务器是按照树状结构组织的,因而域名查找是使用递归的方法,并通过缓存的方式增强性能;
当进行区域传送(主域名服务器向辅助域名服务器传送变化的那部分数据)时会使用 TCP,因为数据同步传送的数据量比一个请求和应答的数据量要多,而 TCP 允许的报文长度更长,因此为了保证数据的正确性,会使用基于可靠连接的 TCP。
当客户端向 DNS 服务器查询域名 ( 域名解析) 的时候,一般返回的内容不会超过 UDP 报文的最大长度,即 512 字节。用 UDP 传输时,不需要经过 TCP 三次握手的过程,从而大大提高了响应速度,但这要求域名解析器和域名服务器都必须自己处理超时和重传从而保证可靠性。
DNS的解析流程
第一、浏览器输入www.163.com,点击回车,浏览器(浏览器是客户端)向本地DNS发送请求查询www.163.com的IP地址。
第二、本地DNS收到请求,会查询缓存列表,如果有就直接返回IP列表。如果没有,则直接请求根DNS。根DNS一看域名是.com,则然后.com的DNS地址,然后去**.com的顶级DNS**去查询。
第三、.com的顶级DNS收到请求,则指明权威服务器,也就是www.163.com的DNS服务器,由权威服务器给出IP地址。然后本地DNS更新IP地址,并返回给浏览器,浏览器用于建立TCP连接。
由此可以看出,DNS是一次递归的结果。
域名解析查询的两种方式
递归查询:如果主机所询问的本地域名服务器不知道被查询域名的 IP 地址,那么本地域名服务器就以 DNS 客户端的身份,向其他根域名服务器继续发出查询请求报文,即替主机继续查询,而不是让主机自己进行下一步查询。
迭代查询:当根域名服务器收到本地域名服务器发出的迭代查询请求报文时,要么给出所要查询的 IP 地址,要么告诉本地服务器下一步应该找哪个域名服务器进行查询,然后让本地服务器进行后续的查询,如上图步骤。
DNS有哪些作用或者应用
站在客户端角度,这是一次 DNS 递归查询过程。因为本地 DNS 全权为它效劳,它只要坐等结果即可。在这个过程中,DNS 除了可以通过名称映射为 IP 地址,它还可以做另外一件事,就是负载均衡。
内部负载均衡
例如,一个应用要访问数据库,在这个应用里面应该配置这个数据库的 IP 地址,还是应该配置这个数据库的域名呢?显然应该配置域名,因为一旦这个数据库,因为某种原因,换到了另外一台机器上,而如果有多个应用都配置了这台数据库的话,一换 IP 地址,就需要将这些应用全部修改一遍。但是如果配置了域名,则只要在 DNS 服务器里,将域名映射为新的 IP 地址,这个工作就完成了,大大简化了运维。
全局负载均衡
为了保证我们的应用高可用,往往会部署在多个机房,每个地方都会有自己的 IP 地址。当用户访问某个域名的时候,这个 IP 地址可以轮询访问多个数据中心。如果一个数据中心因为某种原因挂了,只要在 DNS 服务器里面,将这个数据中心对应的 IP 地址删除,就可以实现一定的高可用。
另外,我们肯定希望北京的用户访问北京的数据中心,上海的用户访问上海的数据中心,这样,客户体验就会非常好,访问速度就会超快。这就是全局负载均衡的概念。
在域名和 IP 的映射过程中,给了应用基于域名做负载均衡的机会,可以是简单的负载均衡,也可以根据地址和运营商做全局的负载均衡。
什么是负载均衡
其实底层算法是基于哈希算法实现的,一般采用哈希算法对客户端的IP地址或session会话ID进行哈希计算,然后与服务器列表进行取模,然后请求就被路由到具体的某一台服务器上了。
全局负载均衡器会失灵(故障)吗?应该怎么解决?
全局负载均衡器失灵一般会分情况来应对,主要分为以下三种情况。
第一,全局负载均衡器因为访问流量大,导致故障,此时只能通过分布式扩容来解决。
第二,全局负载均衡器故障导致的失灵,那么可能系统存在单点问题,解决方案就是增加备用机器,或者集群。就像Redis集群一样,每个节点还额外配置备用节点。
第三,负载均衡器因为网络故障失灵,就像几年前支付宝的电缆被挖掘机挖断,导致整体故障。此时解决方案就是增加更多的接入线路。这点有点像一些在线视频网站,可以选择线路一,线路二之类的进行观看,就是防止单一路线故障导致不可用。
RPC知道吗?一般怎么设计?
RPC 的全称是 Remote Procedure Call,即远程过程调用,依赖TCP等协议的网络层。当收到某一个请求,然后处理完返回结果给对方。这就完成了一次RPC调用。
RPC设计一般分为注册中心,网络层,有的还可以有序列化层、持久化层、安全验证层等。
当收到一个请求的时候,如果自己没有这个服务,可能会去注册列表看一下谁有这个功能,然后转发请求过去。对方处理完,返回响应,然后再转发给起始的请求对象。在设计RPC的时候,最重要的就是定义一套接口,还有注册中心的设计。(注:我个人感觉配置文件结构也很重要,因为这个决定了你RPC的框架架构)
RPC 的作用就是体现在这样两个方面
- 屏蔽远程调用跟本地调用的区别,让我们感觉就是调用项目内的方法;
- 隐藏底层网络通信的复杂性,让我们更专注于业务逻辑。
如果是一个不存在的域名,那么浏览器的工作流程会是怎么样的呢?
先查浏览器缓存,然后是系统缓存->hosts文件->局域网域名服务器->广域网域名服务器->顶级域名服务器->根域名服务器。这个时间通常要很久,最终找不到以后,返回一个报错页面,chrome是ERR_CONNECTION_ABORTED
CDN 在对待浏览器和爬虫时会有差异吗?为什么?
CDN在对待浏览器和爬虫时没有差异,因为如果没有验证码或者其他验证方式区分的话,浏览器和爬虫都被视为User Agent(客户代理)
IP地址和MAC
- IP 是地址,有定位功能;MAC 是身份证,无定位功能;
IP 协议的定义和作用
IP 协议(Internet Protocol)又称互联网协议,是支持网间互联的数据包协议。该协议工作在网络层,主要目的就是为了提高网络的可扩展性,和传输层 TCP 相比,IP 协议提供一种无连接/不可靠、尽力而为的数据包传输服务,其与TCP协议(传输控制协议)一起构成了TCP/IP 协议族的核心。IP 协议主要有以下几个作用:
寻址和路由:在IP 数据包中会携带源 IP 地址和目的 IP 地址来标识该数据包的源主机和目的主机。IP 数据报在传输过程中,每个中间节点(IP 网关、路由器)只根据网络地址进行转发,如果中间节点是路由器,则路由器会根据路由表选择合适的路径。IP 协议根据路由选择协议提供的路由信息对 IP 数据报进行转发,直至抵达目的主机。
分段与重组:IP 数据包在传输过程中可能会经过不同的网络,在不同的网络中数据包的最大长度限制是不同的,IP 协议通过给每个 IP 数据包分配一个标识符以及分段与组装的相关信息,使得数据包在不同的网络中能够传输,被分段后的 IP 数据报可以独立地在网络中进行转发,在到达目的主机后由目的主机完成重组工作,恢复出原来的 IP 数据包。
IPV4 地址不够如何解决
DHCP:动态主机配置协议。动态分配 IP 地址,只给接入网络的设备分配IP地址,因此同一个 MAC 地址的设备,每次接入互联网时,得到的IP地址不一定是相同的,该协议使得空闲的 IP 地址可以得到充分利用。
CIDR:无类别域间路由。CIDR 消除了传统的 A 类、B 类、C 类地址以及划分子网的概念,因而更加有效的分配 IPv4 的地址空间,但无法从根本上解决地址耗尽问题。
NAT:网络地址转换协议。我们知道属于不同局域网的主机可以使用相同的 IP 地址,从而一定程度上缓解了 IP 资源枯竭的问题。然而主机在局域网中使用的 IP 地址是不能在公网中使用的,当局域网主机想要与公网进行通信时, NAT 方法可以将该主机 IP 地址转换成全球 IP 地址。该协议能够有效解决 IP 地址不足的问题。
IPv6 :作为接替 IPv4 的下一代互联网协议,其可以实现 2 的 128 次方个地址,而这个数量级,即使是给地球上每一颗沙子都分配一个IP地址,该协议能够从根本上解决 IPv4 地址不够用的问题。
DHCP
DHCP(Dynamic Host Configuration Protocol),动态主机配置协议,是一个应用层协议。当我们将客户主机ip地址设置为动态获取方式时,DHCP服务器就会根据DHCP协议给客户端分配IP,使得客户机能够利用这个IP上网。
第一步:Client端在局域网内发起一个DHCP Discover包,目的是想发现能够给它提供IP的DHCP Server。
第二步:可用的DHCP Server接收到Discover包之后,通过发送DHCP Offer包给予Client端应答,意在告诉Client端它可以提供IP地址。
第三步:Client端接收到Offer包之后,发送DHCP Request包请求分配IP。
第四步:DHCP Server发送ACK数据包,确认信息。
路由器和交换机的区别
交换机:交换机用于局域网,利用主机的物理地址(MAC 地址)确定数据转发的目的地址,它工作与数据链路层。
路由器:路由器通过数据包中的目的 IP 地址识别不同的网络从而确定数据转发的目的地址,网络号是唯一的。路由器根据路由选择协议和路由表信息从而确定数据的转发路径,直到到达目的网络,它工作于网络层。
路由器的分组转发流程
① 从 IP 数据包中提取出目的主机的 IP 地址,找到其所在的网络;
② 判断目的 IP 地址所在的网络是否与本路由器直接相连,如果是,则不需要经过其它路由器直接交付,否则执行 ③;
③ 检查路由表中是否有目的 IP 地址的特定主机路由。如果有,则按照路由表传送到下一跳路由器中,否则执行 ④;
④ 逐条检查路由表,若找到匹配路由,则按照路由表转发到下一跳路由器中,否则执行步骤 ⑤;
⑤ 若路由表中设置有默认路由,则按照默认路由转发到默认路由器中,否则执行步骤 ⑥;
⑥ 无法找到合适路由,向源主机报错。
ICMP 协议概念/作用
ICMP(Internet Control Message Protocol)是因特网控制报文协议,主要是实现 IP 协议中未实现的部分功能,是一种网络层协议。该协议并不传输数据,只传输控制信息来辅助网络层通信。其主要的功能是验证网络是否畅通(确认接收方是否成功接收到 IP 数据包)以及辅助 IP 协议实现可靠传输(若发生 IP 丢包,ICMP 会通知发送方 IP 数据包被丢弃的原因,之后发送方会进行相应的处理)。
ICMP 的应用
Ping
Ping(Packet Internet Groper),即因特网包探测器,是一种工作在网络层的服务命令,主要用于测试网络连接量。本地主机通过向目的主机发送 ICMP Echo 请求报文,目的主机收到之后会发送 Echo 响应报文,Ping 会根据时间和成功响应的次数估算出数据包往返时间以及丢包率从而推断网络是否通常、运行是否正常等。
TraceRoute
TraceRoute 是 ICMP 的另一个应用,其主要用来跟踪一个分组从源点耗费最少 TTL 到达目的地的路径。TraceRoute 通过逐渐增大 TTL 值并重复发送数据报来实现其功能,首先,TraceRoute 会发送一个 TTL 为 1 的 IP 数据报到目的地,当路径上的第一个路由器收到这个数据报时,它将 TTL 的值减 1,此时 TTL = 0,所以路由器会将这个数据报丢掉,并返回一个差错报告报文,之后源主机会接着发送一个 TTL 为 2 的数据报,并重复此过程,直到数据报能够刚好到达目的主机。此时 TTL = 0,因此目的主机要向源主机发送 ICMP 终点不可达差错报告报文,之后源主机便知道了到达目的主机所经过的路由器 IP 地址以及到达每个路由器的往返时间。
ARP头部
ARP 协议
参考地址
ARP协议是“Address Resolution Protocol”(地址解析协议)的缩写。其作用是在以太网环境中,数据的传输所依懒的是MAC地址而非IP地址,而将已知IP地址转换为MAC地址的工作是由ARP协议来完成的。
应用接受用户递交的数据,触发 TCP 建立连接,TCP 的第一个 SYN 报文通过 connect 函数到达IP 层,IP 层通过查询路由表:
如果目的 IP 和自己在同一个网段:
当 IP 层的 ARP 高速缓存表中存在目的 IP 对应的 MAC 地址时,则调用网络接口 send 函数(参数为 IP Packet 和目的 MAC))将数据交给网络接口,网络接口完成 Ethernet Header + IP + CRC 的封装,并发送出去;
当 IP 层的 ARP 高速缓存表中不存在目的 IP 对应的 MAC 地址时,则 IP 层将 TCP 的 SYN缓存下来,发送 ARP 广播请求目的 IP 的 MAC,收到 ARP 应答之后,将应答之中的<IP 地址,对应的 MAC>对缓存在本地 ARP 高速缓存表中,然后完成 TCP SYN 的 IP 封装,调用网络接口send 函数(参数为 IP Packet 和目的 MAC))将数据交给网络接口,网络接口完成 Ethernet Header + IP + CRC 的封装,并发送出去;
如果目的 IP 地址和自己不在同一个网段,就需要将包发送给默认网关(网关是默认的数据出口),这需要知道默认网关的 MAC 地址:
当 IP 层的 ARP 高速缓存表中存在默认网关对应的 MAC 地址时,则调用网络接口 send 函数(参数为 IP Packet 和默认网关的 MAC)将数据ᨀ交给网络接口,网络接口完成 Ethernet Header + IP + CRC
当 IP 层的 ARP 高速缓存表中不存在默认网关对应的 MAC 地址时,则 IP 层将 TCP 的SYN 缓存下来,发送 ARP 广播请求默认网关的 MAC,收到 ARP 应答之后,将应答之中的<默认网关地址,对应的 MAC>对缓存在本地 ARP 高速缓存表中,然后完成 TCP SYN 的 IP 封装,调用网络接口 send 函数(参数为 IP Packet 和默认网关的 MAC)将数据ᨀ交给网络接口,网络接口完成 Ethernet Header + IP + RC 的封装,并发送出去。
为了通俗易懂的解释ARP协议的作用,这里就举一个简单的PING命令例子:
假设在一个局域网中,(注意这里的前提是两台主机在同一局域网中)我们的计算机IP地址是192.168.1.1,现在DOS窗口中执行这个命令:ping192.168.1.2。该命令会通过ICMP协议发送ICMP数据
包。该过程需要经过下面的步骤:
1、应用程序构造数据包,该步骤是产生ICMP包,然后把它提交给内核(网卡驱动程序);
2、内核检查是否能够转化该IP地址为MAC地址,也就是在本地的ARP缓存中查看IP-MAC对应表;
3、如果存在该IP-MAC对应关系,那么跳到步骤7;如果不存在该IP-MAC对应关系,那么接续下面的步骤;
4、内核进行ARP广播,即发送 ARP Request,向整个网络中大喊,这个IP是谁的,这个IP是谁的(这个ARP Request中包含有我们计算机的MAC地址;
5、当192.168.1.2主机接收到该ARP请求后,就发送一个ARP 回应,即ARP REPLY命令,说道:这个IP是我的,你看这是我的MAC地址(ARP Request中包含自己的MAC地址);
6、我们的计算机获得192.168.1.2主机的IP-MAC地址对应关系,就保存到自己的ARP缓存中;
7、内核将把目标主机IP转化为MAC地址,然后封装在以太网头结构中,再把数据发送出去;
8、这样主机B看到发送过来的数据包包头里有自己的MAC地址,才会识别它,噢,这个数据是发送给我的(当数据包包头里只有B的IP地址时,主机B会不认识它,不去接收这个数据包)
ARP 的位置
OSI 模型有七层,TCP 在第 4 层传输层,IP 在第 3 层网络层,而 ARP 在第 2 层数据链路层。高层对低层是有强依赖的,所以 TCP 的建立前要进行 ARP 的请求和应答。
ARP 高速缓存表在 IP 层使用。如果每次建立 TCP 连接都发送 ARP 请求,会降低效率,因此在主机、交换机、路由器上都会有 ARP 缓存表。建立 TCP 连接时先查询 ARP 缓存表,如果有效,直接读取 ARP 表项的内容进行第二层数据包的发送;只有表失效时才进行 ARP 请求和应答进行 MAC 地址的获取,以建立 TCP 连接。
ARP 地址解析协议的原理和地址解析过程
ARP(Address Resolution Protocol)是地址解析协议的缩写,该协议提供根据 IP 地址获取物理地址的功能,它工作在第二层,是一个数据链路层协议,其在本层和物理层进行联系,同时向上层提供服务。当通过以太网发送 IP 数据包时,需要先封装 32 位的 IP 地址和 48位 MAC 地址。在局域网中两台主机进行通信时需要依靠各自的物理地址进行标识,但由于发送方只知道目标 IP 地址,不知道其 MAC 地址,因此需要使用地址解析协议。
ARP 协议的解析过程
ARP 协议的解析过程如下:
① 首先,每个主机都会在自己的 ARP 缓冲区中建立一个 ARP 列表,以表示 IP 地址和 MAC 地址之间的对应关系;
② 当源主机要发送数据时,首先检查 ARP 列表中是否有 IP 地址对应的目的主机 MAC 地址,如果存在,则可以直接发送数据,否则就向同一子网的所有主机发送 ARP 数据包。该数据包包括的内容有源主机的 IP 地址和 MAC 地址,以及目的主机的 IP 地址。
③ 当本网络中的所有主机收到该 ARP 数据包时,首先检查数据包中的 目的 主机IP 地址是否是自己的 IP 地址,如果不是,则忽略该数据包,如果是,则首先从数据包中取出源主机的 IP 和 MAC 地址写入到 ARP 列表中,如果已经存在,则覆盖,然后将自己的 MAC 地址写入 ARP 响应包中,告诉源主机自己是它想要找的 MAC 地址。
④ 源主机收到 ARP 响应包后。将目的主机的 IP 和 MAC 地址写入 ARP 列表,并利用此信息发送数据。如果源主机一直没有收到 ARP 响应数据包,表示 ARP 查询失败。
TCP/IP数据链路层的交互过程
网络层等到数据链层用mac地址作为通信目标,数据包到达网络等准备往数据链层发送的时候,首先会去自己的arp缓存表(存着ip-mac对应关系)去查找改目标ip的mac地址,如果查到了,就讲目标ip的mac地址封装到链路层数据包的包头。如果缓存中没有找到,会发起一个广播:who is ip XXX tell ip XXX,所有收到的广播的机器看这个ip是不是自己的,如果是自己的,则以单拨的形式将自己的mac地址回复给请求的机器
两台电脑连起来后 ping 不通,你觉得可能存在哪些问题?
首先看网络是否连接正常,检查网卡驱动是否正确安装。
局域网设置问题,检查 IP 地址是否设置正确。
看是否被防火墙阻拦(有些设置中防火墙会对 ICMP 报文进行过滤),如果是的话,尝试关闭防火墙 。
看是否被第三方软件拦截。
两台设备间的网络延迟是否过大(例如路由设置不合理),导致 ICMP 报文无法在规定的时间内收到。
运输层协议和网络层协议的区别
网络层协议负责提供主机间的逻辑通信;运输层协议负责提供进程间的逻辑通信。
TTL 是什么?有什么作用
TTL 是指生存时间,简单来说,它表示了数据包在网络中的时间。每经过一个路由器后 TTL 就减一,这样 TTL 最终会减为 0 ,当 TTL 为 0 时,则将数据包丢弃。通过设置 TTL 可以避免这两个路由器之间形成环导致数据包在环路上死转的情况,由于有了 TTL ,当 TTL 为 0 时,数据包就会被抛弃。
ping
PING (Packet Internet Groper),因特网包探索器,用于测试网络连通性的程序。
工作原理:利用网络上机器IP地址的唯一性,给目标IP地址发送一个数据包(ICMP(Internet Control Messages Protocol,因特网信报控制协议)),再要求对方返回一个同样大小的数据包来确定两台网络机器是否连接相通,时延是多少。
ping的作用
1. ping localhost:
localhost的IP地址一般为127.0.0.1, 也称loopback(环回路由);如果此时ping不通,则表示协议栈有问题;ping 该地址不经过网卡,仅仅是软件层面
2. ping 本机IP:
ping 本机IP其实是从驱动到网卡,然后原路返回;所以如果此时ping不通,则表示网卡驱动有问题,或者NIC硬件有问题;
3. ping 网关:
所谓网关,就是连接到另外一个网络的“关卡”, 一般为离我们终端最近的路由器;可以使用ipconfig (windows)或ifconfig (Linux)查看;若此时ping不通,则为主机到路由器间的网络故障;
4. ping 目的IP:
若此步骤不成功,应该就是路由器到目的主机的网络有问题
MAC 地址和 IP 地址分别有什么作用
MAC 地址是数据链路层和物理层使用的地址,是写在网卡上的物理地址。MAC 地址用来定义网络设备的位置。
IP 地址是网络层和以上各层使用的地址,是一种逻辑地址。IP 地址用来区别网络上的计算机。
为什么有了 MAC 地址还需要 IP 地址
如果我们只使用 MAC 地址进行寻址的话,我们需要路由器记住每个 MAC 地址属于哪一个子网,不然每一次路由器收到数据包时都要满世界寻找目的 MAC 地址。而我们知道 MAC 地址的长度为 48 位,也就是说最多总共有 2 的 48 次方个 MAC 地址,这就意味着每个路由器需要 256 T 的内存,这显然是不现实的。
和 MAC 地址不同,IP 地址是和地域相关的,在一个子网中的设备,我们给其分配的 IP 地址前缀都是一样的,这样路由器就能根据 IP 地址的前缀知道这个设备属于哪个子网,剩下的寻址就交给子网内部实现,从而大大减少了路由器所需要的内存。
为什么有了 IP 地址还需要 MAC 地址
只有当设备连入网络时,才能根据他进入了哪个子网来为其分配 IP 地址,在设备还没有 IP 地址的时候或者在分配 IP 地址的过程中,我们需要 MAC 地址来区分不同的设备。
数据链路层上的三个基本问题
封装成帧:将网络层传下来的分组前后分别添加首部和尾部,这样就构成了帧。首部和尾部的一个重要作用是帧定界,也携带了一些必要的控制信息,对于每种数据链路层协议都规定了帧的数据部分的最大长度。
透明传输:帧使用首部和尾部进行定界,如果帧的数据部分含有和首部和尾部相同的内容, 那么帧的开始和结束的位置就会判断错,因此需要在数据部分中出现有歧义的内容前边插入转义字符,如果数据部分出现转义字符,则在该转义字符前再加一个转义字符。在接收端进行处理之后可以还原出原始数据。这个过程透明传输的内容是转义字符,用户察觉不到转义字符的存在。
差错检测:目前数据链路层广泛使用循环冗余检验(CRC)来检查数据传输过程中是否产生比特差错。
私网地址和公网地址之间进行转换
同一个局域网内的两个私网地址,经过转换之后外面看到的一样吗
当采用静态或者动态转换时,由于一个私网 IP 地址对应一个公网地址,因此经过转换之后的公网 IP 地址是不同的;而采用端口复用方式的话,在一个子网中的所有地址都采用一个公网地址,但是使用的端口是不同的。
物理层主要做什么事情
作为 OSI 参考模型最低的一层,物理层是整个开放系统的基础,该层利用传输介质为通信的两端建立、管理和释放物理连接,实现比特流的透明传输。物理层考虑的是怎样才能在连接各种计算机的传输媒体上传输数据比特流,其尽可能地屏蔽掉不同种类传输媒体和通信手段的差异,使物理层上面的数据链路层感觉不到这些差异,这样就可以使数据链路层只考虑完成本层的协议和服务,而不必考虑网络的具体传输媒体和通信手段是什么。
主机之间的通信方式
单工通信:也叫单向通信,发送方和接收方是固定的,消息只能单向传输。例如采集气象数据、家庭电费,网费等数据收集系统,或者打印机等应用主要采用单工通信。
半双工通信:也叫双向交替通信,通信双方都可以发送消息,但同一时刻同一信道只允许单方向发送数据。例如传统的对讲机使用的就是半双工通信。
全双工通信:也叫双向同时通信,全双工通信允许通信双方同时在两个方向是传输,其要求通信双方都具有独立的发送和接收数据的能力。例如平时我们打电话,自己说话的同时也能听到对面的声音。
通道复用技术
频分复用(FDM,Frequency Division Multiplexing)。频分复用将传输信道的总带宽按频率划分为若干个子频带或子信道,每个子信道传输一路信号。用户分到一定的频带后,在数据传输的过程中自始至终地占用这个频带。由于每个用户所分到的频带不同,使得传输信道在同一时刻能够支持不同用户进行数据传输,从而实现复用。除了传统意义上的 FDM 外,目前正交频分复用(OFDM)已在高速通信系统中得到广泛应用。
时分复用(TDM,Time Division Multiplexing)。顾名思义,时分复用将信道传输信息的时间划分为若干个时间片,每一个时分复用的用户在每一个 TDM 帧中占用固定时隙进行数据传输。用户所分配到的时隙是固定的,所以时分复用有时也叫做同步时分复用。这种分配方式能够便于调节控制,但是也存在缺点,当某个信道空闲时,其他繁忙的信道无法占用该空闲信道,因此会降低信道利用率。
波分复用(WDM,Wavelength Division Multiplexing)。在光通信领域通常按照波长而不是频率来命名,因为光的频率和波长具有单一对应关系,因此 WDM 本质上也是 FDM,光通信系统中,通常由光来运载信号进行传输,WDM 是在一条光纤上传输多个波长光信号,其将 1 根光纤看做多条「虚拟」光纤,每条「虚拟」光纤工作在不同的波长上,从而极大地提高了光纤的传输容量。
码分复用(CDM,Code Division Multiplexing)。码分复用是靠不同的编码来区分各路原始信号的一种复用方式,不同的用户使用相互正交的码字携带信息。由于码组相互正交,因此接收方能够有效区分不同的用户数据,从而实现每一个用户可以在同样的时间在同样的频带进行数据传输,频谱资源利用率高。其主要和各种多址接入技术相结合从而产生各种接入技术,包括无线和优先接入。
ARP 攻击
在 ARP 的解析过程中,局域网上的任何一台主机如果接收到一个 ARP 应答报文,并不会去检测这个报文的真实性,而是直接记入自己的 ARP 缓存表中。并且这个 ARP 表是可以被更改的,当表中的某一列长时间不适使用,就会被删除。ARP 攻击就是利用了这一点,攻击者疯狂发送 ARP 报文,其源 MAC 地址为攻击者的 MAC 地址,而源 IP 地址为被攻击者的 IP 地址。通过不断发送这些伪造的 ARP 报文,让网络内部的所有主机和网关的 ARP 表中被攻击者的 IP 地址所对应的 MAC 地址为攻击者的 MAC 地址。这样所有发送给被攻击者的信息都会发送到攻击者的主机上,从而产生 ARP 欺骗。通常可以把 ARP 欺骗分为以下几种:
洪泛攻击
攻击者恶意向局域网中的网关、路由器和交换机等发送大量 ARP 报文,设备的 CPU 忙于处理 ARP 协议,而导致难以响应正常的服务请求。其表现通常为:网络中断或者网速很慢。
欺骗主机
这种攻击方式也叫仿冒网关攻击。攻击者通过 ARP 欺骗使得网络内部被攻击主机发送给网关的信息实际上都发送给了攻击者,主机更新的 ARP 表中对应的 MAC 地址为攻击者的 MAC。当用户主机向网关发送重要信息使,该攻击方式使得用户的数据存在被窃取的风险。
欺骗网关
该攻击方式和欺骗主机的攻击方式类似,不过这种攻击的欺骗对象是局域网的网关,当局域网中的主机向网关发送数据时,网关会把数据发送给攻击者,这样攻击者就会源源不断地获得局域网中用户的信息。该攻击方式同样会造成用户数据外泄。
中间人攻击
攻击者同时欺骗网关和主机,局域网的网关和主机发送的数据最后都会到达攻击者这边。这样,网关和用户的数据就会泄露。
IP 地址冲突
攻击者对局域网中的主机进行扫描,然后根据物理主机的 MAC 地址进行攻击,导致局域网内的主机产生 IP 冲突,使得用户的网络无法正常使用。
路由器与交换机
根据 OSI模型的网络体系划分,自底向上,路由器 工作在第三层(网络层),而我们常说的交换机 工作在第二层(链路层)(目前有更加高级的三层交换机,四层交换机,甚至还有七层交换机)
路由器:寻址,转发(依靠 IP 地址)
交换机:过滤,转发(依靠 MAC 地址)
路由器内有一份路由表,里面有它的寻址信息(就像是一张地图),它收到网络层的数据报后,会根据路由表和选路算法将数据报转发到下一站(可能是路由器、交换机、目的主机)
交换机内有一张MAC表,里面存放着和它相连的所有设备的MAC地址,它会根据收到的数据帧的首部信息内的目的MAC地址在自己的表中查找,如果有就转发,如果没有就放弃。
每一个路由器与其之下连接的设备,其实构成一个局域网
交换机工作在路由器之下,就是也就是交换机工作在局域网内
交换机用于局域网内网的数据转发
路由器用于连接局域网和外网
如何设计server,使得能够接收多个客户端的请求
多线程,线程池,io复用