TCP+IP的深入介绍

简介

TCP协议出现的目的就是在不可靠的IP通信协议上加上一层稳定的通信协议。1981年rfc791,rfc793制定的。可以说一个非常的古老的协议了。另外,HTTP并非必须建立在TCP上,也可以实现在UDP上,但是需要在应用层自己处理丢包,乱序等问题。所以基本没人这么搞。TCP的主要问题是过度设计,导致效率有一定问题。

TCP和UDP在IP协议的基础上增加了一个端口设定。注意,TCP和UDP可以绑定同一个端口

1024-65535为用户端口,又分为: BSD临时端口(1024-5000)和BSD服务器(非特权)端口(5001-65535).
0-1023: BSD保留端口,也叫系统端口,这些端口只有系统特许的进程才能使用;
1024-5000: BSD临时端口,一般的应用程序使用1024到4999来进行通讯;
5001-65535: BSD服务器(非特权)端口,用来给用户自定义端口.

一个TCP/UDP连接的四元组如下

Client_IP1:Client_Port1 <---> Server_IP:Server_Port

操作系统可以根据上面四元组不同将一个连接绑定到一个句柄供应用读取和写入。

IP地址

在这里插入图片描述
D类IP地址为组播地址,E类IP地址为实验性地址。
对于一般用路由器的家庭,你PC上的地址是一个C类的私有地址。路由器上显示的WAN地址则是一个A类或者是B类的私有地址。而你实际上百度自己IP得到的地址,才是A B C类中的非私有地址。因为目前IPV4目前的地址肯定是不够用的。因为你的wan ip和公网IP都是ISP随机分配的,所以在自己家搭网站相当于一个流动商贩,人家根本找不到你。

TCP的三次握手

在这里插入图片描述

TCP为什么需要三次握手

在《计算机网络》一书中其中有提到,三次握手的目的是“为了防止已经失效的连接请求报文段突然又传到服务端,因而产生错误”,这种情况是:一端(client)A发出去的第一个连接请求报文并没有丢失,而是因为某些未知的原因在某个网络节点上发生滞留,导致延迟到连接释放以后的某个时间才到达另一端(server)B。本来这是一个早已失效的报文段,但是B收到此失效的报文之后,会误认为是A再次发出的一个新的连接请求,于是B端就向A又发出确认报文,表示同意建立连接。如果不采用“三次握手”,那么只要B端发出确认报文就会认为新的连接已经建立了,但是A端并没有发出建立连接的请求,因此不会去向B端发送数据,B端没有收到数据就会一直等待,这样B端就会白白浪费掉很多资源。如果采用“三次握手”的话就不会出现这种情况,B端收到一个过时失效的报文段之后,向A端发出确认,此时A并没有要求建立连接,所以就不会向B端发送确认,这个时候B端也能够知道连接没有建立

TCP的四次挥手

在这里插入图片描述
挥手过程任何一方都可以主动发起。要求断开连接的一方必然已经没有要主动发送的事情了。所以,他现在需要的就是等待对方把没说完的化说完,然后等待对方提供终止,然后等待一段时间后终止。

如果是客户端发起。
在这里插入图片描述
注意2MSL可不是说的2ms。而是Maximum Segment Lifetime.TCP报文在网络中最长存活时间。跟IP协议的TTL的概念类似。RFC规范中要求该值是2分钟,要就是要等四分钟。这个确实优点恐怖。等待这么长时间的理由有以下两个

1、为了保证客户端发送的最后一个ACK报文段能够到达服务器。因为这个ACK有可能丢失,从而导致处在LAST-ACK状态的服务器收不到对FIN-ACK的确认报文。服务器会超时重传这个FIN-ACK,接着客户端再重传一次确认,重新启动时间等待计时器。最后客户端和服务器都能正常的关闭。假设客户端不等待2MSL,而是在发送完ACK之后直接释放关闭,一但这个ACK丢失的话,服务器就无法正常的进入关闭连接状态。

