Linux网络(应用层,传输层)

应用层:负责应用程序之间的数据沟通—协议都是用户自己定的

自定制协议:

  • 实现一个网络版计算器
  • num1 num2 op
  • num1;num2;op
  • 结构体
    typedef struct info{int num1;int num2;char op;}Info
    Info info
    info.num1=11; info.num2=22; op=’+’

    Linux网络(应用层,传输层)_第1张图片

结构化数据的传输
序列化: 将数据对象按照指定协议进行持久化存储或者数据传输进行组织
反序列化: 将二进制串按照指定的协议进行解析
序列化方式:json protobuf


知名协议:HTTP协议(超文本传输协议)

URL:统一资源定位符(网址)
协议方案名: //用户名:密码@服务器地址:端口/资源路径?查询字符串#片段标识符

查询字符串使用户提交给服务端程序的数据,这些数据不能带有特殊字符;因为若是带有特殊字符和url中的分隔符产生歧义导致服务端解析错误;若真有特殊字符,则需要对特殊字符进行url编码操作,避免二义性

URL编码(urlencode):将这个特殊字符转换为16进制数字的字符串,并在字符串前加上%表示%后两个字节的数据需要url解码
URL解码(urldecode):遇到%符号,则将后边两个16进制字符转换为一个字节的数据:%2b - (‘2’ - ‘0’)<<4+b-‘0’

HTTP协议格式:fiddler

1.首行:

请求首行:请求方法 URL 协议版本

请求方法:GET/POST

GET和POST的区别: GET没有正文, GET提交的输在url中(查询字符串);POST提交的数据在正文中
URL
协议版本:0.9/1.0/1.1//2

响应首行:协议版本 响应状态码 状态码描述符信息

响应状态码:1** 2** 3** 4** 5** 200 / 302 / 404 / 502

2.头部: 都是一个个以冒号空格形式组成的键值对,这些键值对之间以\r\n进行间隔

Content-Length/Content-Type/Referer/Cookie/Set-Cookie/Location/Transfer-Encoding

3.空行: \r\n 间隔头部与正文
4.正文:
http协议是一个在传输层基于tcp协议实现的应用层协议,在传输层默认使用80端口,使用字符串明文传输


实现一个最最最简单的HTTP服务器

http协议是一个在传输层基于
http服务器实际是一个tcp服务器,不过应用数据的处理时http协议格式
处理数据的时候:

1、获取http头部(首行+头部)
2、获取数据
3、根据头部中的请求处理数据
接收到浏览器的请求之后,打印一下请求的数据,并且响应:

Hello word


传输层:负责端与端之间的数据传输,TCP/UDP

端口: 一个端口只能被一个进程占用,一个进程可以使用多个端口
五元组: 源IP,目的IP,源端口,目的端口,协议—每条数据都有-表示一条通信
netstat-anptul


UDP:无连接,不可靠,面向数据报

udp协议字段

16位源端口,16位目的端口:负责端与端之间的传输
16校验和:验证数据的完整一致性(二进制反码求和)
16位udp数据报总长度—表示数据报有多大

无连接不可靠: 不需要建立连接,只要知道地址信息可以直接发送数据,并且不关注数据是否到达
面向数据报: --数据只能一整条一整条交付给应用层;传输不够灵活,但是不会产生粘包 (数据的整条收发—依靠协议字段中的数据报长度字段实现)
1、一条数据通过sendto发送,数据到了传输层封装了udo报头之后直接进行发送;对端收到udp报文;对整个报文进行二进制反码求和(协议字段中的校验和),判断接收到的报文是否和发送的一致;

若数据一致,表示这条数据可以向上层交付(当用户调用recvfrom能够获取数据);
向上层交付的时候,不能交付半条数据,因为每条数据都有自己的数据报长度,一旦交付半条数据,就会造成剩下的数据没有报头指定数据有多长;需要额外进行处理,因此面向数据报只能整条数据收发

2、udp数据总长度的存储只用了2个字节保存(16位),意味着一个包含头部信息的udp数据报最大长=64k(65535字节)个字节长度;

若用户sendto发送的数据大于(64k-8个)字节,sendto会失败;因此若数据过大需要用户在应用层进行分包处理

3、意味着如果要发送的数据长度大于(65535-8)个字节大小,则这个数据需要用户在应用层进行数据分段;因为udp不会在传输层自动进行数据分段

4、udp不保证数据报的有序到达;需要用户在应用层进行包序管理

Linux网络(应用层,传输层)_第2张图片


TCP: 面向连接,可靠传输,面向字节流
tcp的连接管理

Linux网络(应用层,传输层)_第3张图片
三次握手: 双方确认对方是否具备数据收发能力—两次不安全,四次没必要

