当前位置:   article > 正文

TCP协议-状态机_tcp状态机

tcp状态机

0.引用与阅读

dog250写的网络疑难杂症

1.为什么使用TCP?

  1. 1.数据可靠,必达;
  2. 2.传输效率不低;
  3. 3.顺序传输数据.

2.TCP报文头图示

 

  1. 1.端口号:用来标识同一台计算机的不同的应用进程。
  2. 1)源端口:源端口和IP地址的作用是标识报文的返回地址。
  3. 2)目的端口:端口指明接收方计算机上的应用程序接口。
  4. TCP报头中的源端口号和目的端口号同IP数据报中的源IP与目的IP唯一确定一条TCP连接。
  5. 2.序号和确认号:是TCP可靠传输的关键部分。序号是本报文段发送的数据组的第一个字节的序号。在
  6. TCP传送的流中,每一个字节一个序号。e.g.一个报文段的序号为300,此报文段数据部分共有100
  7. 节,则下一个报文段的序号为400。所以序号确保了TCP传输的有序性。确认号,即ACK,指明下一个期
  8. 待收到的字节序号,表明该序号之前的所有数据已经正确无误的收到。确认号只有当ACK标志为1时才
  9. 有效。比如建立连接时,SYN报文的ACK标志位为0
  10. 3.数据偏移/首部长度:4bits。由于首部可能含有可选项内容,因此TCP报头的长度是不确定的,报
  11. 头不包含任何任选字段则长度为20字节,4位首部长度字段所能表示的最大值为1111,转化为10进制为
  12. 1515*32/8 = 60,故报头最大长度为60字节。首部长度也叫数据偏移,是因为首部长度实际上指示
  13. 了数据区在报文段中的起始偏移值。
  14. 4.保留:为将来定义新的用途保留,现在一般置0
  15. 5.控制位:URG ACK PSH RST SYN FIN,共6个,每一个标志位表示一个控制功能。
  16. 1)URG:紧急指针标志,为1时表示紧急指针有效,为0则忽略紧急指针。
  17. 2)ACK:确认序号标志,为1时表示确认号有效,为0表示报文中不含确认信息,忽略确认号字段。
  18. 3)PSH:push标志,为1表示是带有push标志的数据,指示接收方在接收到该报文段以后,应尽快将这
  19. 个报文段交给应用程序,而不是在缓冲区排队。
  20. 4)RST:重置连接标志,用于重置由于主机崩溃或其他原因而出现错误的连接。或者用于拒绝非法的
  21. 报文段和拒绝连接请求。
  22. 5)SYN:同步序号,用于建立连接过程,在连接请求中,SYN=1和ACK=0表示该数据段没有使用捎带的
  23. 确认域,而连接应答捎带一个确认,即SYN=1和ACK=1
  24. 6)FIN:finish标志,用于释放连接,为1时表示发送方已经没有数据发送了,即关闭本方数据流。
  25. 6.窗口:滑动窗口大小,用来告知发送端接受端的缓存大小,以此控制发送端发送数据的速率,从而
  26. 达到流量控制。窗口大小是一个16bit字段,因而窗口大小最大为65535
  27. 7.校验和:奇偶校验,此校验和是对整个的 TCP 报文段,包括 TCP 头部和TCP数据,以16位字进
  28. 行计算所得。由发送端计算和存储,并由接收端进行验证。
  29. 8.紧急指针:只有当 URG 标志置 1 时紧急指针才有效。紧急指针是一个正的偏移量,和顺序号字段
  30. 中的值相加表示紧急数据最后一个字节的序号。 TCP 的紧急方式是发送端向另一端发送紧急数据的一
  31. 种方式。
  32. 9.选项和填充:最常见的可选字段是最长报文大小,又称为MSS(Maximum Segment Size),每个连接
  33. 方通常都在通信的第一个报文段(为建立连接而设置SYN标志为1的那个段)中指明这个选项,它表示
  34. 本端所能接受的最大报文段的长度。选项长度不一定是32位的整数倍,所以要加填充位,即在这个字
  35. 段中加入额外的零,以保证TCP头是32的整数倍。
  36. 10.数据部分: TCP 报文段中的数据部分是可选的。在一个连接建立和一个连接终止时,双方交换的
  37. 报文段仅有 TCP 首部。如果一方没有数据要发送,也使用没有任何数据的首部来确认收到的数据。在
  38. 处理超时的许多情况中,也会发送不带任何数据的报文段。

