源端口:数据从哪里来
目的端口:数据送到哪里去
报文长度:在传输数据时,单个报文段的最大长度
校验和:为保障数据正确性
注意:以上4个均为两字节,报文最大不能超过65536。
如果真的要传输一个数据量大的信息,UDP如何处理?
为什么要有校验和,没有行不行?
不行,首先校验和是为了保证数据的正确性,网络在传输时是不稳定的,传输使用的是电信号,电信号用0或1表示,当由于外界的影响导致电信号0->1或1->0是不可避免的。发送方把载核通过校验和算法计算出sum1,发送方把数据发送到接收方,当接受方收到数据时也采用相同的校验和算法计算出sum2,此时比较sum1和sum2的值是否相等判断数据发送的正确性。
TCP不是说,100%能够传过去,当传不过去的时候发送方知道自己没传过去,也就是说在接受方,收到或者没收到,会有个应答,此机制称为确认应答(ACK),实现可靠性的最核心机制!
一切顺利:使用确认应答保证可靠性
出现丢包,使用超时重传作为补充
当发送消息时还会产生后发先到的情况,为解决此问题我们可以添加编号(序列号)方式来确认,此时就引入了序号和确认序号。TCP针对每个字节进行编号注意,确认序号的规则,不是说,发送方的序号是啥就是啥,而是取得是发送方发过来的所有数据,最后一个字节的下一个字节的序号作为确认序号。确认序号的含义:小于确认序号的数据,我已收到。此时发送方就能知道那些数据接收到了。
TCP在传输时,因为面向字节流传输,一条完整数据分成每部分,每个部分数据走的路径不一样就会导致后发先到,TCP有一个接收缓冲区,TCP可以按序号针对收到的消息进行整队(这也是TCP序号的一个重要用途)。此时应用程序读数据,读到的一定是有序的了(和发送顺序一样)。
比如你正在打游戏此时画面卡成ppt,也就是说在传输数据时丢失了。丢包是指在网络传输过程中,数据包因为各种原因没有正确地到达目标地点,而丢失的现象。
在传输数据时,利用交换机和路由器每个设备都在承担很多转发任务,每个设备的转发能力是有上限的,当某一时刻某个设备流量达到峰值此时会引起丢包。
如果包丢了,此时接受方就接收不到了,也就不会返回ACK,发送方就迟迟拿不到应答报文,此时等待一段时间还没收到,则认为是丢包。此时就会重新发一遍也称为“超时重传”。
发送方对于丢包的判定,一段时间内没有收到ACK
由于发送方区分不了这两种情况,所以只能都重传。
当是第二种情况数据重发是非常严重的,但是TCP非常好的处理了此问题,它会在接收缓冲区中根据收到的数据的序号,自动去重。保证应用程序读到的数据只有一份。重传后的数据也是会导致丢包的,一旦连续丢包这种情况很大的可能是网络出现了严重的问题。
TCP针对多个包丢失,每次丢包超时等待时间就会变长(重传的频率变低了),连连续多次重传,都无法得到ACK,此时TCP就会尝试重置连接,如果重置连接也失效,TCP就会关闭连接,放弃网络通信。
syn:称为同步报文段,一方向另一方申请建立连接
ack:应答信号
因为上图中间部分服务器向客户端发送的ack和syn可以合并成一个。
三次握手这个过程,验证了客服端和服务器,个资的发送能力和接收能力是否正常,也为可靠性做出了一份力。
可以,中间部分服务器向客户端发送的ack和syn可以分开成两个分别发送,也就是四次握手。但是效率低于三次握手。
通信双方,各自给对方发送一个FIN(结束报文),在各自给对方返回ACK。
ACK和FIN有一定概率合并成一个,但通常情况下不能合并。
三次握手:ACK和SYN是同一时机触发的(都是由内核完成的)
四次挥手:ACK和FIN是不同时机触发,ACK是内核完成的,会在收到FIN的时候第一时间返回,FIN则是应用程序代码控制,再调用socket的close方法的时候才会触发FIN,close的执行时机可能是很久也可能是立即取决于代码怎么写。
客户端进程结束了,但是TCP还会把TCP连接维护,直到四次挥手结束。
想要提高效率,减少等待时间,可以采用批量发送(一次发多条数据,收到一个ACK就可以直接发下一条数据)。批量不是无限发送,是发送到一定程度,就等待ack,不等待直接发送的数据量是有上限的,而且是回来一个ack就立即发送下一条,相当于总的要批量等待的数据是一致的(滑动窗口大小)。
批量发送了四条数据,就等待着四个ack,白色区域就是等待窗口,当收到2001的ack就意味着1001~2000的这个数据得到了确认,此时就会立即发送5001~6000这个数据。
这个图中相当于一半的ack都丢了,相当高的丢包率,这种情况啥事没有,对于可靠性没有影响,因为通过后一个ack能告诉我们前面ack虽然是丢了但是我收到了。若是程序 最后一个数据的ack丢失我们采用超时重传。
由于1001~2000的数据丢了,但是接收方依然索要的是1001的数据,而不是收到了2001~3000的数据,返回3001。当A收到多个索要1001这个数据,1A就重传此数据,当B收到了1001~2000的数据,B的返回ACK确认序号是7001,因为之前的数据我已经收到了。上述重传过程,丢了数据才重传,不丢不重传,整体速度比较快,重传过程也称为快速重传
滑动窗口和快速重传,是在大量数据的时采取的措施。
接收到的数据,先放到接收缓冲区,接下来应用程序通过socket里的InputStream来读了,代码中读出来的数据就从接收缓冲区删除了。
首先并不是窗口越大越好,发的快会瞬间吧接收缓冲区打满,接下来发送的数据,就会丢包,这种情况得不偿失,还不如慢点。
通过流量控制,本质就是限制发送方的速度,ack报文里携带窗口大小这样的字段,这里写的值是建议发送方发送的窗口大小,直接拿接收缓冲区的剩余空间作为窗口大小。当缓冲区满了之后就暂停发送,但是每隔一段时间触发一个窗口探测报文。
拥塞控制:衡量中间节点的传输能力,通过实验的方式,按照小的速率发送若不丢包则提高速率(扩大窗口大小),如果出现丢包,立即把速率调小,重复上述过程。此过程又称为动态平衡。
刚开始回给一个小的窗口值,每次翻倍式增长,当到一定阀值就会线性增长,当出现丢包就认为到达上限,此时又将窗口设成一个比较小的值。
实际发送方的窗口大小=min(流量控制窗口,拥塞控制窗口)
延时等待:接受方不立即发送ACK,而是等待一会再发送从而提高效率。
假设立即返回的ACK中带有的窗口大小为n,如果稍微等待一下,在返回(这个过程中应用程序也在读取缓冲区中的数据)此时再返回的ACK,窗口大概率比立即返回的窗口大,此时就效率提升了。
A发送请求,B立即做出确认应答,当B发送请求通过write写数据,通过一些代码执行到才返回,这俩时机不同,但是要是延时等待,就很有可能将ack和请求合并成一个发送。这也就是为什么四次挥手三次挥完的原因。
当A向给B连续发送多个应用层数据报,这些数据就都紧紧挤在B的缓冲区中,此时B应用程序在读数据的时候,就难以区分一个完整的应用层数据报,很容易读出半个报,多个报的情况。
解决方案:
1.定义分隔符,每一个数据报结尾以某个特定字符结尾
2.约定前四个字节,表示整个数据报长度。
进程没了,socket是文件,随之被关闭,但是连接还在仍然可以继续四次挥手。
先杀死所有进程,也会触发四次挥手,如果挥完更好,如果没挥完会对端进行重传fin,重传几次都没有ACK,此时尝试重连,如果还不行,直接进行释放连接
瞬间机器关了,来不及做任何挥手操作。
如果对方是发送方,对端就收不到ACK,此时进行超时重传=》重置连接=》释放连接。
如果对方是接收方,此时对端无法知道是否机器挂了,此时TCP内置心跳包(保活机制),发送方定期发生心跳包。
绝大部分情况下都可以使用TCP
对于效率要求较高,但是对于不可靠性要求不高的情况下,如同一个机房内网之间的数据传输。