linux高性能服务器网络编程第三章(TCP协议详解)

TCP服务的特点:

tcp面向链接、字节流和可靠;UDP则不同;

tcp通过调用send()/recv()函数处理数据,udp通过调用sendto()/recvfrom()处理数据;

可靠是指,接受确认、定时重发、由于IP分片对TCP报文段重排和整理;

可见,数据最终都是封成IP数据报发送的。

这里有个疑问,IP进行分片,tcp进行分组,那么怎么保证tcp协议不会被ip分片呢?

经查,有个MSS = MTU - 20(IP头) - 20(tcp头),而udp则为 MTU - 20(IP头)- 8 (udp)头,udp最大分片长度;

下面介绍TCP的头部结构:

长度:20字节

16位端口号

16位目标端口号

32位序号(seq):被系统初始化值为某个随机值ISN

32位确认号(ack)

4位头部长度,最长为60

6位保留

6位标志位:URG | ACK | PSH | RST | SYN | FIN 

16位窗口大小

16位校验和

16位紧急指针

TCP头部选项:

最长长度为40字节

典型结构为: | kind(1字节) | length(1字节) | info(n字节)

kind=0

kind=1 

kind=2 length=4 最大长度(2字节)

kind=3 length=3 移位数(1字节)

kind=4 length=2

kind=5 length=N*8+2 第1块左边沿第1块右边沿......第N块左边沿第N块右边沿

kind=8 length=10 时间戳(4) 时间戳回显应答(4)

TCP链接和关闭:

这就是大名鼎鼎的三次握手四次挥手了。

A ->   seq=N (SYN)                     B

A        seq=M(SYN) ack=N+1  <- B

A ->    ack=M+1                           B

 

A ->   seq=N +1,ack=M+1(FIN)     B

A        ack=N+2                          <- B

A        seq=M+1,ack=N+2 (FIN) <- B

A ->    ack=M+2                              B

半关闭状态:

TCP是双工的允许,客户端执行半关闭状态,可以通过read()==0或者shutdown()函数对半关闭的支持;

所谓的半关闭是通讯的一端告诉对方已经发送完毕,这是发送FIN,但是允许接收对方的数据,直到对方的数据发送完毕后进行关闭。

链接超时:

由于链接一个距离远或者繁忙的服务器可能造成链接超时。

TCP状态转移:

服务端:连接:LISTEN 发送syn SYN_RECV 收到ack确认 ESTABLISHED 

              关闭:收到客户端的断开链接  CLOSE_WAIT 发送端口链接包 LAST_ACK 收到客户端ACK完全断开

客户端:调用connect SYN_SEND ESTABLISHED

               FIN_WAIT1 FIN_WAIT2 TIME_WAIT

             若在FIN_WAIT1收到最终的ack称为孤儿连接,由操作系统内核管理

             为防止孤儿链接内核变量在/proc/sys/net/ipv4/tcp_max_orphans和/proc/sys/net/ipv4/tcp_fun_timeout中定义

TIME_WAIT状态:

        客户端在链接结束后并未进入到CLOSED状态,而是进入到TIME_WAIT状态,需要等待2MSL时间,MSL是TCP最大生存时间,建议为2min

        作用是:可靠的终止连接、保证让迟来的TCP报文能有足够的时间呗识别而不被丢弃。

        解决办法是用socket选项的SO_REUSEADDR

复位报文段:

       那么什么情况会被发复位报文呢?有三种情况分别是:

    (1)访问不存在的端口,或该端口也处于TIME_WAIT状态

    (2)异常终止连接,可以用SO_LINGER来发送复位报文段

    (3)处理半打开连接,服务端关闭(可能是网线断了),客户端没收到最后一个ack确认包,这样无任何连接信息了,如果想向其发送报文,这时候需要客户端发送一个复位报文。

TCP交互数据流:

所携带的应用程序数据分为两种:交互数据和成块数据

nagle算法解决拥塞,TCP连接在通讯双方任意时刻只最多只能发送一个未确认的tcp报文段。这有效的解决的许多小包发许多次的问题;

TCP成块数据流:

带外数据(OOD):

带外数据是指具有比普通数据更高优先级的数据,应该立即被发送,有一条独立的传输通道。

有某些协议有带外数据的使用,如telnet和ftp;

UDP没有带外数据实现。TCP没有真正意义上的带外数据的实现。通过设置紧急标准和紧急指针可以在TCP变相实现带外数据。

可以通过socket选项中的SO_OOBINLINE将带外数据设置成和普通数据一样进行发送。

TCP超时重传:

为了保证每个确认包都在固定的超时时间内返回,所以TCP为每个TCP报文都生成一个计时器,若超过超时时间,那么将被包重发。

拥塞控制:

为了提高网络利用率,降低丢包率,保证网络资源对每条数据有公平性。

提供了慢启动、拥塞避免、快速重传、快速恢复等功能;

算法,拥塞控制在linux下有reno/vegas/cubic等

发送窗口:SWND

最大长度:SMSS

拥塞窗口:CWND

接收窗口:RWND

慢启动和拥塞避免:

慢启动:

CWND+=min(N,MSS) N为确认包中未被确认的字节数 (1)

拥塞避免:

CWND+=SMSS*SMSS/CWND(2)

那么CWND的值何时更新呢,根据RFC 5681中提到

每个RTT时间按照(1)形计算;

没收到一个新的确认报文段按(2)计算;

接下来介绍拥塞发生时的两种形式:

传输超时,或者说TCP重传定时器溢出;

接收重复的确认报文段;

对于第一种情况用慢启动和拥塞避免的方式进行解决,对于第二种方式用快速重传快速恢复的形式进行决绝;

如果是第一种情况,那么进行重传,并且修改sstrsh=max(FlightSize/2,2*SMSS) CWND<=SMSS

快速重传和快速恢复:

当发生端连续收到三个重复确认的报文段,就发生拥塞了,对此应用快速重传和快速恢复,具体做法如下:

1)当收到三个重复确认包时,计算ssthresh值,然后立即重传丢失的报文段,并且按下列式子设置CWND

                                                                     CWND=ssthresh + 3*SMSS

2)每次收到一个重复确认,设置CWND=CWND+SMSS

3)收到新的确认时候设置CWND=ssthresh

 

 

 

 

 

 

 

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值