3.状态图的转变

   

4.TCP协议栈的实现

  1. 我的实现版本:
  2. struct tcphdr{
  3. unsighed short sport;
  4. unsighed short dport;
  5. unsighed int seqnum;
  6. unsighed int acknum;//
  7. unsigned char hdrlen:4,
  8. resv:4;
  9. unsigned char cwr:1,
  10. ece:1,
  11. urg:1,//紧急位
  12. ack:1,//
  13. psh:1,
  14. rst:1,
  15. syn:1,
  16. fin:1;
  17. unsighed short cwnd;
  18. unsighed short check;
  19. unsighed short urg_pointer;
  20. }
  21. struct tcppkt{
  22. struct ethhdr eh;
  23. struct iphdr ip;
  24. struct tcphdr tcp;
  25. unsighed char body[0];
  26. }
  1. 内核源码中的实现版本,所在文件include/Linux/tcp.h:
  2. struct tcphdr {
  3. __be16 source; //16位源端口号
  4. __be16 dest; //16位目的端口号
  5. //每个tcp段都包源和目的端口号,用于寻找发送端和接受端的应用进程。
  6. //这两个端口号加上ip报头中的源ip和目的ip,来确定一个唯一的TCP连接。
  7. __be32 seq; //此次发送的数据在整个报文段中的起始字节数。此序号用来标识从tcp发送端向tcp接受端发送的数据字节流,
  8. //seq表示在这个报文段中的第一个数据字节。如果将字节流看做在两个应用程序间的单向流动,
  9. //则tcp用序号对每个字节进行计数。32 bit的无符号数。为了安全起见,它的初始值是一个随机生成的数,
  10. //它到达232次方-1后又从零开始。
  11. __be32 ack_seq; //是下一个期望接收的字节,确认序号应当是上次已成功接收的序号+1,只有ack标志为1时确认序号字段才有效。
  12. //一旦一个连接已经建立了,ack总是=1
  13. #if defined(__LITTLE_ENDIAN_BITFIELD) //小端
  14. __u16 res1:4, // 保留位
  15. doff:4, //tcp头部长度,指明了在tcp头部中包含了多少个32位的字。由于options域的长度是可变的,
  16. //所以整个tcp头部的长度也是变化的。4bit可表示最大值15,故15*32=480bit=60字节,所以tcp首部最长60字节
  17. //然后,没有任选字段,正常的长度是20字节
  18. fin:1, //发端完成发送任务
  19. syn:1, //同步序号用来发起一个连接
  20. rst:1, //重建连接
  21. psh:1, //接收方应该尽快将这个报文段交给应用层
  22. ack:1, //一旦一个连接已经建立了,ack总是=1
  23. urg:1, //紧急指针有效
  24. ece:1,
  25. cwr:1;
  26. #elif defined(__BIG_ENDIAN_BITFIELD)
  27. __u16 doff:4,
  28. res1:4,
  29. cwr:1,
  30. ece:1,
  31. urg:1,
  32. ack:1,
  33. psh:1,
  34. rst:1,
  35. syn:1,
  36. fin:1;
  37. #else
  38. #error "Adjust your <asm/byteorder.h> defines"
  39. #endif
  40. __be16 window; //窗口大小,单位字节数,指接收端正期望接受的字节,16bit,故窗口大小最大为16bit=1111 1111 1111 1111(二进制)
  41. //=65535(十进制)字节
  42. __sum16 check; //校验和校验的是整个tcp报文段,包括tcp首部和tcp数据,这是一个强制性的字段,一定是由发端计算和存储,
  43. //并由收端进行验证。
  44. __be16 urg_ptr;
  45. };

