目录
一、再谈端口号
1、端口号范围划分
2、相关指令
二、UDP协议
1、udp协议格式
2、udp的特点
4、udp使用注意事项及基于udp的应用层协议
三、TCP协议
1、tcp协议格式
2、tcp报头解释
3、TCP确认应答机制
4、TCP超时重传机制
5、tcp三次握手四次挥手
6、滑动窗口
7、流量控制
8、拥塞控制
端口号用来唯一标识一台主机上通信的进程,在tcp协议中用“源ip”、“源端口”、“目的IP”、“目的端口”、“协议号”这样一个五元组来标识一个通信。
1024 - 65535: 操作系统动态分配的端口号. 客户端程序的端口号, 就是由操作系统从这个范围分配的.
一些知名端口号:
1)查看知名端口号
cat /etc/services
2)netstat查看网络状况
netstat选项
- n:拒绝显示别名,能显示数字的全部转化成数字。
- l:列出所有在listen服务状态
- p:显示建立相关链接的程序名
- t:只显示tcp相关选项
- u:只显示udp相关选项
- a:显示所有选项,默认不显示listen相关
3)查看服务器的进程id--pidof
pidof 进程名
思考1:一个进程可以bind多个端口号?
可以,一个进程可以绑定多个端口号,通过这些端口号都可以找到这个进程。
思考2:一个端口号可以被多个进程bind?
不可以,端口号的作用是唯一标识一台主机上的具体某一个进程,如果多个进程都绑定一个端口号就失去了端口号的意义。
用户数据报协议(udp)是工作在传输层的一个重要协议,UDP 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据包的方法。
思考1:上层协议是如何将udp协议的报头和有效载荷(数据)分离?
udp的报头是一个8字节的定长报头,当拿到一个udp数据报是读取到的前8个字节就是报头,报头中包含一个16位的udp长度字段,通过该字段计算出udp有效载荷大小。这样就将udp报头和有效载荷成功分离。
思考2:udp协议最多可以传送多少数据?
注意:16位udp长度可以认为是一个unsigned类型,且16位udp长度包括报头的8字节。
16位udp长度最多可以表示65535个字节,而报头占8字节,因此udp有效载荷最大长度为65535-8=65527字节。
思考3:udp协议如何保证用户数据报的完整性?
udp称为用户数据报协议,因此一定要保证用户数据报的完整性,具体做法就是8字节报头+16位丷长度来计算有效载荷长度,从而确保上层协议拿到的是一个完整的数据报。
思考4:16位检验和有什么作用?
udp是一个无连接不可靠的传输层协议,它的检验和只是用来检验数据本身是否出错。由于udp的不可靠性,一旦检测到检验和出错,udp协议直接将该数据包丢弃。
总结:udp协议通过协议包头中的16位的源端口和16位的目的端口直到自己“从哪来,到哪去”,通过16位udp长度直到自己携带了多大的数据报,并且可以通过这16位udp长度将报头和有效载荷进行分离。还有一个16位的检验和,用来验证数据报是否出错,如果出错直接丢弃。
udp协议有以下三个主要特点:
其次udp还有以下特点:
udp的缓冲区:
udp是全双工的,同一时间可以同时进行读和写数据,但是如果数据量较大时就有可能导致数据处理不过来,因此需要缓冲区将数据存起来。
udp可以传输的数据大小为2^16字节(64k),在当今的互联网环境下是一个非常小的数字,因此如果需要传送大于64k的数据就需要在应用层手动分包多次发送,并在接收端手动拼接。
基于udp的应用层协议:
TCP全称为传输控制协议,主要功能是对数据的传输进行详细的控制。
tcp协议的报头是不定长的,报头的首部有一个4位字段表示报头的长度,具体表示的是该tcp 报头有多少个32bit(4字节),也就是说规定了tcp报头最大长度为(2^4-1)*4 = 60字节。由于tcp传输的是字节流,因此tcp的有效载荷可以是任意长度的。
同样的,头部8个字节源端口和8个字节目的端口表示数据“从哪来到哪去”。由于tcp是面向连接的可靠传输,因此在tcp的头部还有很多字段用来确保数据的可靠传输(后面详细讲)。
URG:紧急指针是否有效
ACK:确认号是否有效
PSH:当接收端缓冲区满时,且一直没有拿走数据,发送端会发送一个PSH标志位为1的数据,提示接收端应用快速将缓冲区的数据取走。
PST:重新建立连接,携带PST标志位的报文称为复位报文段。
SYN:请求建立连接,携带SYN标志位的报文称为同步报文段。
FIN:关闭连接,携带FIN标志位的报文称为结束报文段。
tcp进行的是可靠传输,传输的是字节流。因此,对于tcp协议需要做到以下两点:①确保接收端成功接收到数据②确保接收端接收到数据的有序性。
网络中存在许多复杂的情况,因此并不能保证接收端接收到的数据的顺序就是发送端发送的顺序,因此tcp协议需要确保接收端接收到数据后能够按照正确的顺序对数据进行处理。
确认应答机制:发送方每发送一个数据,接收方就要向发送方发送一个确认信号,表明自己已经收到该数据,从而确保接收端成功接收到数据。
思考:tcp 是如何实现确认应答机制并且确保接收到的数据的顺序的?
在tcp报头中,有两个4字节的字段,分别表示的是32位序号和32位确认序号。tcp协议对将每个字节的数据都进行了编号,这个编号就是序号。在发送数据时会将数据的编号放在报头的32位序号中一起发送过去,接收端接收到数据后会发送一个包含32位确认序号的数据,32位确认序号表示的意思是已经接受度奥x号数据期望收到x+1号数据。例如,上图中发送端发送1~1000号数据,接收端接收到会给发送端发送确认号为1001的确认数据,表示自己已将将1000之前的所有数据都接收到了,希望下次接收到的是1001号数据。这样,tcp协议就完美的实现了确认应答机制。同时tcp协议还可以通过32位序号将接收到的序号按照发送顺序进行处理,保证数据的顺序不会出错。
注意:确认应答机制永远也不能对所有的数据进行确认,总是会有最后一条数据无法确认。因此确认应答机制并不对确认数据进行确认。
tcp协议在保证数据可靠传输是,还需要确保丢失的数据能够重新发送。因为,在主机A发送给主机B的数据可能由于网络原因导致数据发送失败,因此主机A也就收不到主机B的确认。在tcp协议中,如果主机A给主机B发送一个数据,在规定时间内主机A没有收到数据B的确认,主机A 就认为该数据丢失了(实际上不一定是丢失了,有可能是网络拥堵数据正在发送),会将该数据重新发送给主机B,这就是超时重传机制。
无论是数据发送失败(接收端确实没有接收到数据),还是数据还没发送过去或者只是确认数据发生丢包(接收端接收到了数据),一旦超时发送端都会将该数据重新进行发送,那么一定会导致接收端存在很多重复的数据,那么该如何设置超时时间呢?
最理想的情况就是找一个最小时间,确保数据在这个时间内一定能够到达。但是这个时间的长短随着网络环境的变化,是不同的。如果时间设置过长会影响传输速率,如果时间设置过短有可能会频繁发送重复的包。
TCP为了保证无论在任何情况下都能比较高性能的通信,会动态计算这个时间。在linux中超时时间以500ms为一个单位,每次判断超时重发的时间都是500ms的整数倍。如果重发一次仍然得不到应答,会等待500*2ms后进行重传,如果还是不能得到应答则在等待4*500ms重发,以此类推。当累计到一定次数,tcp会认为对端或者网络出现异常,直接强制断开连接。
三次握手过程
1)如何理解连接
对于服务器来说,服务器是1对多的,一个服务器可能要被很多个客户端连接。因此,服务器端需要将众多的连接进行管理,这就导致服务器得为这些连接建立相应的数据结构,同时这些操作也是需要时间的。所以,建立连接是需要时间+空间代价的。
2)三次握手过程
3)为什么是三次握手?
四次挥手过程
1)为什么是四次挥手
断开连接是需要上方同时建立起断开连接状态的,因此需要互相请求和确认,一共就是四次挥手。
2)四次挥手的过程
理解TIME_WAIT和CLOSE_WAIT状态
1)TIME_WAIT状态
为什么客户端给服务器端发完ACK确认后,没有立刻进入CLOSED状态?
对于客户端的最后一次ACK确认,服务器端不会在进行确认,如果客户端立刻进入CLOSED状态,有可能导致服务器端并没有接收到ACK确认,对FIN进行超时重传,但是此时客户端已经断开连接,服务器端是一直收不到客户端确认的,只能等到重传次数到达一定程度强制断开连接。因此,在客户端发送完确认后会等待一段时间,如果在改时间内没有收到服务器端重传的FIN数据就会断开连接,否则对重传的FIN重新进行确认,确保服务器端收到并断开连接。
其次,客户端发送完ACK确认进入TIME_WAIT状态,还有一个重要作用就是防止网络中没有传完,保证历史数据在网络中消散。
2)如何验证TIME_WAIT状态?
set|socktop接口,即使㝉端口处于监听状态也可以进行绑定。
3)CLOSED_WAIT状态
首先屏蔽服务器daunt程序中关闭文件描述符的代码,启动服务器,让客户端进行链接,客户端连接成功后关掉客户端,此时客户端就想服务器端发送了断开连接的请求,但是由于服务器端屏蔽了close,无法断开连接。此时,查看服务器状态就为CLOSE_WAIT状态。
前面讨论的确认应答策略是基于“一次发送,一次确认”,经过“一次发送,一次确认”完成后才能进行下一次发送,这样数据的传输效率会很低。
但是,确认应答机制很好的保证了数传输的安全性,为了解决其传输效率问题,又引入滑动窗口。华东窗口是指,可以一次性发送一批数据,这一批数据发送的过程不需要等待其中某一个数据的确认,窗口大小就是指这批数据的最大值。
如下图:
新的技术的引入,必然会带来一些新的问题和挑战,滑动窗口虽然解决了数据传输效率低问题,那么如果数据丢包又该如何重传呢?
其实,滑动窗口机制已经很好的解决了这个问题,滑动窗口中是可以容忍部分ACK丢包的,只要后续ACK进行确认即可(ACK确认序号表示的是该序号之前的所有数据都已经收到,接下来向收到的数据的序号)。例如,上图中1001的ACK丢了并不影响,只要后边任意一个到达都表示1~1000的收据已经收到。
当数据包真正丢失了,是需要进行重传的,这也就要求滑动窗口中发送的ACK要确保他之前的数据都已经接收到。例如,接收端接收到的数据的顺序是1~1000、5001~6000、4001~5000,当接收到5001~6000的数据后不能立即发送6001的ACK,必须等到6001之前的数据全部到达才能发送。
其次,滑动窗口的重传机制和之前的超时重传机制也有所不同。滑动窗口中,如果某一个数据真的丢了(超过时间没有收到),接收方会给发送方发送的每一个不是丢失的数据发送的确认号都是丢失数据的确认号。当接收端接收到三次这个ACK时,会重新对这个ACK的数据进行重新发送。这种机制叫做高速重发机制,也称快重传。
例如:
思考1:有了快重传,为什么还需要超时重传
其实,超时重传是一种兜底策略。试想,如果所发送的数据小于3次时,即使数据丢失也引发不了块重传,此时还是需要超时重传进行解决。同时,如果是最后一个数据丢了,也是无法引发快重传的。
注意:实际上接收端和发送端第一次“协商”窗口大小是在三次我说的额过程中。三次握手建立连接时,SYN包中就包含了窗口大小。
为了解决这个问题,引入了拥塞窗口的概念。开始发送数据的时候,拥塞窗口定义为1,每收到一个ACK拥塞窗口+1,每次发送数据的时候将拥塞窗口和主机端反馈的窗口大小做比较,取最小的作为实际发送的窗口大小。
但是,这样的拥塞窗口增长速度是指数级别的,。慢启动只是启动时速度慢,但是增长速度非常快。为了不增长的那么快, 因此不能使拥塞窗口单纯的加倍. 此处引入一个叫做慢启动的阈值。当拥塞窗口超过这个阈值的时候, 不再按照指数方式增长, 而是按照线性方式增长。