**传输层:**负责数据能够从发送端传输接收端.
传输层所封装的报头里一定有:源端口号和目的端口号的。
**端口号:**可以标识一台主机中的唯一一个进程(运用程序),这样当数据传输到传输层的时候就可以通过端口号来辨别上层交付的对象是谁。
有些服务器是非常常用的, 为了使用方便, 人们约定一些常用的服务器, 都是用以下这些固定的端口号: ssh服务器, 使用22端口
ftp服务器, 使用21端口
、 telnet服务器, 使用23端口
http服务器, 使用80端口
https服务器, 使用443
执行下面的命令, 可以看到知名端口
cat /etc/services
一个端口号只可以对应一个进程,一个进程可以绑定多个端口号。
传输的协议:结构体字段,把端口号和数据包装。
UDP协议:
UDP的结构非常简单
1、无连接:知道对方的端口号和IP就可以发送数据,不需要链接
2、不可靠:没有确认机制,既不能确定对方是否收到了发送方发送的数据,也就没有重传重传机制——不可靠
3、面向数据报:一次发多少接收方就要读多少
缓冲区
发送缓冲区:UDP没有真正意义上的发送缓冲区. 调用sendto会直接交给内核, 由内核将数据传给网络层协议进行后续的传输动作;
接收缓冲区:UDP具有接收缓冲区. 但是这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一致; 如果缓冲区满了, 再到达的UDP数据就会被丢弃;——不可靠
TCP控制协议
可以对数据的传输进行详细的控制——提高可靠性的同时,即使工作繁杂但任然在提高效率。
既然要对传输详细的管控那么其结构一定是要有多个标识数据来实现的,所以结构一定是复杂的。
报头和有效载荷如何分离:TCP报头:标准报头长度(20)+选项+有效载荷——那么如何分离呢?
4位首部长度就可以表示出报头的大小
但是有效载荷是多大呢??不要忘了TCP包的大小其实就是IP协议的有效载荷的大小:
所以有效载荷=ip有效载荷的大小-TCP报头总大小
缓冲区
TCP的发送端和接收端都有缓冲区
内核的全双工确认应答机制:
在收报文后,把报文放当接收缓冲区内核就会给发送端发送确认收到的应答报文
——保证了可靠性(只要收到应答就能保证历史最近一条消息接受方是收到的)
TCP数据发送的方式:
基本理解:发送一个应答一个
但其实实际中是发送和应答是重叠的——保证可靠性的同时提升效率
建立连接—三次握手(捎带应答)
TCP双发想要通信的话首先就要建立连接,三次握手建立连接方式可以
1、最小成本验证全双工——双方都可以确认可以向对方收发报文。
2、奇数次握手,保证了一定是客户端先把链接建立好后服务器才会建立连接,防止恶意的客户端不断的让服务器建立连接,自己又不建立连接,服务器要把建立的链接进行管理的如果建立的链接太多的话就会造成服务器卡顿。
超时重传
断开连接——四次回首
滑动窗口+丢包
窗口大小指的是无需等待确认应答而可以继续发送数据的最大值
滑动窗口是发送缓冲区的一个区域,里面有要发送的数据
滑动窗口的大小在连接刚建立成功是由接收端的窗口大小决定,之后如果发生了网络拥塞就由拥塞控制大小
滑动窗口的最大值并不止是16位那么大,他还有一个窗口扩大因子M,来左移M个数的大小。
如上图:发送前四个段的时候, 不需要等待任何ACK, 直接发送;
收到第一个ACK后, 滑动窗口向后移动, 继续发送第五个段的数据; 依次类推;
操作系统内核为了维护这个滑动窗口, 需要开辟 发送缓冲区 来记录当前还有哪些数据没有应答; 只有确认应答过的数据, 才能从缓冲区删掉;
窗口越大, 则网络的吞吐率就越高;
那么如果出现了丢包, 如何进行重传? 这里分两种情
情况1:数据包没有丢失,只是确认丢失,
情况二:数据包直接丢失
流量控制:
接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 “窗口大小” 字段, 通过ACK端通知发送端;
窗口大小字段越大, 说明网络的吞吐量越高;
接收端一旦发现自己的缓冲区快满了, 就会将窗口大小设置成一个更小的值通知给发送端;
发送端接受到这个窗口之后, 就会减慢自己的发送速度(窗口变小了,每次不用等待ACK应答的段变少了,所以消耗的时间就多了,速度就变慢了)
如果接收端的缓冲区满了,ACK回来的滑动窗口就是0,此时发送端就停止发送数据,而是定期发送一个窗口探测数据段,使接收端把当前窗口的大小告诉发送端。
网络拥塞:
网络和中的数据太多或者网络的环境太差,造成我们发送的大量的数据都得不到应答(出现大量丢包),此时我们采用的方案就不是超时重传了。而是拥塞控制——控制滑动窗口的大小——控制每次发送数据大量,通过指数增长式的试探,当增长到某个特定值后就加法增长。
如果网络非常好,那么此时发送数据的大小就由对方的接收能力决定(滑动窗口),如果网络拥塞,就要用拥塞窗口决定(拥塞避免算法)
滑动窗口大小=min(拥塞窗口大小,对方接收能力)
拥塞避免算法:指数级(慢开始,快增长)当到达一定阈(yu)值时再线性增长,当检测到网络拥堵(拥堵窗口大小)时,就不用此值/2得到下次拥塞算法的阈值。再此过程中窗口大小任然要遵循min(拥塞窗口大小,对方接收能力)
其实在TCP进行网络数据发送的时候,斌不并不是一开始就根据对法接收能力发送大量数据的,而是就是根据这种拥塞算法来发送数据的
注意:min(拥塞窗口大小,对方接收能力),如果拥塞窗口大小>对方接收能力,那么就说明此时的网络状态是正常的。
延时应答(想给发送端应答一个大一点的滑动窗口)
接收端收到数据后不忙给客户端发送ACK通告自己的滑动窗口,而是等一等,如果接收端在这段时间把数据处理了,那么接收缓冲区能接受的能力就越大,应答回去的滑动窗口就越大,对方的发送速度就越快了。
等多久:
1:等待接收到N个包就应答一次(常用)
2:等待最大延时时间就应答一次(最大应答时间(200ms)<超时重传时间(500ms))
面向字节流
调用write时, 数据会先写入发送缓冲区中; 如果发送的字节数太长, 会被拆分成多个TCP的数据包发出; 如果发送的字节数太短,
就会先在缓冲区里等待, 等到缓冲区长度差不多了, 或者其他合适的时机发送出去; 接收数据的时候,
数据也是从网卡驱动程序到达内核的接收缓冲区; 然后应用程序可以调用read从接收缓冲区拿数据; 另一方面, TCP的一个连接,
既有发送缓冲区, 也有接收缓冲区, 那么对于这一个连接, 既可以读数据, 也可以写数据. 这个概念叫做全双工 写100个字节数据时,
可以调用一次write写100个字节, 也可以调用100次write, 每次写一个字节; 读100个字节数据时,
也完全不需要考虑写的时候是怎么写的, 既可以一次read 100个字节, 也可以一次read一个字节, 重复100次;
粘包问题
数据的拆分在应用层实现——http/https协议——解决粘包问题
因为TCP是面向字节流的,所以发送过来的数据包之间是粘在一起的,但是他们的顺序是有序的(因为有序号这样的字段),所以我们http就要解决包与包之间的分离,报头和数据之间的分离——对于定长报头,通过报头中的len字段就可以确定这个包有多大,然后根据/n/r 空格就实现了报头和数据之间的分离,这样就解决了粘包问题和向上的反序列化
解决粘包问题:
1,包与包之间通过特殊字符分割
2.定长报文,包与包的大小一样
3,报头+自描述字段(len)__报头的大小是定长的——(我们常用)
TCP异常
如果在双方网络通信的过程中,突然进程挂了的话,内核(操作系统会自动做四次回首关闭连接(socket套接字文件))——进程终止、机器重启、
如果网线突然被切断的话,服务器是来不及做四次回首断开连接的,那么就需要他每隔一段时间就去询问一下客户端还活着吗(报活/心跳机制),如果客户端一直没有答复那么就会自动关闭连接
listen(socket,default_backlog)的第二个参数default_backlog
创建服务器:
1.创建套接字
2、绑定套接字信息
3、把套接字设置为监听套接字
4、获取连接
如果我们把listen的第二个参数设置为1,然后不去调用accept获取连接的话,
此时有客户端的链接请求的话,服务器的内核是会给我们维护一个全连接队列的,虽然我们上层并没有获取连接,但是在队列中是已经帮我们保存了链接,并和客户端链接上的。全连接队列能够保存链接的个数=listen的第二个参数值+1
那么这样做的作用:在服务器很忙的时候来不及accept(),但是链接请求又来了,那么就先保存链接好。——(饭店吃饭的例子)
问:服务器不获取连接就不会与客户端建立连接吗?
答:否,要看Listen的第二个参数值决定的全连接队列