倒霉的菜鸟

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理
  • 什么是IP 协议? 

协议就是为了实现网络通信而创建的一系列规范。  通常我们的网络模型从上到下共分为4层: 应用层, 传输层, 网络层 和数据链路层。

IP协议属于网络层协议,它精确定义了网络通信中的各种细节, 比如发送信息的格式和含义。 所有的TCP, UDP数据都要以IP数据的格式来传输。 同时在互联网中,为每一台主机分配ip地址来表示自己。IP地址用4个字节,32位的二进制数来表示(IPv4), 为了便于使用,通常取每个字节的十进制数,字节与字节之间用“.”隔开, 如192.168.1.1

IP只为主机提供一种 无连接, 不可靠,尽力而为的数据包传送服务

所谓不可靠就是说它不保证ip包一定能到达目的地, 有可能在传输过程中丢包。至于丢包之后怎么办, 它认为这是TCP/UDP要做的事

所谓无连接就是说每个数据包都是独立的, 它不维护任何状态,就像邮递员送信,每封信件都是独立的, 收信人并不知道邮递员会不会来,什么时候来

那么, 当初为什么不把ip协议设计成可靠的呢?

因为在“端到端的可靠传输中”, 即使通信网络100%可靠, 也不能保证端到端100%可靠(数据读写各个环节都可能出现差错),所以在终端设备已经十分智能的情况下,没有耗费大量性能来保证通信网络的可靠, 而是应该让网络简单些,让智能终端来完成“让传输可靠的任务”

具体到我们的通信过程中,就是TCP要做的事情。TCP可以提供可靠的数据传送, 比如从A传输一份文件到B, B主机的TCP一旦发现数据传输有差错,就会告诉A主机将有差错的那部分重传。

那么问题来了, TCP如何保证可靠传输呢?

  • 什么是TCP?

TCP是传输层协议,其特点是 可靠,面向连接,面向字节流, 全双工, 且抵达时的数据顺序和发送时相同。 因此TCP适合对准确性要求高或者要求有连接的场景。

具体如何保证可靠传输呢?

1, 校验和(checksum?)

计算方式:将发送的报文段都当成一个16位的整数,将这些整数加起来,取反(进位不丢弃,补在前面)

发送方: 计算校验和,放在报文段的首部

接收方:收到数据后,用同样方式计算校验和,进行对比。 如果不相同,则说明数据传输有误

2, 确认应答与序列号

TCP传输字节流,传输时将每个字节的数据都进行了编号,就是序列号

当新连接建立的时候, 发送的第一个字节数据的序号叫做初始序号(ISN), 初始序号并不一定是1, 一般是根据连接建立时的时间来定的。 初始序号告诉对方第一个报文段是谁,三次握手的目的之一就是确认初始序号。 (三次握手还会确认最大消息长度MSS, 两端主机在发出“建立TCP连接请求的SYN包”时,会在SYN包的TCP首部中写入MSS选项,告诉对方自己所能够适应的MSS的大小,然后发送端主机会在两者之间选择一个较小的MSS值投入使用。)

报文段序号和字节序号:假设一个报文段序号为301, 它携带了100个字节,那么字节序号就是301-400

确认号:每传送一个TCP段,都要等待对方发出确认, 但这样实在效率太低,所以在TCP协议中,一般采用累积确认的方式,即没收到一些连续的TCP段,可以只对最后一个进行确认。

如发送方发出了序号为301的报文段, 它携带了100字节的数据【301-400】,则接收方发出的ACK报文中的确认号应该是401,表示期望下次从401开始发送

 

3,超时重传

因为TCP在每次发送数据包后都会等待接收方应答, 那么如果发送过程中由于网络原因数据包整体丢失,接收方没收到消息,或者接收方发出了应答但是发送方没收到怎么办呢?

为了解决这个问题, TCP引入了超时重传机制,就是发送完等待一段时间后,如果没有收到接收方的ACK报文,就再发一次