5.思考问题

  1. 1.服务器连接那么多客户端,如何做到将对应的包发给对应的客户端?
  2. fd与五元组.
  3. 2.状态机?
  4. 状态迁移图.
  5. 3.一开始的时候窗口大小是多少?
  6. 默认是0,也可以赋值,但不论多少,传什么值是没有意义的.
  7. 4.如何实现三次握手?
  8. (1)客户端给服务器端发第一次请求;
  9. (2)然后服务端给客户段发确认和申请;
  10. (3)客户端给客户端发确认.
  11. 5.为什么不是2次握手或4次握手?
  12. 为什么不是两次?服务器没法知道ACK的值;
  13. 为什么不是四次?没有必要浪费,三次已经可以了,是协议上被动完成.全双工.
  14. 6.半连接队列和全连接队列?
  15. 第一次请求连接的时候服务器段需要准备一个syn队列来接受想要连接的客户端信息;
  16. 完成三次握手之后,要将队列中的节点移动到accept队列.
  17. syn队列 ---半连接队列
  18. accept队列---全连接队列
  19. 7.clientfd = accept(listenfd,adddr);中的accpt做了啥?recv函数又做了啥?
  20. 这里的accept做了两件事:
  21. (1)accept队列里面取出一个结点;
  22. (2)分配一个fd,与节点一一对应.
  23. accept的阻塞是通过条件变量来判断的,判断全连接队列中是否有数据,如果没有数据,就阻塞.
  24. recv函数把内存从内核区拷贝到用户区.
  25. 8.结点的生命周期是什么?
  26. liten到time_wait
  27. 9.tcp的11状态报错在哪里?
  28. 保存在tcb这个控制块里面,从liten到time_wait,这个tcb一直存在.
  29. 10.fd--结点如何一一对应上的呢?
  30. fd通过五元组判断唯一性.五元组是客户端唯一性的标识.(send如何找到对端,就是通过这个五元组)
  31. 11.端口只有65535,为什么同时连接数能够做到上百万?
  32. 一个fd对应一个五元组(sip,dip,sport,dport,proto),对于特定的协议来说,就是四元组.
  33. 12.什么是syn洪水攻击?如何避免?
  34. syn占着半连接队列,不发送确认请求建立连接,syn攻击会使有效的连接没法进来;
  35. 在外层加堡垒机或者防火墙给防住,软件内部很难实现.
  36. 13. 如何只发送syn不发送ack?
  37. 原生构造包,raw socket.
  38. 14.listen(fd,backlog);中的backlog是什么?
  39. 可以理解为:
  40. (1)syn队列的长度;(linux2.2版本以前,MAC中是这样的)
  41. (2)syn+accept队列的长度.(linux2.2以后的版本)
  42. 15.如何学好tcp?
  43. 站在设计者的角度思考如何实现可靠的流式协议.
  44. 16.send(fd,buffer,length,0);返回成功代表什么?
  45. send返回0,只是表示把buffer中的数据放到了内核缓冲区中,至于什么时候发,发多少,这个都是由协议栈决定的.
  46. 17.内核缓冲区中的包有有哪几种状态?
  47. (1)已发送已确认;
  48. (2)已发送未确认;
  49. (3)允许发送等待被发送.
  50. (4)暂不允许被发送数据.
  51. 17.tcp如何保证高效的?
  52. 多个包一起发送.
  53. 18.什么是滑动窗口?
  54. 多个包在网络上走,这个窗口是动态的,有一个开始位置和结束位置.
  55. 19.编号124...25个包,此时25号包收到了ack,这表示什么?
  56. 表示25号以前的包都收到了,但是25号包还没有收到.注意,确认的包里会有滑动窗口大小的值.
  57. 20.窗口大小是怎么确定的?
  58. 是对端发送ack的时候那个包中的window size字段确定的.窗口的起始位置是ack的值,终止位置是cwnd.
  59. 21.会间隔着确认吗?如何保证顺序的?
  60. 比如说发送端有4567891011号包等待发送,然后包到达对端的顺序是4567
  61. 91011,内核中会有一个定时器,比如说200ms,200ms到了之后会进行包的确认,如果在这个时间
  62. 内还没有收到8号包,那么此时接收端就会给发送端发送8号确认包,之后91011会和8一起进行重
  63. 传,这个就是tcp一种保证顺序的一种手段.
  64. 22.既然有tcp的可靠传输,为什么也会用udp做可靠传输?
  65. udp做可靠传输的场景
  66. (1)udp做可靠传输,不会带拥塞控制,不会去计算带宽,可用于下载,迅雷会员一开,局域网只有
  67. 你在用,其他人都上不了,这里的传输效率的提高牺牲了别人的上网条件;
  68. (2)udp的实时性,tcp会有一个延迟确认(为了追求传输效率),upd则牺牲了效率,实现实时性,
  69. udp的包是一个包一个包确认,来一个包确认一次.
  70. 23.如何计算窗口cwind?什么时候应该变大?什么时候应该变小?
  71. cwind应该是和rtt相关的,如果没有及时回复或者回复的时候没有达到理想的数值,系统可以认为
  72. 网络此时是比较拥塞;如果在规定的时间内超额完成里往返任务,系统可以认为此时的网络是比较空闲的.
  73. 当系统认为网络比较拥塞的时候,可以让窗口变小点;
  74. 当系统认为网络比较空闲的时候,可以让窗口变大点.
  75. 比如说计划10m内发送10个包并接受ACK确认包,
  76. 如果在10m内完成了这个任务,可以认为此时的网络是比较拥塞的,
  77. 如果在10m内没有完成这个任务,可以认为此时的网络是比较空闲的.
  78. 这个是Vegas算法采取的策略,RTT增加,拥塞窗口减小;RTT减小,拥塞窗口增大.
  79. 24.什么是rtt?
  80. round trip time,一个发送的包从发送出去到接受确认的时间,一次往返的时间.
  81. 25.如何计算rtt?
  82. rtt = 0.9*last_rtt + 0.1*cur_rtt,这个公式可以用来消除网络抖动,
  83. 比如rtt分别是0.1s,5s(进电梯了),2s,0.2s...,用这个公式就可以来在一定程度上消除一些抖动带来的影响.
  84. 26.cwnd与时间的关系是怎样的?
  85. 在门限值以下的时候,按照指数增长,是慢启动的过程;到达门限值以后,当出现返回不了数据或
  86. 者丢包的情况时,开始进行拥塞控制,比如采用窗口大小腰斩.在此过程中,rtt用来计算超时.关于
  87. cwnd与时间的经典关系图中的duplicated acks(重复确认),这时可以认为是拥塞的,发生了重复确
  88. 认,收到三个duplicated acks就会进入快速重传.cwnd是滑动窗口的大小.
  89. 27.思考一个过程,用tcp传输1m的数据的一个流程.
  90. 28.发送窗口,接受窗口和拥塞窗口?
  91. 应该是没有拥塞窗口这个概念的.
  92. 29.什么是快速重传?
  93. 在回复多个包的时候,将多个包的回复信息和send一起带过去.
  94. 30.在tcp的编程接口socket,bind,connect,listen,accpect,recv,sendclose这些中,
  95. 哪些动作对方能感知到?
  96. connect,sendclose.
  97. 31.查看网络连接的状态的命令?
  98. netstat
  99. 32.如何终止fin_wait_2状态?
  100. 只有kill进程,否则不会终止.此时只能接受数据不能发送数据,是主动发起断开连接请求一方的状态.
  101. 33.断开连接为什么要四次挥手?
  102. 因为是tcp通信时全双工的,需要A->B断了,也要B->A断了,就像谈恋爱,A和B说要分手,B也要和A说分手,才能断的干净.
  103. 33.time_wait有什么用?
  104. 防止最后一次确认包丢失变成僵尸包,无人收拾(难道这个包是放在内核中的吗?--探索一下,后面又说不会出现了~~~~~~)
  105. time_wait的时间是2MSL, MSL的意思是Maximum Segment Lifetime.
  106. 1)让TCP再次发送最后的ACK以防这个ACK丢失(被动关闭的一方超时并重发最后的FIN);保证TCP的可靠的全双工连接的终止。
  107. 2)允许老的重复分节在网络中消失。
  108. 3)TCP连接的建立和终止 在TIME_WAIT状态 时两端的端口不能使用,要等到2MSL时间结束才可继
  109. 续使用。当连接处于2MSL等待阶段时任何迟到的报文段都将被丢弃。不过在实际应用中可以通过在函
  110. 数setsockopt设置 SO_REUSEADDR选项达到不必等待2MSL时间结束再使用此端口,如setsockopt(sfd,
  111. SOL_SOCKET, SO_REUSEADDR,&opt, sizeof(opt));
  112. 33.time_wait的产生与影响分析?
  113. https://blog.csdn.net/mingongge/article/details/106631854
  114. 34.出现close_wait状态?
  115. 调用close时间不对.

