端口号范围划分
0-1023:知名端口号,HTTP,FTP,SSH等这些广为使用的应用层协议,他们的端口号都是固定的
1024-65535 操作系统动态分配的端口号,客户端程序的端口号,就是由操作系统从这个范围分配的。
认识知名端口号
- SSH服务器,使用22端口
- ftp服务器,使用21端口
- Telnet服务器,使用23端口
- http服务器,使用80端口
- https服务器,使用443
netstat
- n拒绝显示别名,能显示数字的全部转化为数字
- l 仅列出有在listen的服务状态
- p 显示建立相关连接的程序名
- t 仅显示tcp相关信息
- u 仅显示udp相关信息
- a 显示所有的选项,默认不显示listen相关
- pidof查看服务器的进程ID
UDP协议
- 16位源端口号 和16位目的端口号
- 16位UDP长度 和16位校验和
- 16位UDP长度,表示整个数据报(UDP首部+UDP数据)的最大长度
- 如果校验和出错,就会直接丢弃
UDP的特点
- 无连接:知道对端的IP和端口号就直接进行传输,不需要建立连接
- 不可靠:没有确认机制,没有重传机制,如果因为网络故障该段无法发到对方,UDP协议层也不会给应用层返回任何错误信息
- 面向数据报:不能够灵活的控制读写数据的次数和数量
面向数据报
应用层交给UDP多长的报文,UDP原样发送,既不会拆分,也不会合并
UDP的缓冲区
- UDP没有真正意义上的发送缓冲区,调用sendto会直接交给内核,由内核将数据传给网络层协议进行后续的传输动作
- UDP具有接受缓冲区,但是这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一致,如果缓冲区满了,再到达的UDP数据就会被丢弃
- 操作系统把数据给网卡(硬件),然后触发中断(大端CPU当前正在执行的东西)
UDP使用注意事项
- UDP协议首部有一个16位的最大长度,也就是说一个UDP能传输的数据最大长度是64K(包含UDP首部)
- 如果我们需要传输的数据超过64K,就需要在应用层手动的分包,多次发送,并在接收端手动拼装
TCP协议
- 16位源端口号,16位目的端口号
- 32位序号
- 32位确认序号
- 4位首部长度,保留6位状态码,16位窗口大小
16位校验和,16位紧急指针
源/目的端口号:表示数据从哪个进程来,到哪个进程去
- 32位序号/32位确认信号:序号有三重作用,我就列三条分别说明吧
- 有了序列号,我们就可以将字节流的数据一一编号,然后我们可以保证数据的顺序一致
- 序列号还可以去重,当确认报文丢失,服务器会以为发送的数据已经收到,但是客户端一直无法收到ACK,然后客户端就会不断发送重复报文,数据就会重复,但是有了序号服务器就会知道那些数据多发了,然后用相应的机制进行去重
- 32位确认序号就是每当序号比如1-1000收到,然后确认序号就设置为1001,保证之前的报文绝对不出错
- 4位TCP报头长度:表示该TCP头部有多少个32位bit(有多少个4字节),所以TCP头部最大长度是15*4 =60;
6位标志位
- URG:紧急指针,这是优先级,判断那段报文优先发送
- ACK:确认号是否有效
- PSH: 提示接收端应用程序立刻把从TCP缓冲区把数据读走
- RST: 对方要求重新建立连接,我们把携带RST标识的称为复位报文段(告知客户端建立连接失败,重新链接一下)
- SYN:请求建立连接,我们把携带SYN标识的称为同步报文段
- FIN:通知对方,本端要关闭了,我们称携带FIN标识的位结束报文段(当客户端长时间没有响应(发送数据报时)服务器任务客户端挂掉了,就会断开连接)因为服务器要维持连接的话需要消耗资源,对于这种暂时不用的资源,当然是选择挂掉,待他再次响应时重新进行连接)
- 16位窗口大小:就是发送方的接收缓冲区的大小,在进行三次握手的时候,服务器会和客户端协商窗口大小,得知对方的接收能力从而控制发送速度来避免出现阻塞
确认应答(ACK)机制
当我们启动服务器,然后启动客户端,然后关闭服务器,再立刻运行服务器,就会出现问题,原因如下
- 虽然server的应用程序终止了,但是TCP协议层的连接并没有完全断开,因此不能再次监听同样的server端口
- TCP协议规定,主动关闭连接的一方要处于TIME_WAIT状态,等待两个MSL的时间才能回到CLOSED状态
- 我们使用ctrl+C终止了服务器,所以服务器是主动关闭的一方,在TIME_WAIT期间仍然不能再次监听同样的服务器端口
- MSL在RFC1122中规定为两分钟,操作系统实现不同,Centos7默认配置的值是60s
- 可以通过 cat/proc/sys/net/ipv4/tcp_fin_timeout 查看msl值
为什么要等待两个MSL时间
- MSL是TCP报文的最大生存时间,因此TIME_WAIT持续存在2MSL的话,可以保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失(否则服务器立刻重启,可能会收到来自上一个进程的迟到的数据,但是这种数据很可能是错误的)
- 同时也是在理论上保证最后一个报文可靠到达(假设最后一个ACK丢失,那么服务器会重发一个FIN,这时虽然客户端的进程不在了,但是TCP的连接还在,仍然可以重发LAST-ack);
当连接数量很大,虽然存在时间很短,如果服务器主动关闭连接,就会造成大量的TIME_WAIT连接,导致服务器的端口不够用,无法处理新的连接,想一想,每个都要等两分钟,那么效率会很低
- 使用setsockopt()设置socket描述符的选项SO_REUSEADDR为1,表示允许创建端口号相同DNAIP地址不同的多个socket描述符
- 在server的socket()和bind()调用之间插入
int opt =1;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
- 一发一收的方式性能较低,那么我们一次发送多条数据,就可以大大提供性能(其实是将多个短的等待时间叠加在一起)
- 窗口大小就是无需等待确认应答而可以继续发送数据的最大值
- 收到第一个ACK之后,滑动窗口就开始向后移动,继续发送数据
- 操作系统为了维护这个滑动窗口,需要开辟发送缓冲区来记录当前还有那些数据没有应答,只有确认应答过的数据,才能从缓冲区删除
- 窗口越大,则网络的吞吐率就越高
超时重传机制
- 主机A发送数据给B之后,可能因为网络拥堵等原因,数据无法到达主机B
- 如果主机A在一个特定时间间隔内没有收到来自B发来的确认应答,就会进行重发
如果是B发过来的确认应答丢掉了,那么客户端就会以为数据没有发送过去,然后就会一直发送知道收到ACK为止。主机B收到很多重复的数据,那么TCP协议需要能够识别出那些包是重复的包,并且把重复的丢弃掉,就可以利用序列号进行去重工作
超时时间如何确定?
- 在最理想的情况下,找到一个最小的时间,保证确认应答一定能在这个时间内返回
- 根据网络状况会有差异
- 如果设置的太长,会影响整体的重传效率
- 设置太短,就会频繁发送重复的包
TCP为了保证无论在任何环境下都能比较高性能的通信,因此会动态计算这个最大超时时间
- Linux的超时以500ms为一个单位,如果重发一次仍然收不到应答,那就*2,一次类推,累积到一定的重传次数之后,TCP认为网络或者对端主机出现异常,强制关闭连接
- 如果发了10000个包,只是丢了五六个,默认重传,但是如果丢了八九千个,那肯定是网络出现了故障,果断断开连接
快重传机制
- 当某一段报文丢失之后,发送端会一直收到1001这样的ACK,如果发送端连续3次发送同样的1001的应答,就会将对应的数据重新发送
- 这个时候接收端收到了1001之后,再次返回的ACK就是7001了(因为2001-7000)接收端之前就已经收到了,被放到接收端操作系统内核的接收缓冲区内
流量控制
- 接收端处理数据的速度是有限的,如果发送端发的太快,导致接收端的缓冲区被放满,这个时候如果发送端继续发送,就会造成丢包,继而引起丢包重传等一系列的连锁反应
- 因此TCP支持根据接收端的处理能力,来决定发送端的发送速度
- 接收端将自己可以接收的缓冲区大小放入TCP首部中的窗口大小字段,通过ACK端通知发送端,窗口大小字段越大,说明网络的吞吐量越高
- 接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值通知给发送方
- 接收端收到这个窗口之后,就会减慢自己的发送速度
- 如果接收端缓冲区满了,就会将窗口设置为0,这时发送方不再发送数据,但是需要定期发送一个窗口探测数据段,使接收端把窗口大小告诉发送端。
拥塞控制
- 拥塞是由于网络状况不好,大量丢包重发导致的。如果网络不好,但是发送速度不变,就会出现网络拥塞,因此TCP引入一个慢启动机制,先发少量的数据,摸清当前的网络拥堵状态,再决定按照多大的速度传输数据
- 发送开始的时候,定义拥塞窗口大小为1,每次收到一个ACK应答,拥塞窗口加1
- ,每次发送数据报的时候,将拥塞窗口和接收端主机反馈的窗口大小作比较,取较小的值作为实际发送的窗口,这样一定可以避免面数据传输拥塞问题。
虽然叫做慢启动,但是那只是刚开始增长比较慢,到后面会呈现指数增长,增长速度太快了,为了改变这种状况,我们需要知道下面的原理
- 为了不增长的那么快,我们不能让拥塞窗口单纯的加倍
- 此处引入一个慢启动的阈值
- 当拥塞窗口超过这个阈值时,不再按照指数增长,而是按照线性增长
- 当TCP开始启动的时候,慢启动阈值等于窗口最大值
- 在每次超时重发的时候,慢启动阈值会变成原来的一半,同时拥塞窗口置为1
- 出现少量的丢包现象时,我们出发超时重传机制,但是出现大量的丢包时,我们就认为是网络拥塞引起的。
- TCP的这套机制是为了在保证速度的同时保证数据传输的安全性和可靠性。
延迟应答
- 如果接收数据的主机立刻返回ACK应答,这时候返回的窗口可能比较小
- 窗口越大,网络吞吐量就越大,传输效率就越高,我们的目标是在保证网络不拥塞的情况下尽量提高传输效率。
- 但是不是每个包都能延时应答,应该是隔几个包有一次应答,当超过最大延迟时间就应答一次
- 延迟应答的等待时间也是和网络状况有关的,因此是动态变化的。
粘包问题
- TCP的报文是按照顺序传输过来的
- 站在应用层的角度,看到的知识一串连续的字节数据
- 当应用程序看到了这么一连串的字节数据,就不知道从那部分开始到哪个部分是一个完整的应用层数据包
为了避免粘包问题,我们需要明确两个包之间的边界
- 对于定长的包,,保证每次都按照固定大小读取即可
- 对于变长的包,可以在包头的位置,约定一个包总长度的字段,从而就知道了包的结束位置
- 对于变长的包,还可以在包和包之间使用明确的分隔符
- 对于UDP来说不存在粘包问题
- UDP,如果还没有上层交付数据,UDP的报文长度仍然在,同时,UDP是一个一个把数据交付给应用层,就有很明确的数据边界
- 站在应用层的角度,使用UDP时,要么收到完整的UDP报文,要么不收不会出现半个的情况
TCP异常情况
- 进程终止:进程终止会释放文件描述符,仍然可以发送FIN,和正常关闭没有什么区别
- 机器重启:和进程终止的情况相同
- 机器掉电/网线断开:接收端认为连接还在,一旦接收端有写入操作,接收端发现连接不在了,就会进行reset,即使没有写入操作,TCP自己也内置了一个保活定时器,会定期询问对方是否还在,如果对方不在,也会把连接释放