第一次握手:客户端发送TCP包,置SYN标志位为1,将初始序号X,保存在包头的序列号(Seq)里。
第二次握手:服务端回应确认包,置SYN标志位为1,置ACK为X+1,将初始序列号Y,保存在包头的序列号里。
第三次握手:客户端对服务端的确认包进行确认,置SYN标志位为0,置ACK为Y+1,置序列号为Z。

四次挥手: 被动关闭方接收到关闭请求之后,进行ack确认,但是操作系统需要用户调用close才会发送FIN请求

第一次挥手:客户端发送一个FIN,用来关闭客户端到服务器的数据传送,客户机进入FIN_WAIT_1状态。
第二次挥手:服务器收到FIN后,发送一个ACK给客户端,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),服务器进入CLOSE_WAIT状态。
第三次挥手:服务器发送一个FIN,用来关闭服务器到客户端的数据传送,服务器进入LAST_ACK状态。
第四次挥手:客户端收到FIN后,客户端进入TIME_WAIT状态,接着发送一个ACK给服务器,确认序号为收到序号+1,服务器进入CLOSED状态,完成四次挥手。

TIME_WAIT:

1、假如没有time_wait,客户端直接关闭;但是又重启了相同地址的客户端;

有可能因为四次挥手最后一次ACK丢失

导致服务端重传FIN包,对后续连接造成影响
新客户端发送SYN到服务端,服务端认为状态有误,回复重置连接报文-RST报文

因此主动关闭方发送最后一个ACK后不能直接关闭,需要等待一段时间—2个MSL时间
MSL:报文最大生存周期

等待两个MSL时间是为了能够处理对端重传的FIN包进行ACK回复;
并且等待两个MSL时间是为了让所有网络中延迟的报文都消失在网络中,不会对后续连接造成影响

介绍一下TCP连接建立与关闭过程中的状态。TCP连接过程是状态的转换,促使状态发生转换的因素包括用户调用、特定数据包以及超时等,具体状态如下所示:

1.CLOSED : 初始状态,表示没有任何连接。
2.LISTEN : Server 端的某个 Socket 正在监听来自远方的 TCP 端口的连接请求。
3.SYN_SENT : 发送连接请求后等待确认信息。当客户端 Socket 进行 Connect 连接时,会首先发送 SYN 包,随即进入 SYN_SENT 状态,然后等待 Server 端发送三次握手中的第 2 个包。
4.SYN_RECEIVED : 收到一个连接请求后回送确认信息和对等的连接请求,然后等待确认信息。通常是建立 TCP 连接的三次握手过程中的一个中间状态,表示 Server 端的 Socket 接收到来自 Client 的 SYN 包,并作出回应。 ESTABLISHED :表示连接已经建立,可以进行数据传输。
5.FIN_WAIT_1 : 主动关闭连接的一方等待对方返回 ACK 包。若 Socket 在 ESTABLISHED 状态下主动关闭连接并向对方发送 FIN 包(表示己方不再有数据需要发送),则进入 FIN_WAIT_1 状态,等待对方返回 ACK 包,此后还能读取数据,但不能发送数据。在正常情况下,无论对方处于何种状态,都应该马上返回 ACK 包,所以 FIN_WAIT_1 状态一般很难见到。
6.FIN_WAIT_2 : 主动关闭连接的一方收到对方返回的 ACK 包后,等待对方发送 FIN 包。处于 FIN_WAIT_1 状态下的 Socket 收到了对方返回的 ACK 包后,便进入 FIN_WAIT_2 状态。由于 FIN_WAIT_2 状态下的 Socket 需要等待对方发送的 FIN 包,所有常常可以看到。若在 FIN_WAIT_1 状态下收到对方发送的同时带有 FIN 和 ACK 的包时,则直接进入 TIME_WAIT 状态,无须经过 FIN_WAIT_2 状态。
7.TIME_WAIT : 主动关闭连接的一方收到对方发送的 FIN 包后返回 ACK 包(表示对方也不再有数据需要发送,此后不能再读取或发送数据),然后等待足够长的时间( 2MSL )以确保对方接收到 ACK 包(考虑到丢失 ACK 包的可能和迷路重复数据包的影响),最后回到 CLOSED 状态,释放网络资源。
8.CLOSE_WAIT : 表示被动关闭连接的一方在等待关闭连接。当收到对方发送的 FIN 包后(表示对方不再有数据需要发送),相应的返回 ACK 包,然后进入 CLOSE_WAIT 状态。在该状态下,若己方还有数据未发送,则可以继续向对方进行发送,但不能再读取数据,直到数据发送完毕。
9. LAST_ACK : 被动关闭连接的一方在 CLOSE_WAIT 状态下完成数据的发送后便可向对方发送 FIN 包(表示己方不再有数据需要发送),然后等待对方返回 ACK 包。收到 ACK 包后便回到 CLOSED 状态,释放网络资源。
10.CLOSING : 比较罕见的例外状态。正常情况下,发送 FIN 包后应该先收到(或同时收到)对方的 ACK 包,再收到对方的 FIN 包,而 CLOSING 状态表示发送 FIN 包后并没有收到对方的 ACK 包,却已收到了对方的 FIN 包。有两种情况可能导致这种状态:其一,如果双方几乎在同时关闭连接,那么就可能出现双方同时发送 FIN 包的情况;其二,如果 ACK 包丢失而对方的 FIN 包很快发出,也会出现 FIN 先于 ACK 到达。

