这里跟大家分享下我复习时整理的计算机网络常考知识点。
网络协议
OSI7层模型
- 物理层:传输物理比特
- 数据链路层:将比特封装成帧
- 网络层:将网络地址翻译成物理地址,路由选择。路由器。分组:数据报。IP协议
- 传输层:将大数据分割传给网络层,流量控制。TCP,UDP协议
- 会话层:建立应用程序的通讯
- 表示层:解决不同系统间通信语法问题
- 应用层:通过应用进程间的交互来完成特定网络应用,报文
TCP/IP4层模型(相比5层少一个物理层)
-
物理层:物理层(physical layer)的作用是实现相邻计算机节点之间比特流的透明传送,尽可能屏蔽掉具体传输介质和物理设备的差异。
-
链路层:两台主机之间的数据传输,总是在一段一段的链路上传送的,这就需要使用专门的链路层的协议。数据报组装成帧。
-
网络层:网络层的任务就是选择合适的网间路由和交换结点, 确保数据及时传送。数据报, IP
-
传输层:负责向两台主机进程之间的通信提供通用的数据传输服务,TCP/UDP
-
应用层:通过应用进程间的交互来完成特定网络应用,报文,HTTP,DNS,SMTP
三次握手
- tcp报文头:
- Sqquence Number:4字节,报文序号,若当前107带了100字节,则下一次序号207。
- Acknowledgment Number:4字节,期望收到下一个报文的下一个字节序好,B发送了201序号300字节的数据,期望收到A501的ack,故B的确认号写501。
- TCP Flages;
- URG:
- ACK:确认标识
- PSH:
- RST:重置连接标志
- SYN:同步序号,用于建立连接过程
- FIN:finish标识
+
- 为什么要三次握手才能建立连接:初始化Sequence Number的初始值
- SYN超时:Server收到Client的SYN,回复SYN-ACK时未收到ACK确认,Server不断重试,linux默认等63秒,可能会照成占用服务器资源。可使用SYN-cookie解决。
- 建立连接后,Client故障:保活机制,发送饱和报文,直至到达饱和次数
四次挥手
- 为什么要有TIME_WAIT
- 1、保证对方收到ACK包
- 2、避免新旧连接混淆
- 为什么需要四次挥手 :全双工,发送方与接收方都与要发送FIN与ACK
- 服务器出现大量CLOSE_WAIT:对方关闭socket连接,我方忙于读或写,没有及时关闭
- 检查代码,特别是资源释放的
- 检查配置,特别是处理请求的线程配置,如:线程池中的线程数字
为什么建立连接协议是三次握手,而关闭连接却是四次握手?
这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你未必会马上会关闭SOCKET,即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。
UDP简介
- 特点:1、面向非连接 2、不维护连接状态 3、抱头8字节很小 4、吞吐量只受数据生成,传输速率限制 5、不提供拆分报文
- TCP与UDP区别:
- 面向连接与无连接
- 可靠性
- 有序性
- 速度
- 量级
TCP 协议如何保证可靠传输
- 应用数据被分割成 TCP 认为最适合发送的数据块。
- TCP 给发送的每一个包进行编号,接收方对数据包进行排序,把有序数据传送给应用层。
- 校验和: TCP 将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP 将丢弃这个报文段和不确认收到此报文段。TCP 的接收端会丢弃重复的数据。
- 流量控制: TCP 连接的每一方都有固定大小的缓冲空间,TCP的接收端只允许发送端发送接收端缓冲区能接纳的数据。当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。TCP 使用的流量控制协议是可变大小的滑动窗口协议。 (TCP 利用滑动窗口实现流量控制)
- 拥塞控制: 当网络拥塞时,减少数据的发送。慢开始 、 拥塞避免 、快重传 和 快恢复。
- ARQ协议: 也是为了实现可靠传输的,它的基本原理就是每发完一个分组就停止发送,等待对方确认。在收到确认后再发下一个分组。
- 超时重传: 当 TCP 发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段
TCP的滑动窗口
- RTT和RTO
- RTT:发送数据包到收到ACK所花费的时间
- RTO:重传时间间隔
- 作用:提供可靠性与流控制性
拥塞控制
拥塞窗口:
发送方维持一个拥塞窗口 cwnd ( congestion window )的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。发送方让自己的发送窗口等于拥塞。
慢开始算法:
当主机开始发送数据时,如果立即所大量数据字节注入到网络,那么就有可能引起网络拥塞,因为现在并不清楚网络的负荷情况。因此,较好的方法是 先探测一下,即由小到大逐渐增大发送窗口,也就是说,由小到大逐渐增大拥塞窗口数值。
拥塞避免:
让拥塞窗口cwnd缓慢地增大,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是加倍。这样拥塞窗口cwnd按线性规律缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢得多。
快重传算法:
快重传算法首先要求接收方每收到一个失序的报文段就立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方),然后若是发送方接收到3个重复确认ACK,则启动快重传算法。
快速恢复算法:
与快重传配合使用的还有快恢复算法,其过程有以下两个要点:
- 当发送方连续收到三个重复确认,就执行“乘法减小”算法,把慢开始门限ssthresh减半。
- 与慢开始不同之处是现在不执行慢开始算法(即拥塞窗口cwnd现在不设置为1),而是把cwnd值设置为 慢开始门限ssthresh减半后的数值,然后开始执行拥塞避免算法(“加法增大”),使拥塞窗口缓慢地线性增大。
HTTP简介
- 特点:支持C/S模式、简单快速、灵活、短连接(1.1之前,一次处理一个请求,请求结束后关闭)长连接(1.1之后,打开网页后并不会立刻断,下一次访问还用这个)、无状态(协议对于事物处理没有记忆)、请求响应模型
- 请求:请求方法,url,协议版本,请求头部,请求数据
- 相应:协议版本,成功/错误代码,服务器信息,响应头部,响应数据
- 请求响应步骤:客户端连接到WEB服务器,发送HTTP请求、接受请求并返回HTTP相应、释放TCP连接,客户端解析HTML程序
- 键入URL,按下回车后经历的流程
- DNS解析、TCP连接、发送HTTP请求、处理请求并返回报文、浏览器渲染界面、连接结束
- TCP,IP,OPSF(IP数据包在路由器中传输),ARP(IP转MAC),HTTP
- HTTP状态码:
- 1xx:指示信息–请求已接受,继续处理
- 2xx:成功 --表示请求已被成功接收、理解、接受
- 3xx:重定向–要完成请求必须进行更进一步的操作
- 4xx:客户端错误–请求语法错误或请求无法实现
- 5xx:服务器错误–服务器未能实现合法的请求
- 常见状态码:
- GET和POST请求的区别
- HTTP报文层面:GET将请求信息放在URL中,POST放在报文体中
- 数据库层面:GET符合幂等性(一次多次操作一致)和安全性,POST不符合
- 其他层面:GET可以被缓存,被储存,而POST不行
- Cookie和Session区别:
- Cookie:
- 服务器向客户端发送的特殊信息,以文本的形式存放在客户端
- 客户端再次请求时会把Cookie回发,服务器接收后,解析Cookie生成与客户端相对应的内容。
- Session:
- 服务器端机制,在服务器上保存的信息
- 解析客户端请求并操作Session id,按需保存状态信息
- Session实现方式:1、使用Cookie实现 2、使用URL回写来 返回 JSESSIONID
+区别: - 1、Cookie数据在客户服务器上,Session在服务器上
- Session相对Cookie更安全
- 若考虑减轻服务器负担,应当使用Cookie
HTPP1.1与1.0区别
HTTP1.0最早在网页中使用是在1996年,那个时候只是使用一些较为简单的网页上和网络请求上,而HTTP1.1则在1999年才开始广泛应用于现在的各大浏览器网络请求中,同时HTTP1.1也是当前使用最为广泛的HTTP协议。 主要区别主要体现在:
- 缓存处理,在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略。
- 带宽优化及网络连接的使用,HTTP1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。
- 错误通知的管理,在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
- Host头处理,在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。
- 长连接,HTTP 1.1支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在HTTP1.1中默认开启Connection: keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点。
HTTP2.0与1.x区别
- 新的二进制格式(Binary Format),HTTP1.x的解析是基于文本。基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合。基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便且健壮。
- 多路复用(MultiPlexing),即连接共享,即每一个request都是是用作连接共享机制的。一个request对应一个id,这样一个连接上可以有多个request,每个连接的request可以随机的混杂在一起,接收方可以根据request的 id将request再归属到各自不同的服务端请求里面。
- header压缩,如上文中所言,对前面提到过HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。
- 服务端推送(server push),同SPDY一样,HTTP2.0也具有server push功能。
HTTP与HTTPS区别
- SSL(安全套接层):为网络通信提供安全与数据完整性的一种安全协议,是操作系统对外的API,采用身份验证和数据加密保证安全和数据的完整性
- HTTPS数据传输流程:
- 浏览器将支持加密算法的信息发送给服务器
- 服务器选择一套浏览器支持的加密算法,以证书的形式回发浏览器
- 浏览器验证证书的合法性,并结合证书公匙加密信息发送给服务器
- 服务器使用私钥解密信息,验证哈希,加密响应消息回发浏览器
- 浏览器解密响应消息,并对消息进行验真,之后进行加密交互数据
- HTTPS与HTTP传输区别
- HTTPS需要到CA申请证书,HTTP不需要
- HTTPS密文传输,HTTP明文传输
- 连接方式不同,HTTPS默认使用443端口,HTTP使用80端口
- HTTPS=HTTP+加密+认证+完整性保护,较HTTP安全
- HTTPS真的安全吗?浏览器默认填充http,请求需要进行跳转,有被劫持的风险,可以使用HSTS优化
Cookie的作用是什么?和Session有什么区别?
Cookie 和 Session都是用来跟踪浏览器用户身份的会话方式,但是两者的应用场景不太一样。
Cookie 一般用来保存用户信息 比如①我们在 Cookie 中保存已经登录过得用户信息,下次访问网站的时候页面可以自动帮你登录的一些基本信息给填了;②一般的网站都会有保持登录也就是说下次你再访问网站的时候就不需要重新登录了,这是因为用户登录的时候我们可以存放了一个 Token 在 Cookie 中,下次登录的时候只需要根据 Token 值来查找用户即可(为了安全考虑,重新登录一般要将 Token 重写);③登录一次网站后访问网站其他页面不需要重新登录。Session 的主要作用就是通过服务端记录用户的状态。 典型的场景是购物车,当你要添加商品到购物车的时候,系统不知道是哪个用户操作的,因为 HTTP 协议是无状态的。服务端给特定的用户创建特定的 Session 之后就可以标识这个用户并且跟踪这个用户了。
Cookie 数据保存在客户端(浏览器端),Session 数据保存在服务器端。
Cookie 存储在客户端中,而Session存储在服务器上,相对来说 Session 安全性更高。如果要在 Cookie 中存储一些敏感信息,不要直接写入 Cookie 中,最好能将 Cookie 信息加密然后使用到的时候再去服务器端解密。
URI与URL区别
- URI(Uniform Resource Identifier) 是统一资源标志符,可以唯一标识一个资源。
- URL(Uniform Resource Location) 是统一资源定位符,可以提供该资源的路径。它是一种具体的URI,即 URL 可以用来标识一个资源,而且还指明了如何 locate 这个资源。
Socket简介
- Socket是对TCP/IP协议的抽象,是操作系统对外开放的接口
- Socket通信流程
public class TCPServer {
public static void main(String[] args) throws Exception {
//创建socket,并将socket绑定到65000端口
ServerSocket ss = new ServerSocket(65000);
//死循环,使得socket一直等待并处理客户端发送过来的请求
while (true) {
//监听65000端口,直到客户端返回连接信息后才返回
Socket socket = ss.accept();
//获取客户端的请求信息后,执行相关业务逻辑
new LengthCalculator(socket).start();
}
}
}
public class LengthCalculator extends Thread {
//以socket为成员变量
private Socket socket;
public LengthCalculator(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
//获取socket的输出流
OutputStream os = socket.getOutputStream();
//获取socket的输入流
InputStream is = socket.getInputStream();
int ch = 0;
byte[] buff = new byte[1024];
//buff主要用来读取输入的内容,存成byte数组,ch主要用来获取读取数组的长度
ch = is.read(buff);
//将接收流的byte数组转换成字符串,这里获取的内容是客户端发送过来的字符串参数
String content = new String(buff, 0, ch);
System.out.println(content);
//往输出流里写入获得的字符串的长度,回发给客户端
os.write(String.valueOf(content.length()).getBytes());
//不要忘记关闭输入输出流以及socket
is.close();
os.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class TCPClient {
public static void main(String[] args) throws Exception {
//创建socket,并指定连接的是本机的端口号为65000的服务器socket
Socket socket = new Socket("127.0.0.1", 65000);
//获取输出流
OutputStream os = socket.getOutputStream();
//获取输入流
InputStream is = socket.getInputStream();
//将要传递给server的字符串参数转换成byte数组,并将数组写入到输出流中
os.write(new String("hello world").getBytes());
int ch = 0;
byte[] buff = new byte[1024];
//buff主要用来读取输入的内容,存成byte数组,ch主要用来获取读取数组的长度
ch = is.read(buff);
//将接收流的byte数组转换成字符串,这里是从服务端回发回来的字符串参数的长度
String content = new String(buff, 0, ch);
System.out.println(content);
//不要忘记关闭输入输出流以及socket
is.close();
os.close();
socket.close();
}
}
- UDP
public class UDPServer {
public static void main(String[] args) throws Exception {
// 服务端接受客户端发送的数据报
DatagramSocket socket = new DatagramSocket(65001); //监听的端口号
byte[] buff = new byte[100]; //存储从客户端接受到的内容
DatagramPacket packet = new DatagramPacket(buff, buff.length);
//接受客户端发送过来的内容,并将内容封装进DatagramPacket对象中
socket.receive(packet);
byte[] data = packet.getData(); //从DatagramPacket对象中获取到真正存储的数据
//将数据从二进制转换成字符串形式
String content = new String(data, 0, packet.getLength());
System.out.println(content);
//将要发送给客户端的数据转换成二进制
byte[] sendedContent = String.valueOf(content.length()).getBytes();
// 服务端给客户端发送数据报
//从DatagramPacket对象中获取到数据的来源地址与端口号
DatagramPacket packetToClient = new DatagramPacket(sendedContent,
sendedContent.length, packet.getAddress(), packet.getPort());
socket.send(packetToClient); //发送数据给客户端
}
}
public class UDPClient {
public static void main(String[] args) throws Exception {
// 客户端发数据报给服务端
DatagramSocket socket = new DatagramSocket();
// 要发送给服务端的数据
byte[] buf = "Hello World".getBytes();
// 将IP地址封装成InetAddress对象
InetAddress address = InetAddress.getByName("127.0.0.1");
// 将要发送给服务端的数据封装成DatagramPacket对象 需要填写上ip地址与端口号
DatagramPacket packet = new DatagramPacket(buf, buf.length, address,
65001);
// 发送数据给服务端
socket.send(packet);
// 客户端接受服务端发送过来的数据报
byte[] data = new byte[100];
// 创建DatagramPacket对象用来存储服务端发送过来的数据
DatagramPacket receivedPacket = new DatagramPacket(data, data.length);
// 将接受到的数据存储到DatagramPacket对象中
socket.receive(receivedPacket);
// 将服务器端发送过来的数据取出来并打印到控制台
String content = new String(receivedPacket.getData(), 0,
receivedPacket.getLength());
System.out.println(content);
}
}
有限状态机
注:主动、被动与服务器、客户端没有明确的对应关系。
每个连接均开始于CLOSED状态。当一方执行了被动的连接原语(LISTEN)或主动的连接原语(CONNECT)时,它便会脱离CLOSED状态。如果此时另一方执行了相对应的连接原语,连接便建立了,并且状态变为ESTABLISHED。任何一方均可以首先请求释放连接,当连接被释放后,状态又回到了CLOSED。
CLOSED: 表示初始状态。
LISTEN: 表示服务器端的某个SOCKET处于监听状态,可以接受连接了。
SYN_RCVD: 这个状态表示接受到了SYN报文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态,很短暂,基本上用netstat很难看到这种状态的,除非特意写一个客户端测试程序,故意将三次TCP握手过程中最后一个ACK报文不予发送。因此该状态下收到客户端的ACK报文后,它会进入到ESTABLISHED状态。
SYN_SENT: 表示客户端已发送SYN报文,当客户端SOCKET执行CONNECT连接时,它首先发送SYN报文,因此也随即它会进入到了SYN_SENT状态,并等待服务端的发送三次握手中的第2个报文。
ESTABLISHED:表示连接已经建立了。
FIN_WAIT_1: 其实FIN_WAIT_1和FIN_WAIT_2状态都表示等待对方的FIN报文。
**而这两种状态的区别是:**FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状态,当然在实际的正常情况下,无论对方何种情况下,都应该马上回应ACK报文,所以FIN_WAIT_1状态一般是比较难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。
FIN_WAIT_2:实际上FIN_WAIT_2状态下的SOCKET,表示半连接,即有一方要求close连接,但另外还告诉对方,我暂时还有点数据需要传送给你,稍后再关闭连接。
TIME_WAIT: 表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。在FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。
CLOSING: 表示双方都正在关闭SOCKET连接,属于一种比较罕见的例外状态。正常情况下,当发送FIN报文后,按理来说是应该先收到(或同时收到)对方的ACK报文,再收到对方的FIN报文。但是CLOSING状态表示发送FIN报文后,并没有收到对方的ACK报文,反而却收到了对方的FIN报文。如果双方几乎在同时close一个SOCKET的话,那么就出现了双方同时发送FIN报文的情况,即出现CLOSING状态。
CLOSE_WAIT: 表示在等待关闭。当对方close一个SOCKET后发送FIN报文给自己,你毫无疑问地会回应一个ACK报文给对方,此时则进入到CLOSE_WAIT状态。接下来真正需要考虑的事情是查看是否还有数据发要送给对方,如果没有的话,那么就可以close这个SOCKET,发送FIN报文给对方,即关闭连接。所以在CLOSE_WAIT状态下,需要完成的事情是等待你去关闭连接。
LAST_ACK: 这个状态还是比较容易好理解的,它是被动关闭一方在发送FIN报文后,最后等待对方的ACK报文。当收到ACK报文后,也即可以进入到CLOSED可用状态了。
关闭的状态转换:假设通信双方是A,B;A主动发起关闭
(1)主动发起关闭方
A首先主动发起FIN报文,准备关闭TCP连接,然后进入FIN_WAIT1状态;
如果A收到了ACK报文,进入FIN_WAIT2状态,说明B还有数据发给A。不久之后,B发送FIN给A,然后,A发送ACK,并进入TIME_WAIT状态。
如果A收到到了ACK + FIN,A直接进入TIME_WAIT状态。
经过2个MSL,没有收到FIN信号,那么TIME_WAIT就自动转化为CLOESD。
(2)被动接收方
B在收到A的FIN报文后,知道A准备关闭 TCP连接了(注意只是A单方面关闭发数据的连接,也就是说A还可以接收数据)。B将发送ACK给A,进入CLOSED_WAIT状态。如果此时B也有数据发送给A,那么就一直发送好了,反正A不会发数据了。此时A处于FIN_WAIT2状态。当B的数据发送完毕之后,那么B发送FIN给 A,B进入LAST_ACK状态,当收到A发过来的ACK信号后,A进入CLOSED状态。
详解
来源:oschina
链接:https://my.oschina.net/u/4353890/blog/4288936