确认应答
当数据到达接收主机时,接收端主机会返回一个确认应答:ACK
如果发送端一定时间内没有等到确认应答,认为数据丢失,并重发。
没有收到确认应答有可能是因为:数据丢失 或者 应答丢失
为了避免应答丢失时,接收端重复接收数据,需要能识别是否已接收数据、又能判断是否需要接收:按顺序给发送的数据的每个字节标上编号:序列号
接收端查询接收数据TCP首部的序列号和包含的数据的长度,将自己下一步应该接收的序列号作为确认应答返送回去。
重发超时如何确认:每次发包时都会计算往返时间及其偏差,重发超时的时间就是比这个总和稍大一点的值。
TCP以段为单位发送数据
最大消息长度 MSS :以mss大小分割数据进行发送
在三次握手时,两端主机发送建立连接请求时,会在TCP首部写入MSS选项,两者会选择较小值投入使用
窗口
每发一段进行一次确认:包的往返时间越长,网络的吞吐量会越差,通信性能越差。
为了解决这个问题,引入窗口:
确认应答不再以每个分段,而是以最大的单位进行确认。窗口大小就是指无需等待确认应答而可以继续发送数据的最大值。
收到确认应答时,窗口滑动到应答中的序列号的位置。
TCP首部
- 源端口号
发送端端口号,16位
- 目标端口号
接收端端口号,16位
- seq/序列号
指示发送数据的位置,32位
- ack/确认应答号
下一次应该收到的数据序列号,32位
- 数据偏移
指示TCP首部的长度,单位为4字节(32位),4位
- 控制位
ACK:应答
SYN:建立连接
FIN:断开连接
URG:紧急比特(urgent),当URG=1时,表明紧急指针字段有效,代表该封包为紧急封包。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据), 且上图中的 Urgent Pointer 字段也会被启用。
ACK:确认比特(Acknowledge)。只有当ACK=1时确认号字段才有效,代表这个封包为确认封包。当ACK=0时,确认号无效。
PSH:(Push function)若为1时,代表要求对方立即传送缓冲区内的其他对应封包,而无需等缓冲满了才送。
RST:复位比特(Reset) ,当RST=1时,表明TCP连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立运输连接。
SYN:同步比特(Synchronous),SYN置为1,就表示这是一个连接请求或连接接受报文,通常带有 SYN 标志的封包表示『主动』要连接到对方的意思。
FIN:终止比特(Final),用来释放一个连接。当FIN=1时,表明此报文段的发送端的数据已发送完毕,并要求释放运输连接。
窗口大小
校验和
紧急指针
三次握手、四次挥手
http://blog.csdn.net/oney139/article/details/8103223
http://blog.csdn.net/whuslei/article/details/6667471/
【问题1】为什么连接的时候是三次握手,关闭的时候却是四次握手?
答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,”你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。
【问题2】为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。
MSL就是maximum segment lifetime(最大分节生命期),这是一个IP数据包能在互联网上生存的最长时间,超过这个时间IP数据包将在网络中消失 。MSL在RFC 1122上建议是2分钟,而源自berkeley的TCP实现传统上使用30秒。
这种2MSL等待的另一个结果是这个TCP连接在2MSL等待期间,定义这个连接的插口对(包括本地以及远端的IP地址和端口号)不能再被使用。
特别是对于服务器,因为服务器使用熟知的端口,如果终止一个服务器程序,并试图立即重启这个服务器程序,服务器程序将不能把它的这个熟知端口赋值给它的端点,因为这个端口是处于2MSL连接的一部分。
描述TCP的半关闭、半打开
- 半关闭
TCP提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。这就是所谓的半关闭。
某一端已经完成数据传送,因此发送一个文件结束(FIN)给另一端,但还想接收另一端发送来的数据,直到它给我发送文件结束(FIN)。
- 半打开
如果一方已经关闭或异常终止连接而另一方却不知道,称为半打开。
处于半打开的连接,如果双方不进行数据通信,是发现不了问题的,只有在通信时才真正的察觉到这个连接已经处于半打开状态,如果双方不传输数据的话,仍处于连接状态的一方就不会检测另外一方已经出现异常。
这样会导致服务器主机产生很多半打开的TCP连接,通过保活定时器keep alive可以使TCP的一端发现另一端已经消失:
http://www.cnblogs.com/youxin/p/4056041.html
它每隔一段时间会超时,超时后会检查连接是否空闲太久了,如果空闲的时间超过了设置时间,就会发送探测报文。然后通过对端是否响应、响应是否符合预期,来判断对端是否正常,如果不正常,就主动关闭连接,而不用等待HTTP层的关闭了。
当服务器发送探测报文时,客户端可能处于4种不同的情况:仍然正常运行、已经崩溃、已经崩溃并重启了、由于中间链路问题不可达。在不同的情况下,服务器会得到不一样的反馈。
TCP的keepalive是检查当前连接是否还活着,HTTP的keepalive是让一个TCP连接活久点。
描述TCP同时打开、同时关闭
- 同时打开
两个程序同时彼此执行主动打开的情况:需要每一方使用一个对方熟知的端口作为本地的端口。
对于同时打开,它仅建立一条连接而不是两条连接
- 同时关闭
上述为一方先发送第一个FIN执行主动关闭。双方都执行主动关闭也是可能的。TCP协议允许这样的同时关闭。
如果应用程序同时发送FIN,则在发送后会首先进入FIN_WAIT_1状态。在收到对端的FIN后,回复一个ACK,会进入CLOSING状态。在收到对端的ACK后,进入TIME_WAIT状态。
同时关闭时两端都会进入TIME_WAIT状态。
TCP粘包问题
- 什么是粘包现象
TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。
- 为什么出现粘包现象
1、发送方原因
我们知道,TCP默认会使用Nagle算法。而Nagle算法主要做两件事:1)只有上一个分组得到确认,才会发送下一个分组;2)收集多个小分组,在一个确认到来时一起发送。
所以,正是Nagle算法造成了发送方有可能造成粘包现象。
2、接收方原因
TCP接收到分组时,并不会立刻送至应用层处理,或者说,应用层并不一定会立即处理;实际上,TCP将收到的分组保存至接收缓存里,然后应用程序主动从缓存里读收到的分组。这样一来,如果TCP接收分组的速度大于应用程序读分组的速度,多个包就会被存至缓存,应用程序读时,就会读到多个首尾相接粘到一起的包。
- 什么时候需要处理粘包现象
(1)如果发送方发送的多个分组本来就是同一个数据的不同部分,比如一个很大的文件被分成多个分组发送,这时,当然不需要处理粘包的现象;
(2)但如果多个分组本毫不相干,甚至是并列的关系,我们就一定要处理粘包问题了。比如,我当时要接收的每个分组都是一个有固定格式的商品信息,如果不处理粘包问题,每个读进来的分组我只会处理最前边的那个商品,后边的就会被丢弃。这显然不是我要的结果。
- 如何处理粘包现象
(1)发送方
对于发送方造成的粘包现象,我们可以通过关闭Nagle算法来解决,使用TCP_NODELAY选项来关闭Nagle算法。
(2)应用层处理
TCP是个流协议,就是数据没有界限。故在应用层的处理简单易行!并且不仅可以解决接收方造成的粘包问题,还能解决发送方造成的粘包问题:封包
封包就是给一段数据加上包头,这样一来数据包就分为包头和包体两部分内容了
解决服务器TIMEWAIT过多的问题
2MSL等待期间,定义这个连接的插口对(包括本地以及远端的IP地址和端口号)不能再被使用。
如果程序设计为服务器主动关闭,需要关注TIMEWAIT状态过多的问题。
1 使用SO_REUSEADDR重新使用2MSL等待的端口
3 利用SO_LINGER选项强制关闭,发RST而不是FIN,越过TIMEWAIT状态直接进入CLOSED状态。