那么这个时间是多少呢?

超时以500ms(0.5秒)为一个单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍。重发一次后,仍未响应,那么等待 2 * 500ms 的时间后,再次重传。等待4 * 500ms的时间继续重传。以一个指数的形式增长。累计到一定的重传次数,TCP就认为网络或者对端出现异常,强制关闭连接。

4, 流量控制

接收端在收到数据后,会将其放进缓冲区进行处理。 那么如果发送的速度太快, 缓冲区很快就填满了,接下来就会出现数据丢失,超时重传等一些列问题

因此, 需要根据接收端对数据的处理能力,来决定发送端发送的速度,这就是流量控制,具体做法就是接收方在确认应答发送ACK报文时,会将自己的即时窗口大小(实际上就是缓冲区剩余内存)放在报文里,如果这个数字为0, 那么发送方就会停止发送数据,并定时发送窗口探测数据,让接收方告诉自己当前的窗口大小。

16位的窗口大小最大能表示65535个字节(64K),但是TCP的窗口大小最大并不是64K。在TCP首部中40个字节的选项中还包含了一个窗口扩大因子M,实际的窗口大小就是16位窗口字段的值左移M位。每移一位,扩大两倍。

5, 拥塞控制

TCP传输过程中, 如果一开始就传输大量的数据,很可能会引起拥堵,造成丢包或超时重传

所以TCP引入了一个慢开始的机制, 在开始发送数据时, 先发送少量的数据探路,摸清楚网络状况后再决定以多大的速率进行发送

慢开始算法和拥塞避免算法

算法的具体过程:

发送方维护一个拥塞窗口和慢开始门限,拥塞窗口的大小取决于网络的拥塞情况和接收窗口的大小

通信开始时, 发送方的发送窗口设为1, 每次收到确认应答后, 都将发送窗口设为当前的两倍

当拥塞窗口大于慢开始门限时, 则使用拥塞避免算法, 每次收到确认应答后都将发送窗口+1(加法增加)

若发送方出现了超时重传,则表明出现了拥塞, 此时发送方会将慢开始门限设定为当前发送窗口的一半(乘法减小),发送窗口设为1

快重传和快恢复:

因为TCP具有累计确认的能力,所以并不会每收到一个分组都会发出确认(太影响性能),通常是收到多个分组之后才去确认。

快重传机制要求, 一旦接收方收到一个失序的分组,就要立即发出前一个正确分组的确认应答, 这样就可以让发送者尽早知道数据可能丢失
快恢复是指,如果发送方连续收到3个队某分组的确认应答, 就表明当前正发送的分组已丢失,这时无需等待超时,直接执行乘法减小加法增大(将慢开始门限设为当前拥塞窗口的一半,执行拥塞避免算法)

 

6,失序重排  重复丢弃 

TCP有接收缓存, 如果收到的分组序号大于当前期望的序号, 会把它按序号缓存起来, 等待它之前序号的分组收到后再重新排列

如果收到的分组序号小于当前期望的序号, 说明是重复分组,直接丢弃

 

  • TCP三次握手四次挥手?

TCP是面向连接的, 无论哪一方向另一方发送数据之前都需要先建立连接, 这就是三次握手, 握手的目的是交换序列号,确认号以及窗口信息

具体过程就是:

第一次握手,建立连接。客户端发送请求连接报文, 将SYN置为1, 序列号置为x, 然后客户端进入SYN_SEND状态,等待服务器确认

第二次握手,服务器收到SYN报文段,向发送方发出确认报文(ACK= x+1), 同时自己发送SYN请求信息(SYN为1, 序列号为y),放在同一个报文段中。然后服务器进入SYN_RECV状态

第三次握手,客户端收到服务器报文后,向服务器发送ACK报文(ACK= y+1), 将序列号置为初始序号, 发送完毕后,客户端和服务器都进入ESTABLISHED状态

 

