Chinaunix首页 | 论坛 | 博客
  • 博客访问: 487997
  • 博文数量: 25
  • 博客积分: 111
  • 博客等级: 民兵
  • 技术积分: 1279
  • 用 户 组: 普通用户
  • 注册时间: 2012-10-26 20:51
文章分类

全部博文(25)

文章存档

2014年(17)

2013年(8)

分类: C/C++

2013-08-19 00:58:20

最近在看网络编程方面的东西,原来对TCP的状态的认识都不是很清楚,这次仔细地看了看,下面先对TCP的状态和其转换进行简要说明,然后给出FSM对其进行模拟的代码。
首先,看看TCP的状态转移图,如下:

图片来自:
我们规定发起链接的主机叫做S,被动接受链接的主机叫做D,下面简要说明状态转移过程:
首先说说粗线,也就是主机S的状态转移过程,在CLOSED状态下,主机S向目的主机D发起链接,即:向D发送SYN k消息,也就是主机S调用了connect函数,然后进入SYN_SENT状态,此时若等待主机D返回对SYN k的ACK消息超时,则主机重新进入CLOSED状态,若主机S准时收到了主机D返回来的ACK以及自己的同步消息SYN j,主机S先向主机D发送SYN J的确认ACK消息,然后进入ESTABLISHED状态,也就是说主机connect成功。在此状态下,主机S与主机D进行正常的通信。若通信结束,主机S向目的主机发送FIN消息,也就是主机S调用了close函数,然后主机S 进入FIN_WAIT_1状态等待着主机D返回来FIN消息的ACK,当接收到主机D返回来的ACK后,主机S进入FIN_WAIT_2状态,因为通信是双端的,所以主机D也会向主机S发送FIN消息(也就是目的主机D也调用了close函数),这时主机S向目的主机发送确认ACK消息,同时进行入TIME_WAIT状态。TIME_WAIT状态持续2MSL(MSL最长分节生命期)后,进入CLOSED状态,也就socket正式关闭。之所以在WAIT_2和CLOSED之间加入一个TIME_WAIT状态且维持2MSL,是出于两个目的,1)确保TCP全双工的终止,例如:在FIN_WAIT_2状态时,主机S发完ACK后就关闭啦,而此时这个ACK又发丢失啦,这将导致主机D收不到其FIN的确认ACK而无法关闭。2)确保上一次链接产生的消息 ,在下一次再次连接之前就全部消失,不对下一次链接产生影响。
其次说说虚线,也就是主机D的状态转移过程,主机D处在LISTEN状态时,也就是主机D调用了listen和accept函数,此时主机D收到了主机S发送来了连接请求,也就是SYN K,然后返回给主机S自己的同步消息和S的确认消息SYN J ACK K+1。此时主机D进入SYS_RCVD状态,等待着主机S返回ACK J+1这个ACK,若收到了此确认消息,主机D进入ESTABLISHED状态,若没有收到还会重复发送(上图没有标出若主机D发送完SYN J ACK K+1消息后主机 S宕机了会怎样,猜测会有一个重发机制)。主机D的socket关闭过程与主机S的 关闭过程有些不一样,因为主机D是被迫关闭,此时主机D收到主机S发来的FIN消息,然后祥主机S返回对FIN的ACK,并进入CLOSE_WAIT状态,在此状态下,主机D将自己socket 内的数据处理完毕之后,同样向主机S发送FIN消息也就是调用close函数,此时主机D进入LAST_ACK状态,在收到来自主机S的ACK后进入最终的CLOSED状态。
最后简单说说图中的细线,图中的细线代表主机S和D同时打开和同时关闭时,TCP的状态转变,不过这两种情况是很少发生的。
下面先对FSM坐下简要说明,最后给出相应的模拟代码。
FSM,也就是有限状态机是一种数学上的概念,它是一种协议,用于有限数量的子程序,也就是状态,每一个子程序进行一些处理后进入下一个状态。在C语言中实现FSM的方法有很多,但大多数使用函数指针数组的方式实现,也可以使用显示的分支指令switch实现。下面为了实现上的简便,采用switch来实现 TCP状态 的FSM,如下:

