- 什么是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 连接断连,因此需要通过轮询告诉网络,该连接处于活跃状态。