6.TCP的参数设置

  1. 这里说明的是CentOS5.3,并内核使用的是2.6.18-128.el5PAE #1 SMP .
  2. 修改部分TCP ,有的是为了提高性能与负载,但是存在降低稳定性的风险.有的则是安全方面的配置,则有可能牺牲了性能.
  3. 1.TCP keepalive TCP连接保鲜设置
  4. echo 1800 > /proc/sys/net/ipv4/tcp_keepalive_time
  5. echo 15 > /proc/sys/net/ipv4/tcp_keepalive_intvl
  6. echo 5 > /proc/sys/net/ipv4/tcp_keepalive_probes
  7. keepalive是TCP保鲜定时器。当网络两端建立了TCP连接之后,闲置idle(双方没有任何数据流发送往来)了tcp_keepalive_time后,
  8. 服务器内核就会尝试向客户端发送侦测包,来判断TCP连接状况(有可能客户端崩溃、强制关闭了应用、主机不可达等等)。如果没有收到
  9. 对方的回答(ack包),则会在tcp_keepalive_intvl后再次尝试发送侦测包,直到收到对对方的ack,如果一直没有收到对方的ack,一共
  10. 会尝试tcp_keepalive_probes次,每次的间隔时间在这里分别是15s, 30s, 45s, 60s, 75s。如果尝试tcp_keepalive_probes,
  11. 依然没有收到对方的ack包,则会丢弃该TCP连接。
  12. 2. syn cookies设置
  13. echo 0 > /proc/sys/net/ipv4/tcp_syncookies
  14. 在CentOS5.3中,该选项默认值是1,即启用syn cookies功能。我们建议先关闭,直到确定受到syn flood攻击的时候再开启syn
  15. cookies功能,有效地防止syn flood攻击。也可以通过iptables规则拒绝syn flood攻击。
  16. 3.TCP 连接建立设置
  17. echo 8192 > /proc/sys/net/ipv4/tcp_max_syn_backlog
  18. echo 2 > /proc/sys/net/ipv4/tcp_syn_retries
  19. echo 2 > /proc/sys/net/ipv4/tcp_synack_retries
  20. tcp_max_syn_backlog SYN队列的长度,时常称之为未建立连接队列。系统内核维护着这样的一个队列,用于容纳状态为SYN_RESC
  21. 的TCP连接(half-open connection),即那些依然尚未得到客户端确认(ack)的TCP连接请求。加大该值,可以容纳更多的等待连接的网络连接数。
  22. tcp_syn_retries 新建TCP连接请求,需要发送一个SYN包,该值决定内核需要尝试发送多少次syn连接请求才决定放弃建立连接。
  23. 默认值是5. 对于高负责且通信良好的物理网络而言,调整为2
  24. tcp_synack_retries
  25. 对于远端SYN连接请求,内核会发送SYN+ACK数据包来确认收到了上一个SYN连接请求包,然后等待远端的确认(ack数据包)。该值则
  26. 指定了内核会向远端发送tcp_synack_retires次SYN+ACK数据包。默认设定值是5,可以调整为2.
  27. 4. TCP 连接断开相关设置
  28. echo 30 > /proc/sys/net/ipv4/tcp_fin_timeout
  29. echo 15000 > /proc/sys/net/ipv4/tcp_max_tw_buckets
  30. echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse
  31. echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle
  32. tcp_fin_timeout 对于由本端主动断开连接的TCP连接,本端会主动发送一个FIN数据报,在收到远端ACK后,且并没有收到远端FIN包之前,
  33. 该TCP连接的状态是FIN_WAIT_2状态,此时当远端关闭了应用,网络不可达(拔网张),程序不可断僵死等等,本端会一直保留状态为
  34. FIN_WAIT_2状态的TCP连接,该值tcp_fin_timeout则指定了状态为FIN_WAIT_2的TCP连接保存多长时间,一个FIN_WAIT_2的TCP连接最
  35. 多占1.5k内存。系统默认值是60秒,可以将此值调整为30秒,甚至10秒。
  36. tcp_max_tw_buckets 系统同时处理TIME_WAIT sockets数目。如果一旦TIME_WAIT tcp连接数超过了这个数目,系统会强制清除并且
  37. 显示警告消息。设立该限制,主要是防止那些简单的DoS攻击,加大该值有可能消耗更多的内存资源。如果TIME_WAIT socket过多,
  38. 则有可能耗尽内存资源。默认值是18w,可以将此值设置为5000~30000 tcp_tw_resue 是否可以使用TIME_WAIT tcp连接用
  39. 于建立新的tcp连接。
  40. tcp_tw_recycle 是否开启快带回收TIME_WAIT tcp连接的功能。
  41. 5. tcp 内存资源使用相参数设定
  42. echo 16777216 > /proc/sys/net/core/rmem_max
  43. echo 16777216 > /proc/sys/net/core/wmem_max
  44. cat /proc/sys/net/ipv4/tcp_mem
  45. echo “4096 65536 16777216″ > /proc/sys/net/ipv4/tcp_rmem
  46. echo “4096 87380 16777216″ > /proc/sys/net/ipv4/tcp_wmem
  47. rmem_max 定义了接收窗口可以使用的最大值,可以根据BDP值进行调节。
  48. wmem_max 定义了发送窗口可以使用的最大值,可以根据BDP什值进行调整。
  49. tcp_mem [low, pressure, high] TCP用这三个值来跟踪内存使用情况,来限定资源占用。通常情况下,在系统boot之时,内核会根据
  50. 可用内存总数计算出这些值。如果出现了Out of socket memory,则可以试着修改这个参数。
  51. 1)low: 当TCP使用了低于该值的内存页面数时,TCP不会考滤释放内存。
  52. 2)pressure: 当TCP使用了超过该值的内存页面数量,TCP试图稳定其对内存的占用,进入pressure模式,直到内存消耗达于low值,退出该模式。
  53. 3)hight:允许所有tcp sockets用于排队缓冲数据报的内存页数。
  54. tcp_rmem [min, default, max]
  55. 1)min 为每个TCP连接(tcp socket)预留用于接收缓冲的内存数量,即使在内存出现紧张情况下TCP socket都至少会有这么多数量的内存用于接收缓冲。
  56. 2)default 为TCP socket预留用于接收缓冲的内存数量,默认情况下该值影响其它协议使用的 rmem_default的值,所以有可能被rmem_default覆盖。
  57. 3)max 该值为每个tcp连接(tcp socket)用于接收缓冲的内存最大值。该值不会影响wmem_max的值,设置了选项参数 SO_SNDBUF则不受该值影响。
  58. tcp_wmem [min, default, max] 如上(tcp_rmen)只不过用于发送缓存。
  59. 注:
  60. 1)可以通过sysctl -w 或者写入/etc/sysctl.conf永久保存
  61. 2)性能调优仅在于需要的时候进行调整,调整以后需要采集数据与基准测试数据进行比较。建议,不需要盲从地调整这些参数。

7.TCP流程

在这里插入图片描述

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/754176
推荐阅读
相关标签
  

闽ICP备14008679号