可靠传输:

连接管理 ,确认应答机制 超时重传机制 序号/确认序号 校验和
Linux网络(应用层,传输层)_第4张图片

TCP为了实现可靠传输牺牲了部分性能;但是有些性能的牺牲不是没有必要的(比如确认应答的丢失导致数据重传);因此又推出了以下几种机制来保证尽量降低不必要的性能损失:

滑动窗口机制: 一次性可以发送大量的数据(受限于协议字段中的窗口大小);然后等待回复
滑动窗口的流量控制:
通信双方通过协议字段中的窗口大小来协商接下来应该发送的最大数据长度:

窗口的大小不大于当前接收缓冲区中空闲空间大小;避免因为发送数据过多导致缓冲区放满,而丢弃数据导致重传

滑动窗口中数据的连续传送,尽力避免了因为ack丢失而导致重传:

确认回复中的ack确认序号能够表示,这个序号之前的数据都已经接收到了(若前边的数据没有接收到,反而接收到了后边的数据,则不会对后边的数据进行ack确认)

滑动窗口中的快速重传机制:

当接受端接收数据的时候,若第一条数据没到,但是接收到了第二条数据,认为第一条数据有可能丢失,立即向发送端连续三次发送重传请求;发送端连续接受到三条重传请求,则对这条数据进行重传
Linux网络(应用层,传输层)_第5张图片

拥塞窗口机制: 慢启动,快增长的发送方式
通信初始双方协议窗口大小,窗口有可能很大,一次会发送很多数据;可能会因为网络原因导致大量丢包,导致重传
发送端维护一个拥塞窗口,控制/限制发送端发送的数据最大大小;这个数字随着每次ack的确认回复快速增长,但是一旦出现丢包重传,则立即重新初始化
Linux网络(应用层,传输层)_第6张图片
延迟应答机制: 尽可能的保证窗口大小(因为接收方,有可能很快就会把数据从缓冲区中拿走)
捎带应答机制: 尽可能避免纯报头的确认回复

可靠传输:连接管理,确认应答机制,超时重传机制,序号/确认序号,校验和

提高性能:滑动窗口机制 ,快速重传机制,流量控制,拥塞控制,延迟应答机制,捎带应答机制
面向字节流:
对数据按照以字节为单位的流式传输
特性: 传输比较灵活;但是缺点是tcp的粘包问题
Linux网络(应用层,传输层)_第7张图片
tcp的粘包有可能在发送产生也有可能在接受端产生
tcp为什么会产生粘包问题?

tcp产生粘包就是内核没有对send要发送的数据进行明确的边界区分

需要用户在应用层解决tcp粘包问题?

在应用层给数据增加边界;特殊字符间隔(http);数据定长;不定长数据的应用协议头中定义数据长度(udp)

TCP连接断开:

tcp协议内部实现保活机制:

当长时间没有数据通信,服务器会像客户端发送保活探测包(发送请求,要求回复);
当这些保活探测请求都没有回复,则认为连接断开

tcp连接断开的体现:

recv返回0;send触发SIGPIPE异常
tcp只有连接断开的时候才会返回0

close—退出进程—关机—断电

在这个实现的最基本的tcp服务端程序中,因为服务端不知道客户端数据什么时候到来,因此程序流程只能写死;但是写死就有可能会造成阻塞(accept/recv);导致服务端无法同时处理多个客户端请求;

tcp接口中的细节: listen第二个参数,accept是在做什么
基于tcp协议实现的应用层协议: http/ftp


重点问题

  1. 三次握手建立连接以及四次挥手断开连接流程?
  2. 握手为什么是三次,挥手是四次?
  3. 三次握手失败,服务端是如何处理的?
  4. TIME_WAIT状态有什么用?
  5. 服务端出现大量TIME_WAIT状态的原因以及解决方法

你可能感兴趣的:(Linux网络)