点击(此处)折叠或打开

  1. enum status{CLOSED,LISTEN,ESTABLISED,SYN_RCVD,SYN_SENT,FIN_WAIT_1,FIN_WAIT_2,TIME_WAIT,CLOSE_WAIT,LAST_ACK};
  2. //链接建成后,接收一些数据
  3. int receive(void* buff,size_t len){
  4.     int len = 0;
  5.     //len为实际接收到的数据
  6.     //进行一些关于接收到的数据操作
  7.     return len;
  8. }
  9. int sent(void* buff,size_t len){
  10.     int len = 0;
  11.     //len为实际发送的数据
  12.     //进行一些关于发送数据的操作
  13.     return len;
  14. }
  15. //链接建成后发送一些数据
  16. void
  17. void status_operator(status s){
  18.     switch (s){
  19.         case LISTEN :
  20.             f_Listen();
  21.             break;
  22.         case ESTABLISED:
  23.             f_established();
  24.             break;
  25.         case CLOSED:
  26.             f_close();
  27.             break;
  28.         case SYN_RCVD:
  29.             f_syn_rcvd();
  30.             break;
  31.         case SYN_SENT:
  32.             f_syn_sent();
  33.             break;
  34.         case FIN_WAIT_1:
  35.             f_fin_wait_1();
  36.             break;
  37.         case FIN_WAIT_2:
  38.             f_fin_wait_2();
  39.             break;
  40.         case TIME_WAIT:
  41.             f_time_wait();
  42.             break;
  43.         case CLOSE_WAIT:
  44.             f_close_wait();
  45.             break;
  46.         case LAST_ACK:
  47.             f_last_ack();
  48.             break;
  49.         default:
  50.             break;
  51.     }
  52. }
  53. status f_Listen(void){
  54.     status rst;
  55.     //接收来自主机S发来的SYN K,并对其进行操作:K++
  56.     //向主机D发送确认和自己的同步消息 SYN J
  57.     rst = SYN_RCVD;
  58.     status_operator(rst);
  59. }
  60. status f_established(void){
  61.     status rst;
  62.     //此时链接已建立,也就是connect成功
  63.     //进行一些操作,
  64.     char* rbuf[1024],*wbuf[1024];
  65.     receive(rbuf,1024);
  66.     //一些操作
  67.     sent(wbuf,1024);
  68.     //一些操作
  69.     
  70.     //数据接收或发送完成之后
  71.     if(/*主机主动关闭链接*/){
  72.         rst = FIN_WAIT_1;
  73.     }
  74.     else
  75.         rst = CLOS_WAIT;
  76.     status_operator(rst);
  77. }
  78. status f_close(void){
  79.     status rst;
  80.     //进行一些清理工作
  81. }
  82. void f_syn_rcvd(void){
  83.     status rst;
  84.     //接收主机S发来的SYN K消息,并进行++K操作
  85.     //设置自己的SYN J消息,并和SYN K的确认消息一起发给主机D
  86.     rst = ESTABLISHED;
  87.     status_operator(rst);
  88. }
  89. void f_syn_sent(void){
  90.     status rst;
  91.     //其操作与f_Listen处理一样
  92.     rst = ESTABLISHED;
  93.     status_operator(rst);
  94. }
  95. void f_fin_wait_1(void){
  96.     status rst;
  97.     //应用主动关闭并发送 FIN消息
  98.     rst = FIN_WAIT_2;
  99.     status_operator(rst);
  100. }
  101. void f_fin_wait_2(void){
  102.     status rst;
  103.     //应用收到对在FIN_WAIT_1状态下发送的FIN的ACK
  104.     rst = TIME_WAIT;
  105.     status_operator(rst);
  106. }
  107. void f_time_wait(void){
  108.     status rst;
  109.     //在此状态下等待2MSL
  110.     rst = CLOSED;
  111.     status_operator(rst);
  112. }
  113. void f_close_wait(void){
  114.     status rst;
  115.     //此状态下,应用收到FIN并发送ACK
  116.     //应用进行一些关闭前的操作,比如吧socket中的数据读完
  117.     //并想主机S发送FIN消息
  118.     rst = LAST_ACK;
  119.     status_operator(rst);
  120.     
  121. }
  122. void f_last_ack(void){
  123.     status rst;
  124.     //当收到CLOSE_WAIT发送的FIN的ACK后
  125.     rst = CLOSED;
  126.     status_operator(rst);
  127. }
参考文献:
UNIX网络编程34~38
C专家编程183~185

此文出处:http://blog.chinaunix.net/uid-28311809-id-3853883.html
阅读(6873) | 评论(4) | 转发(5) |
给主人留下些什么吧!~~

bl竹子2013-08-19 16:00:08

AquesterFSM实现协议解析非常优雅,可以说是化算法为数据结构的典范,意思是通过好的结构来弱化算法的复杂性,在mooon的HttpParser实现中,也应用了FSM:https://code.google.com/p/mooon/source/browse/trunk/common_component/src/http_parser/http_parser.cpp

对mooon的HttpParser不怎么了解啊!

回复 | 举报

Aquester2013-08-19 12:58:32

FSM实现协议解析非常优雅,可以说是化算法为数据结构的典范,意思是通过好的结构来弱化算法的复杂性,在mooon的HttpParser实现中,也应用了FSM:https://code.google.com/p/mooon/source/browse/trunk/common_component/src/http_parser/http_parser.cpp