断开连接时,四次分手的过程:

第一次, 主机1(可以是客户端也可以是服务器)向主机2发出FIN报文,请求断开连接, 发送完之后, 主机1进入FIN_WAIT_1状态,表示主机1没有数据要发给主机2了

第二次, 主机2收到主机1的断开请求,作出确认应答,向主机1回复ACK报文,表示同意断开连接, 主机1收到应答后进入FIN_WAIT_2状态

第三次,主机2向主机1发送FIN报文,请求关闭连接,同时主机2进入LAST_ACK状态

第四次, 主机1收到主机2的FIN报文后, 作出确认应答,向主机2发出ACK报文段,然后主机1进入TIME_WAIT 状态, 主机2收到确认报文后,就关闭连接。主机1等待2MSL的时间后,没有收到回复就证明主机2已经关闭, 那么它也会关闭连接。

 

问题: 为什么要握手要3次挥手要4次。

因为通信是双方的事情,双方一问一答,来回就是4次。

握手时因为服务器发送的SYN可以和确认报文ACK合并, 所以看起来时三次

分手时,因为服务器回复ACK和发送PIN并不在一个时间点(客户端请求断开连接时只能代表客户端没有数据要发送了,但可能服务器还有数据要发给客户端),无法合并。所以是4次

  • UDP?

UDP的特点

无连接:知道对方的IP和端口号,就可以直接发送数据包,无需建立连接

不可靠:没有确认机制,没有重传机制, 如果网络故障导致不能发现对方,UDP也不会给应用层返回任何错误信息

面向数据报:应用层使用UDP协议时, 无论是从UDP接收缓冲区拷贝数据, 还是把数据写入发送缓冲区, 都是整条数据拷贝的,既不会拆分也不会合并,不存在粘包的问题

UDP传输的最大长度:是65535个字节

那么如果大于UDP传输的长度, 就需要在应用层手动分包, 多次发送,接收端再手动拼接。

UDP也使用校验和的方式来判断数据是否在传输过程中损坏

应用层使用sendTo将数据写入UDP发送缓冲区,在缓冲区内,加上UDP报头之后就直接提交给网络层进行下一步传输

接收端将从网络层接收到的数据放入接收缓冲区, 应用层使用recvfrom接口将缓冲区内的数据拷贝到应用层。缓冲区不保证数据有序,也不保证可靠。当接收缓冲区满的时候, 收到的数据包直接丢弃。

  • Http?

HTTP协议是建立在TCP协议之上的一种应用层协议。 

HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“ 一次连接 ”。 

1)在HTTP 1.0中,客户端的每次请求都要求建立一次单独的连接,在处理完本次请求后,就自动释放连接。 

2)在HTTP 1.1中则可以在一次连接中处理多个请求,并且多个请求可以重叠进行,不需要等待一个请求结束后再发送下一个请求。 

怎么理解Http协议是无状态的?

无状态是指服务器没有记忆能力,每一次请求都是相互独立的, 服务器并不会保存上次请求的状态。 

在无状态的情况下,如果下一条request需要用到前一条的某些数据, 那没办法,只能重传, 这样就导致每次连接需要传送的数据量很大。 ---怎么解决? cookie, session

Http长连接和短连接

Http是基于TCP的上层应用, 所以我们通常说的HTTP长连接短连接本质上是TCP的长/短连接

在HTTP/1.0中默认使用短连接。也就是说,客户端和服务器每进行一次HTTP操作,就建立一次连接,任务结束就中断连接。当客户端浏览器访问的某个HTML或其他类型的Web页中包含有其他的Web资源(如JavaScript文件、图像文件、CSS文件等),每遇到这样一个Web资源,浏览器就会重新建立一个HTTP会话。

短连接的优点是管理简单, 存在的连接都是有用的,不需要额外的管理手段。 缺点是,如果有频繁的网络请求, 不断握手挥手会消耗资源影响性能。

从HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的HTTP协议,会在响应头加入这行代码:

Connection:keep-alive

在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,客户端再次访问这个服务器时,会继续使用这一条已经建立的连接。Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。实现长连接需要客户端和服务端都支持长连接。

TCP的长连接主要依靠服务器提供TCP保活功能, 在长连接中,完成一次请求后,连接并不会主动断开。 那么当服务器检测到客户端已经消失,但连接未断开,且两个小时内没有任何动作时, 就会给客户端发送一个探测报文,

根据客户端主机响应探测4个客户端状态:

  • 客户主机依然正常运行,且服务器可达。此时客户的TCP响应正常,服务器将保活定时器复位。
  • 客户主机已经崩溃,并且关闭或者正在重新启动。上述情况下客户端都不能响应TCP。服务端将无法收到客户端对探测的响应。服务器总共发送10个这样的探测,每个间隔75秒。若服务器没有收到任何一个响应,它就认为客户端已经关闭并终止连接。
  • 客户端崩溃并已经重新启动。服务器将收到一个对其保活探测的响应,这个响应是一个复位,使得服务器终止这个连接。
  • 客户机正常运行,但是服务器不可达。这种情况与第二种状态类似。

长连接可以省去较多的TCP建立和关闭的操作,减少浪费,节约时间。 但是 在长连接的应用场景下,client端一般不会主动关闭它们之间的连接,Client与server之间的连接如果一直不关闭的话,会存在一个问题,随着客户端连接越来越多,server早晚有扛不住的时候,这时候server端需要采取一些策略,如关闭一些长时间没有读写事件发生的连接,这样可 以避免一些恶意连接导致server端服务受损;如果条件再允许就可以以客户端机器为颗粒度,限制每个客户端的最大长连接数,这样可以完全避免某个蛋疼的客户端连累后端服务。

 一个http请求通常包括些什么东西呢? (请求行, 请求头, 请求体)

              请求行: 请求方法字段、URL字段和HTTP协议版本, 如 GET    /baidu/index.html     HTTP/1.1

                              请求方法有这些:GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE、CONNECT

              请求头: (key value形式) User-Agent:产生请求的浏览器类型。

                                                        Accept:客户端可识别的内容类型列表。

                                                        Host:主机地址

              请求体:get请求会把请求数据拼在url后面

一个http响应包括些什么呢? (状态行,消息报头,响应正文)

              状态行: 状态行是由:HTTP-Version+StatusCode+ReasonPhrase

                             比如:HTTP/1.1 200 ok

                             分别表示http版本 + 状态码 + 状态代码的文本描述。

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

              消息报文(也叫响应头):包含服务器类型,日期,长度,内容类型等

                                                   如 Server:Apache Tomcat/5.0.12

                                                       Date:Mon,6Oct2003 13:13:33 GMT

                                                       Content-Type:text/html

                                                       Last-Moified:Mon,6 Oct 2003 13:23:42 GMT

                                                       Content-Length:112

              响应正文:就是服务器返回的html页面或者json数据

  • DNS?

DNS是域名解析协议(Domain Name System),是基于UDP的一个应用层协议

DNS寻址过程:

根据网络层IP协议, 我们必须知道目的地的Ip地址才可以进行通信,所以当我们在浏览器输入一个网址,首先要做的就是查找它的IP, 具体过程如下:

首先查找浏览器缓存,检查本地host文件中是否有这个域名的映射,如果有,就直接返回

如果没有, 则查询本地DNS解析器缓存

如果还是没有,则查询本地域名服务器(填写或分配的首选域名服务器),如果要查询的域名包含在本地配置区域资源中,返回解析结果

如果查不到, 本地域名服务器就会向根域名服务器(根域名服务器知道所有顶级域名服务器的IP地址)发出查询请求。根域名服务器收到查询请求后, 会判断当前域名是谁来授权管理, 并返回这个顶级域名服务器的ip 