2、他还可以防止已失效的报文段。客户端在发送最后一个ACK之后,再经过经过2MSL,就可以使本链接持续时间内所产生的所有报文段都从网络中消失。从保证在关闭连接后不会有还在网络中滞留的报文段去骚扰服务器。

一般而言主动断开连接的是客户端,所以进入TIME-WAIT状态的也是客户端。

如果服务器端也发生了TIME-WAIT,服务端为了解决这个TIME_WAIT问题,可选择的方式有三种。

  • 保证由客户端主动发起关闭(即做为B端)
  • 关闭的时候使用RST的方式
  • 对处于TIME_WAIT状态的TCP允许重用。

在这里插入图片描述

一个完整的TCP状态机如下

在这里插入图片描述

  • 被动打开的意思不是指外部可以唤醒一个端口从CLOSED到LISTEN状态,而是指打开端口用于接受连接。主动打开的意思是打开端口用于发起连接。

TCP序列号

双方都会建立自己的seq序列号。双方的seq序列号没有关联。确定初始的seq号后,下一次发送的seq号等于上次的seq号+上次报文。接到对方的报文,回复ack中,所携带的ack序列号是上次对方发过来的seq号+上次报文的长度。上两个数是一致的。

报文

数据格式如下
在这里插入图片描述
IP报文如下
在这里插入图片描述

数据大小问题

在链路层,由以太网的物理特性决定了数据帧的长度为(46+18)-(1500+18),其中的18是数据帧的头和尾,也就是说数据帧的内容最大为1500(不包括帧头和帧尾),即MTU(Maximum Transmission Unit)为1500;  
在网络层,因为IP包的首部要占用20字节,所以这的MTU为1500-20=1480; 
在传输层,对于UDP包的首部要占用8字节,所以这的MTU为1480-8=1472;   
所以,在应用层,你的Data最大长度为1472。当我们的UDP包中的数据多于MTU(1472)时,发送方的IP层需要分片fragmentation进行传输,而在接收方IP层则需要进行数据报重组,由于UDP是不可靠的传输协议,如果分片丢失导致重组失败,将导致UDP数据包被丢弃。  
从上面的分析来看,在普通的局域网环境下,UDP的数据最大为1472字节最好(避免分片重组)。   
类似的TCP 包的大小就应该是 1500 - IP头(20) - TCP头(20) = 1460 (Bytes)
但在网络编程中,Internet中的路由器可能有设置成不同的值(小于默认值),Internet上的标准MTU值为576,所以Internet的UDP编程时数据长度最好在576-20-8=548字节以内。
物理设备碰见超过自己的MTU的数据包,就直接会丢弃。所以,TCP必须要协商MSS(Maximum Segment Size,最大报文长度)。
最古老的策略是PMTUD+ICMP
在这里插入图片描述
思科给出的解决方案是
在这里插入图片描述

拆包粘包

TCP是个“流”协议,所谓流,就是没有界限的一串数据。大家可以想想河里的流水,是连成一片的,其间并没有分界线。TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包的划分,所以在业务上认为,一个完整的包可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这就是所谓的TCP粘包和拆包问题。
拆包,粘包

  1. 要发送的数据大于TCP发送缓冲区剩余空间大小,将会发生拆包。
  2. 待发送数据大于MSS(最大报文长度),TCP在传输前将进行拆包。
  3. 要发送的数据小于TCP发送缓冲区的大小,TCP将多次写入缓冲区的数据一次发送出去,将会发生粘包。
  4. 接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包。

滑动窗口机制

在这里插入图片描述

Delay ACK机制

一个简单的本地和远程server的命令行交互的过程是这样的。本地发送要输入的字节,服务器响应字节的确认。服务其发送要求回显的字节,本地对回显字节进行确认。为了提高效率,远程server可以把对字节的确认和发送的数据字节合并为一条报文。这种机制被称为延时确认机制。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值