1. 程式人生 > >設計一個有限狀態機及其思路

設計一個有限狀態機及其思路

前言

我之前一直覺得狀態機是一個比較難理解的概念。所以遇到相關的問題都會覺得自己理解得不是很透徹,上週一個同事在給我分析問題的時候,無意間就談到了狀態機的流程,在分析問題的時候,沒想那麼多,感覺很順暢的感覺,後知後覺發現原來這就是狀態機了!我對於狀態機原理什麼的不懂,只從自己的實踐的角度來分析下怎麼去設計一個狀態機,以及是怎麼一個流程。

有限狀態機

在說明流程之前,我覺得先說說為什麼需要狀態機,同樣,我只從我實踐的角度來理解這個問題。
需要狀態機的場景,基本上是因為一些runtime執行時場景中,在這其中,涉及到多個狀態在不同條件下的遷移流程,以及一些特定事件的發生。舉個例子的話,基本上在做通訊相關的朋友應該都知道,在通訊中,狀態機的使用場景很廣,因為在呼叫過程中,涉及到呼叫的狀態遷移以及相關的流程處理。再比如,更經典的是TCP的有限狀態機,如下圖所示:

這裡寫圖片描述

TCP的有限狀態機不算簡單,也不算複雜,就中等複雜吧。我想待會兒說完我自己寫一個狀態機的流程之後,就以TCP的有限狀態機為例子來作為實戰嘗試一下。

設計狀態

設計狀態機的第一步,那就是設計狀態,設計者應該對於自己的場景中,會有哪些狀態有比較清晰的認識。對應上圖中,所謂的狀態就是黃色矩形所示的圖形,其實這一步是最為困難的,需要對整個業務場景有很清晰的認識,考慮需要很全面,需要覆蓋到場景業務中會出現的所有狀態。

狀態遷移路線

在有了狀態表之後,那就是需要確認的是,狀態的遷移路線,換句話說,狀態一可以直接通過一次躍遷,到達哪一個狀態,那就使用一個向量指向這個狀態。注意這裡的遷移一定是直接遷移,而不能是跨狀態連線,也就是說,不能是狀態一到狀態2這中間有中間狀態的遷移。

重新整理狀態表

這一步主要是在繪製狀態機圖時,需要將狀態設定在合理的位置,然後使用之前的狀態遷移路線連線,這一步相當於是前兩步的綜合,不過很重要,最終的圖示以這個為準,合理的狀態佈置除了便於理解之外,還利於程式碼的編寫。

具體例項

以上就是我所理解的在實踐中去設計一個狀態機所需要的步驟,這其中沒有什麼理論的論訴,實踐流程其實就是那麼簡單的幾步,接下來我想以上圖所示的TCP的有限狀態機為例子來講解下,這些步驟的具體實踐流程。

在TCP的有限狀態圖中,宿主機因為是全雙工的通訊,所以既是客戶機,也是服務端,那麼為了講解的清晰,這裡以客戶機的流程為主來講解。

TCP有限狀態機 客戶機例項

首先想想一個TCP客戶機需要哪些狀態呢?

第一個,毫無疑問是 CLOSED 剛開始的時候,客戶機肯定是處於關閉狀態的,有開始那麼就有傳送階段,客戶機肯定是主動去和服務端通訊的,所以這裡就有一個傳送狀態SEND。我們都知道在實際開始資料通訊之前,TCP是有一個連線建立過程的,就是俗稱的三次握手過程,但是在狀態機圖中,這是沒有必要的,因為這個過程並不能算一個狀態。在完成了傳送之後,在狀態層面上說,下一個狀態應該是一個持久態,就是資料傳輸狀態,即ESTABLISHED。之後的遷移,即是連線的斷開,就TCP來說,全雙工必須要斷開兩端的連線,狀態的設計很依賴場景,因為TCP的全雙工的特點,所以才有了接下來的狀態設計。第一步從客戶機進行斷開,這時候則有一個狀態FIN_WAIT_1,為啥這麼設計?因為這只是斷開了從c-s的連線,還需要斷開s-c的連線,若是服務端在回覆了客戶機之後,這代表服務端準備關閉了,這時候,就可以進入兩端都斷開的狀態FIN_WAIT_2階段了,這時候還不能直接進入CLOSED狀態,因為客戶機需要發出確認資訊給服務端,這時候為了保證連線的正確關閉,需要TIME_WAIT狀態來進行收尾,為什麼需要這個狀態,主要是考慮到網路狀態是不確定的,服務端的FIN包可能會重傳多次,需要正確處理這個狀態。

從以上可以看出,狀態機的設計,需要設計者非常清楚在自己的業務場景中的狀態,只有清楚了所有可能的狀態,接下里的步驟才有意義,這也算最重要的一個階段吧。

列舉出所有可能的狀態:

  • CLOSED
  • SEND
  • ESTABLISHED
  • FIN_WAIT_1
  • FIN_WAIT_2
  • TIME_WAIT

接下來的工作便是通過這些現有狀態進行狀態機遷移圖的繪製,以及整理工作了。注意以上只是c-s的流程,只是為了說明方便起見。

附帶說一句,在狀態機中,因為是runtime執行時的原因,狀態的遷移萬一沒有按期望遷移,那麼需要超時機制來保證遷移,所有在狀態機的設計中有一個很重要的概念,那便是定時器,什麼時候起一個定時器,什麼時候超時都是具體的業務問題了,具體問題具體看待。