然后本地域名服务器再向这个顶级域名服务器发出查询请求, 顶级域名服务器自身如果能查到就返回结果,如果不能查到,就返回次级域名服务器的IP。如此迭代,直到返回结果

  • Socket?

socket是通信的基石, 是支持TCP/IP协议的网络通信的基本操作单元, 它包含了网络通信必须的5种信息:连接使用的协议, 本地主机IP地址,本地协议端口号,远程主机IP地址及 远程协议端口号

应用层通过传输层进行网络通信时, TCP会遇到同时为多个进程提供并发服务的问题, 多个进程要通过同一个TCP协议端口传输数据。 socket接口可以使应用层和传输层区别不同的进程, 实现并发服务

建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket ,另一个运行于服务器端,称为ServerSocket 。 

 

Socket之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。 

服务器监听:服务器端Socket并不定位具体的客户端Socket,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。 

客户端请求:指客户端的Socket提出连接请求,要连接的目标是服务器端的Socket。为此,客户端的Socket必须首先描述它要连接的服务器的Socket,指出服务器端Socket的地址和端口号,然后就向服务器端Socket提出连接请求。 

连接确认:当服务器端Socket监听到或者说接收到客户端Socket的连接请求时,就响应客户端Socket的请求,建立一个新的线程,把服务器端Socket的描述发给客户 端,一旦客户端确认了此描述,双方就正式建立连接。而服务器端Socket继续处于监听状态,继续接收其他客户端Socket的连接请求。

 ServerSocket的常用方法

构造方法 ServerSocket() 创建非绑定服务器套接字

ServerSocket(int port)创建绑定到特定端口的服务器套接字

ServerSocket(int port, int backlog)利用指定的backlog闯将服务器套接字,并将其绑定到指定端口

ServerSocket(int port, int backlog, InetAddress bindAddress)利用指定的端口,侦听backlog和要绑定的本地ip地址创建服务器套接字。  适用于计算机上有多块网卡多个IP的情况, 可以明确规定serverSocket在哪块网卡或者哪个ip上监听连接请求。

所有构造方法都会抛出IOException

accept()等待客户连接,若连接,则创建一套接字。 该方法返回一个与客户端Socket对象相连接的socket。 服务器端使用getInputStream()获得的输入流对象,将对应客户端getOutputStream()写入的输出流对象, 反之亦然。

accept()方法会阻塞线程,直到收到客户的呼叫

isBound()判断serverSocket的绑定状态

getInetAddress()返回此服务器套接字的本地地址

isClosed()判断serverSocket的关闭状态

close()关闭服务器套接字

bind(SocketAddress endpoint)将serverSocket绑定到特定地址 (IP和端口号)

getLocalPort()返回服务器套接字等待的端口号

 

客户端Socket

socket = new Socket("192.168.1.1", "8888")//创建socket并指定要连接的serverSocket的端口号,IP

ops = socket.getOutputStream() //获取输出流对象 

 

创建Socket连接时,可以指定使用的传输层协议,Socket可以支持不同的传输层协议(TCP或UDP),当使用TCP协议进行连接时,该Socket连接就是一个TCP连接。

socket- 长连接, 连接建立后服务器端可以随时向客户端发送数据。 但缺点是在实际网络应用中,客户端到服务器之间的通信往往需要穿越多个中间节点,例如路由器、网关、防火墙等,大部分防火墙默认会关闭长时间处于非活跃状态的连接而导致 Socket 连接断连,因此需要通过轮询告诉网络,该连接处于活跃状态。 

个人笔记,摘抄自 TCP如何保证可靠传输_Lzc的博客-CSDN博客_tcp如何保证数据传输的可靠性

posted on 2021-09-05 23:01  倒霉的菜鸟  阅读(242)  评论(0编辑  收藏  举报