TCP 服务的特点
- 面向连接,字节流和可靠传输.
- 通信双方必须建立连接,必须分配必要的内核资源
- 全双工即双方的数据读写可以通过一个连接来实现,完成数据交换之后,通信双方都必须断开连接释放系统资源.
- 应用程序执行多次写操作作时,TCP模块先将这些数据放入TCP发送缓存区中,当TCP模块真正开始发送数据时,发送缓冲区中的这些等待的数据可能被封住成一个或多个TCP报文段发出,因此,TCP模块发送出的TCP报文段的个数和应用程序执行的写操作次数之间没有固定的数量关系.
字节流:发送端执行的写操作次数和接收端执行的读操作次数之间没有任何数量关系,这就是字节流的概念.
数据报:发送段应用程序每执行一次写操作,UDP模块就将其封装成一个UDP数据报并发送,接收端也必须及时针对每一个UDP数据报执行读操作。,如果用户没有指定足够的应用程序缓冲区来读取UDP数据,则UDP数据将被截断。
- TCP协议采用发送应答机制,超时重传.
- TCP对接收的TCP报文段进行重排,整理。
#### TCP头部结构
- 16位端口号:包含源端口和目的端口,客户端通常使用系统自动选择的临时端口号,而服务器则使用知名服务端口号. 所有的知名服务使用的端口号定义在/etc/services文件中。
- 32位序号:一次TCP通信过程中一个传输方向上的字节流的每个字节的编号,例如:A发送给B的第一个TCP报文段中,序号值被系统初始化为某个随机值ISN,那么在该传输方向上,后续的TCP报文段中序号值将被系统设置为ISN加上该报文段所携带数据的第一个字节在整个字节流中的偏移。
- 32位的确认号: 用作对另外一方发送来的TCP报文段的响应,其值是收到的TCP报文段的序号值加1.
- 4位头部长度: 标识TCP头部有多少个32bit(4字节),最大60字节.
- 6位标志位:
URG:表示紧急指针是否有效.
ACK:表示确认号是否有效,我们称携带ACK标志的TCP报文段为确认报文段.
PSH:提示接收端应用程序应该立即从TCP接收缓冲区中读走数据,为接收数据腾出空间.
RST:表示要求对方重新建立连接.(复位报文段)
SYN:表示请求建立一个连接,(同步报文段);
FIN:表示通知对方本端要关闭连接了(结束报文段); - 16位窗口大小:TCP流量的一个控制手段,,它告知是对方本端的TCP接收缓冲区还能容纳多少字节的数据,这样对方就可以控制发送数据的速度.
- 16位校验和:由发送端填充,接收端对TCP报文段执行CRC算法,检验TCP头部在传输过程中是否损坏,不仅包括TCP头部,也包括数据部分.
- 16位紧急指针:是一个正的偏移量.它和序号字段的值相加表示最后一个紧急数据的下一个字节的序号.
通过修改/proc/sys/net/ipv4/tcp_window_scaling内核变量阿狸启用或关闭窗口扩大因子选项.
半关闭状态
TCP连接是全双工的,所以它允许两个方向的数据传输被独立的关闭,换言之,通信的一端可以发送结束报文段给对方,告诉本端已经完成了数据的发送但允许继续接收来自对方的数据,直到对方也发送结束报文段以关闭连接.
**服务器和客户端应用程序判断对方是否已经关闭连接的方法是:read系统调用返回0(收到结束报文段).
客户端的状态可以用如下流程来表示:
CLOSED->SYN_SEND->ESTABLISHED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED
服务器的状态转移:
CLOSED->LISTEN->SYN收到->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSED;
客户端执行半关闭之后,未等待服务器关闭连接就强行退出,此时客户端连接由内核来接管,可称之为孤儿连接,Linux为了防止孤儿连接长时间留在内核中,定义了两个内核变量:/proc/sys/net/ipv4/tcp_max_orphans 和/proc/sys/net/ipv4/tcp_fin_timeout.前者指定了孤儿连接数目,后者指定孤儿连接在内核中的生存的时间。
TIME_WAIT状态
TIME_WAIT状态存在的原因有两点:
* 可靠的终止TCP连接。
* 保证让迟来的TCP报文段有足够的时间被识别并丢弃.
复位报文段
- 当客户端访问一个不存在的端口时,目标主机将给它发送一个报文段.
- 当客户端程序向服务器的某个端口发起连接,而该端口仍被处于TIME_WAIT状态时,客户端也将收到复位报文段,.
*当给对方发送一个复位报文段,一旦发送了复位报文段,发送端所有排队等待发送的数据都将被丢弃. - 如果客户端或服务器处于半打开状态的连接写入数据,则对方回应一个复位报文段.
TCP交互数据流
客户端针对服务器返回的数据所发送的确认报文段都不携带任何应用程序数据,而服务器每次发送放入确认报文段都包含它需要发送的应用程序的数据.服务器的这种处理方式称为延迟确认.(服务器处理客户端程序快,所以发送报文段时总是有数据一起发送).延时确认可以减少发送TCP报文段的数量.
Nagle算法要求一个TCP连接的通信双方在任意时刻都最多只能发送一个未被确认的TCP报文段,在该TCP报文段的确认到达之前不能发送其它TCP报文段,另一个方面。发送方在等待确认的同时手机本端需要发送的微量数据.并确认到来以一个TCP报文段将它们全部发出,这样就极大减少了网络上微小TCP报文段的数量,该算法的另一个优点是自适应性:确认到达得快,数据就发送得越快.
TCP成块数据
当传输大量大块数据时,发送方会连续发送多个TCP报文段,接收方可以一次确认.
带外数据
- 带外数据比普通数据有更高的优先级,它应该总是立即被发送,而不论发送缓冲区中是否有排队等待发送的普通数据.带外数据的传输可以使用一条独立的传输层连接.
UDP和TCP中都没有真正的带外数据,TCP利用其头部的紧急指针标志和紧急指针两个字段。
SYN攻击
在三次握手过程中,服务器发送SYN-ACK之后,收到客户端的ACK之前的TCP连接称为半连接,此时服务器处于syn_RECV状态,服务器转入ESTABLISHED.
syn攻击就是攻击客户端,在短时间内伪造大量不存在的iP地址,向服务器不断地发送syn包,服务器回复确认包,并等待客户的确认,由于源地址是不存在的,服务器需要不断的重发直至超时,这些syn攻击是一个典型的DDOS攻击。
一般较新的TCP/IP协议栈都对这一过程来修改syn攻击,修改tcp协议实现,主要方法有synAttackProtect保护机制.SYN cookie技术,增加最大半连接和缩短超时时间等.
拥塞控制
TCP模块还有一个重要的任务,就是提高网络利用率,降低丢包率,并保证网络资源对每条数据流的公平性,这就是所谓的拥塞控制。
拥塞控制的四个部分:慢启动,拥塞避免,快速恢复和快速重传.
拥塞控制的最终受控变量是发送端向网络一次连续写入(收到其中第一个数据的确认之前)的数据量,我们称为SWND(发送窗口),不过,发送端最终以TCP报文段来发送数据,所以SWND限定了发送端能连续发送的TCP报文段数量. TCP报文段的最大长度称为SMSS,其值一般等于MSS;
网络延时是指一个数据包从用户的计算机发送到网站服务器 ,然后再立即从网站服务器返回用户计算机的来回时间;网络延时产生有以下几个原因:
* 本机到服务器之间路由跳数过多.由于光/电的传输速度非常快,他们在物理介质中的传播时间几乎可以忽略不计,但是路由器转发数据包的处理时间是不可以忽略的,当本机到服务器链路中有太多路由转发处理时,网络延时就会很明显.
* 网络带宽不够。
* 处理宽带不够,如果客户端和服务器直接通过一个路由器连接,且带宽足够,但服务器处理能力不足,也会造成响应延时.
拥塞窗口
是指发送方在接收到对方的ACK确认前允许网络发送的数据量,数据发送后,拥塞窗口缩小,接收到对方的ACK后,拥塞相应的增加,拥塞窗口越大,可发送的数据量越大,拥塞窗口初始值被规定不超过发送方MSS的两倍.
自己的理解:拥塞窗口由发送方控制流量的一种机制.根据拥塞窗口来进行慢启动.
通知窗口
是指接收方所能接收的每来得及发ACK确认的数据量,接收方数据接收后,通知窗口缩小,发送ACK后,通知窗口相应扩大.
自己的理解:由接收端控制,通知发送方我最多能接收的数据量.
发送窗口
拥塞窗口和通知窗口的最小值.
慢启动
TCP 连接建立好之后.拥塞窗口设置为1W,之后每收到接收端一个确认,其拥塞窗口就以指数形式增加.因为TCP模块刚开始发送数据时并不知道网络的实际情况,需要用一种试探的方式平滑增加拥塞窗口的值.
自己的理解:这个是为了找到网络中能传输的最大传输量.
拥塞避免:
当网络中出现丢包时,拥塞窗口减半,然后以线性的方式找网络传输的最佳临界点.
发送端判断拥塞发生的依据有以下两个:
- 传输超时,或者TCP重传定时器溢出;
解决方案: 仍然使用慢启动和拥塞控制. - 接收的重复的确认报文段.
解决方案:使用快速重传和快速恢复。
快速重传和快速恢复
当TCP报文段丢失(这只是发送端的猜测,比如当定时器到时时,就认为TCP报文段丢了,但是实际上这个TCP报文段没有丢失)或者接收端收到乱序TCP报文段并重排,拥塞控制算法需要判断当收到重复确认报文段时,网络是否整的发生了拥塞,还是TCP报文段是否真的丢失了,做法是:发送端如果收到3个重复的确认报文段,就认为是拥塞发生了,然后启动快速重传和快速恢复处理:过程如下:
* 当收到第三个重复的确认报文段时,把ssthresh设置为拥塞窗口的一半,把拥塞窗口设置为ssthresh的值加3,然后重传丢失的报文段,加三的原因是收到了3个重复的ACK,表明有3个“老”的数据包离开了网络.
* 每次收到重复放入ACK时,拥塞窗口增加1.
* .当收到新的数据包的ACK时,把cwnd设置为第一步中的ssthresh的值。原因是因为该ACK确认了新的数据,说明从重复ACK时的数据都已收到,该恢复过程已经结束,可以回到恢复之前的状态了,也即再次进入拥塞避免状态。