TCP/IP詳解--TIME WAIT狀態詳解
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
Socket中的TIME_WAIT狀態 在高併發短連線的server端,當server處理完client的請求後立刻closesocket此時會出現time_wait狀態然後如果client再併發2000個連線,此時部分連線就連線不上了,用linger強制關閉可以解決此問題,但是linger會導致資料丟失,linger值為0時是強制關閉,無論併發多少多能正常連線上,如果非0會發生部分連線不上的情況!(是無法避免掉的,這是TCP協議實現的一部分。
在WINDOWS下,可以修改登錄檔讓這個時間變短一些
time_wait的時間為2msl,預設為4min.
你可以通過改變這個變數:
TcpTimedWaitDelay
把它縮短到30s TCP要保證在所有可能的情況下使得所有的資料都能夠被投遞。當你關閉一個socket時,主動關閉一端的socket將進入TIME_WAIT狀態,而被動關閉一方則轉入CLOSED狀態,這的確能夠保證所有的資料都被傳輸。當一個socket關閉的時候,是通過兩端互發資訊的四次握手過程完成的,當一端呼叫close()時,就說明本端沒有資料再要傳送了。這好似看來在握手完成以後,socket就都應該處於關閉CLOSED狀態了。但這有兩個問題,首先,我們沒有任何機制保證最後的一個ACK能夠正常傳輸,第二,網路上仍然有可能有殘餘的資料包(wandering duplicates),我們也必須能夠正常處理。
通過正確的狀態機,我們知道雙方的關閉過程如下
圖
假設最後一個ACK丟失了,伺服器會重發它傳送的最後一個FIN,所以客戶端必須維持一個狀態資訊,以便能夠重發ACK;如果不維持這種狀態,客戶端在接收到FIN後將會響應一個RST,伺服器端接收到RST後會認為這是一個錯誤。如果TCP協議能夠正常完成必要的操作而終止雙方的資料流傳輸,就必須完全正確的傳輸四次握手的四個節,不能有任何的丟失。這就是為什麼socket在關閉後,仍然處於 TIME_WAIT狀態,因為他要等待以便重發ACK。
如果目前連線的通訊雙方都已經呼叫了close(),假定雙方都到達CLOSED狀態,而沒有TIME_WAIT狀態時,就會出現如下的情況。現在有一個新的連線被建立起來,使用的IP地址與埠與先前的完全相同,後建立的連線又稱作是原先連線的一個化身。還假定原先的連線中有資料報殘存於網路之中,這樣新的連線收到的資料報中有可能是先前連線的資料報。為了防止這一點,TCP不允許從處於TIME_WAIT狀態的socket建立一個連線。處於TIME_WAIT狀態的socket在等待兩倍的MSL時間以後(之所以是兩倍的MSL,是由於MSL是一個數據報在網路中單向發出到認定丟失的時間,一個數據報有可能在傳送圖中或是其響應過程中成為殘餘資料報,確認一個數據報及其響應的丟棄的需要兩倍的MSL),將會轉變為CLOSED狀態。這就意味著,一個成功建立的連線,必然使得先前網路中殘餘的資料報都丟失了。
由於TIME_WAIT狀態所帶來的相關問題,我們可以通過設定SO_LINGER標誌來避免socket進入TIME_WAIT狀態,這可以通過傳送RST而取代正常的TCP四次握手的終止方式。但這並不是一個很好的主意,TIME_WAIT對於我們來說往往是有利的。
客戶端與伺服器端建立TCP/IP連線後關閉SOCKET後,伺服器端連線的埠
狀態為TIME_WAIT 是不是所有執行主動關閉的socket都會進入TIME_WAIT狀態呢?
有沒有什麼情況使主動關閉的socket直接進入CLOSED狀態呢? 主動關閉的一方在傳送最後一個 ack 後
就會進入 TIME_WAIT 狀態 停留2MSL(max segment lifetime)時間
這個是TCP/IP必不可少的,也就是“解決”不了的。
也就是TCP/IP設計者本來是這麼設計的
主要有兩個原因
1。防止上一次連線中的包,迷路後重新出現,影響新連線
(經過2MSL,上一次連線中所有的重複包都會消失)
2。可靠的關閉TCP連線
在主動關閉方傳送的最後一個 ack(fin) ,有可能丟失,這時被動方會重新發
fin, 如果這時主動方處於 CLOSED 狀態 ,就會響應 rst 而不是 ack。所以
主動方要處於 TIME_WAIT 狀態,而不能是 CLOSED 。
TIME_WAIT 並不會佔用很大資源的,除非受到攻擊。
還有,如果一方 send 或 recv 超時,就會直接進入 CLOSED 狀態 TCP狀態轉移要點
TCP協議規定,對於已經建立的連線,網路雙方要進行四次握手才能成功斷開連線,如果缺少了其中某個步驟,將會使連線處於假死狀態,連線本身佔用的資源不會被釋放。網路伺服器程式要同時管理大量連線,所以很有必要保證無用連線完全斷開,否則大量僵死的連線會浪費許多伺服器資源。在眾多TCP狀態中,最值得注意的狀態有兩個:CLOSE_WAIT和TIME_WAIT。
1、LISTENING狀態
FTP服務啟動後首先處於偵聽(LISTENING)狀態。
2、ESTABLISHED狀態
ESTABLISHED的意思是建立連線。表示兩臺機器正在通訊。
3、CLOSE_WAIT
對方主動關閉連線或者網路異常導致連線中斷,這時我方的狀態會變成CLOSE_WAIT 此時我方要呼叫close()來使得連線正確關閉
4、TIME_WAIT
我方主動呼叫close()斷開連線,收到對方確認後狀態變為TIME_WAIT。TCP協議規定TIME_WAIT狀態會一直持續2MSL(即兩倍的分段最大生存期),以此來確保舊的連線狀態不會對新連線產生影響。處於TIME_WAIT狀態的連線佔用的資源不會被核心釋放,所以作為伺服器,在可能的情況下,儘量不要主動斷開連線,以減少TIME_WAIT狀態造成的資源浪費。
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
目前有一種避免TIME_WAIT資源浪費的方法,就是關閉socket的LINGER選項。但這種做法是TCP協議不推薦使用的,在某些情況下這個操作可能會帶來錯誤。
不久前,我的 Socket Client 程式遇到了一個非常尷尬的錯誤。它本來應該在一個 socket 長連線上持續不斷地向伺服器傳送資料,如果 socket 連線斷開,那麼程式會自動不斷地重試建立連線。 有一天發現程式在不斷嘗試建立連線,但是總是失敗。用 netstat 檢視,這個程式竟然有上千個 socket 連線處於 CLOSE_WAIT 狀態,以至於達到了上限,所以無法建立新的 socket 連線了。 為什麼會這樣呢? 它們為什麼會都處在 CLOSE_WAIT 狀態呢? CLOSE_WAIT狀態的生成原因 首先我們知道,如果我們的 Client 程式處於 CLOSE_WAIT 狀態的話,說明套接字是被動關閉的! 因為如果是 Server 端主動斷掉當前連線的話,那麼雙方關閉這個 TCP 連線共需要四個 packet : Server ---> FIN ---> Client Server <--- ACK <--- Client 這 時候 Server 端處於 FIN_WAIT_2 狀態;而我們的程式處於 CLOSE_WAIT 狀態。 Server <--- FIN <--- Client 這 時 Client 傳送 FIN 給 Server , Client 就置為 LAST_ACK 狀 態。 Server ---> ACK ---> Client Server 迴應了 ACK ,那麼 Client 的套接字才會真正置為 CLOSED 狀態。
我們的程式處於CLOSE_WAIT狀態,而不是LAST_ACK狀態,說明還沒有發FIN給Server,那麼可能是在關閉連線之前還有許多資料要傳送或者其他事要做,導致沒有發這個FIN packet。 原因知道了,那麼為什麼不發 FIN 包呢,難道會在關閉己方連線前有那麼多事情要做嗎? 還有一個問題,為什麼有數千個連線都處於這個狀態呢?難道那段時間內,伺服器端總是主動拆除我們的連線嗎? 不管怎麼樣,我們必須防止類似情況再度發生! 首先,我們要防止不斷開闢新的埠,這可以通過設定SO_REUSEADDR套接字選項做到: 重用本地地址和埠 以前我總是一個埠不行,就換一個新的使用,所以導致讓數千個埠進入 CLOSE_WAIT 狀態。如果下次還發生這種尷尬狀況,我希望加一個限定,只是當前這個埠處於 CLOSE_WAIT 狀態! 在呼叫 sockConnected = socket(AF_INET, SOCK_STREAM, 0); 之後,我們要設定該套接字的選項來重用:
/// 允許重用本地地址和埠: /// 這樣的好處是,即使socket斷了,呼叫前面的socket函式也不會佔用另一個,而是始終就是一個埠 /// 這樣防止socket始終連線不上,那麼按照原來的做法,會不斷地換埠。 int nREUSEADDR = 1; setsockopt(sockConnected, SOL_SOCKET, SO_REUSEADDR, (const char*)&nREUSEADDR, sizeof(int)); |
linger m_sLinger; m_sLinger.l_onoff = 1; // ( 在closesocket()呼叫,但是還有資料沒傳送完畢的時候容許逗留) m_sLinger.l_linger = 0; // ( 容許逗留的時間為0秒) setsockopt(sockConnected, SOL_SOCKET, SO_LINGER, (const char*)&m_sLinger, sizeof(linger)); |
Feedback
# 回覆:[Socket]尷尬的CLOSE_WAIT狀態以及應對策略 2005-01-30 3:41 PM yun.zheng
回覆人: elssann(臭屁蟲和他的開心果) ( ) 信譽:51 2005-01-30 14:00:00 得分: 0
我的意思是:當一方關閉連線後,另外一方沒有檢測到,就導致了CLOSE_WAIT的出現,上次我的一個朋友也是這樣,他寫了一個客戶端和 APACHE連線,當APACHE把連線斷掉後,他沒檢測到,出現了CLOSE_WAIT,後來我叫他檢測了這個地方,他添加了呼叫 closesocket的程式碼後,這個問題就消除了。
如果你在關閉連線前還是出現CLOSE_WAIT,建議你取消shutdown的呼叫,直接兩邊closesocket試試。
另外一個問題:
比如這樣的一個例子:
當客戶端登入上伺服器後,傳送身份驗證的請求,伺服器收到了資料,對客戶端身份進行驗證,發現密碼錯誤,這時候伺服器的一般做法應該是先發送一個密碼錯誤的資訊給客戶端,然後把連線斷掉。
如果把
m_sLinger.l_onoff = 1;
m_sLinger.l_linger = 0;
這樣設定後,很多情況下,客戶端根本就收不到密碼錯誤的訊息,連線就被斷了。
# 回覆:[Socket]尷尬的CLOSE_WAIT狀態以及應對策略 2005-01-30 3:41 PM yun.zheng
elssann(臭屁蟲和他的開心果) ( ) 信譽:51 2005-01-30 13:24:00 得分: 0
出現CLOSE_WAIT的原因很簡單,就是某一方在網路連線斷開後,沒有檢測到這個錯誤,沒有執行closesocket,導致了這個狀態的實現,這在TCP/IP協議的狀態變遷圖上可以清楚看到。同時和這個相對應的還有一種叫TIME_WAIT的。
另外,把SOCKET的SO_LINGER設定為0秒拖延(也就是立即關閉)在很多時候是有害處的。
還有,把埠設定為可複用是一種不安全的網路程式設計方法。
# 回覆:[Socket]尷尬的CLOSE_WAIT狀態以及應對策略 2005-01-30 3:42 PM yun.zheng
elssann(臭屁蟲和他的開心果) ( ) 信譽:51 2005-01-30 14:48:00 得分: 0
斷開連線的時候,
當發起主動關閉的左邊這方傳送一個FIN過去後,右邊被動關閉的這方要回應一個ACK,這個ACK是TCP迴應的,而不 是應用程式傳送的,此時,被動關閉的一方就處於CLOSE_WAIT狀態了。如果此時被動關閉的這一方不再繼續呼叫closesocket,那麼他就不會 傳送接下來的FIN,導致自己老是處於CLOSE_WAIT。只有被動關閉的這一方呼叫了closesocket,才會傳送一個FIN給主動關閉的這一 方,同時也使得自己的狀態變遷為LAST_ACK。
# 回覆:[Socket]尷尬的CLOSE_WAIT狀態以及應對策略 2005-01-30 3:54 PM yun.zheng
elssann(臭屁蟲和他的開心果) ( ) 信譽:51 2005-01-30 15:39:00 得分: 0
比如被動關閉的是客戶端。。。
當對方呼叫closesocket的時候,你的程式正在
int nRet = recv(s,....);
if (nRet == SOCKET_ERROR)
{
// closesocket(s);
return FALSE;
}
很多人就是忘記了那句closesocket,這種程式碼太常見了。
我的理解,當主動關閉的一方傳送FIN到被動關閉這邊後,被動關閉這邊的TCP馬上回應一個ACK過去,同時向上面應用程式提交一個ERROR,導 致上面的SOCKET的send或者recv返回SOCKET_ERROR,正常情況下,如果上面在返回SOCKET_ERROR後呼叫了 closesocket,那麼被動關閉的者一方的TCP就會發送一個FIN過去,自己的狀態就變遷到LAST_ACK.
# 回覆:[Socket]尷尬的CLOSE_WAIT狀態以及應對策略 2005-01-30 4:17 PM yun.zheng
int nRecvBufLength =
recv(sockConnected,
szRecvBuffer,
sizeof(szRecvBuffer),
0);
/// zhengyun 20050130:
/// elssann舉例說,當對方呼叫closesocket的時候,我的程式正在
/// recv,這時候有可能對方傳送的FIN包我沒有收到,而是由TCP代回了
/// 一個ACK包,所以我這邊程式進入CLOSE_WAIT狀態。
/// 所以他建議在這裡判斷是否已出錯,是就主動closesocket。
/// 因為前面我們已經設定了recv超時時間為30秒,那麼如果真的是超時了,
/// 這裡收到的錯誤應該是WSAETIMEDOUT,這種情況下也可以關閉連線的
if (nRecvBufLength == SOCKET_ERROR)
{
TRACE_INFO(_T("=用recv接收發生Socket錯誤="));
closesocket(sockConnected);
continue;
}
網路連線無法釋放—— CLOSE_WAIT
關鍵字:TCP ,CLOSE_WAIT, Java, SocketChannel
問題描述:最 近效能測試碰到的一個問題。客戶端使用NIO,伺服器還是一般的Socket連線。當測試進行一段時間以後,發現伺服器端的系統出現大量未釋放的網路連 接。用netstat -na檢視,連線狀態為CLOSE_WAIT。這就奇怪了,為什麼Socket已經關閉而連線依然未釋放。
解決:Google了半天,發現關於CLOSE_WAIT的問題一般是C的,Java似乎碰到這個問題的不多(這有一篇不錯的,也是解決CLOSE_WAIT的,但是好像沒有根本解決,而是選擇了一個折中的辦法)。接著找,由於使用了NIO,所以懷疑可能是這方面的問題,結果找到了這篇。順著帖子翻下去,其中有幾個人說到了一個問題—— 一端的Socket呼叫close後,另一端的Socket沒有呼叫close.於是查了一下程式碼,果然發現Server端在某些異常情況時,沒有關閉Socket。改正後問題解決。
時間基本上花在Google上了,不過也學到不少東西。下面為一張TCP連線的狀態轉換圖:
說明:虛線和實線分別對應伺服器端(被連線端)和客戶端端(主動連線端)。
結合上圖使用netstat -na命令即可知道到當前的TCP連線狀態。一般LISTEN、ESTABLISHED、TIME_WAIT是比較常見。
分析:
上面我碰到的這個問題主要因為TCP的結束流程未走完,造成連線未釋放。現設客戶端主動斷開連線,流程如下
Client 訊息 Server
close()
------ FIN ------->
FIN_WAIT1 CLOSE_WAIT
<----- ACK -------
FIN_WAIT2
close()
<------ FIN ------
TIME_WAIT LAST_ACK
------ ACK ------->
CLOSED
CLOSED
如上圖所示,由於Server的Socket在客戶端已經關閉時而沒有呼叫關閉,造成伺服器端的連線處在“掛起”狀態,而客戶端則處在等待應答的狀態上。此問題的典型特徵是:一端處於FIN_WAIT2 ,而另一端處於CLOSE_WAIT. 不過,根本問題還是程式寫的不好,有待提高。
TIME_WAIT狀態根據TCP協議,主動發起關閉的一方,會進入TIME_WAIT狀態,持續2*MSL(Max Segment Lifetime),預設為240秒,在這個post中簡潔的介紹了為什麼需要這個狀態。
值得一說的是,對於基於TCP的HTTP協議,關閉TCP連線的是Server端,這樣,Server端會進入TIME_WAIT狀態,可 想而知,對於訪問量大的Web Server,會存在大量的TIME_WAIT狀態,假如server一秒鐘接收1000個請求,那麼就會積壓240*1000=240,000個 TIME_WAIT的記錄,維護這些狀態給Server帶來負擔。當然現代作業系統都會用快速的查詢演算法來管理這些TIME_WAIT,所以對於新的 TCP連線請求,判斷是否hit中一個TIME_WAIT不會太費時間,但是有這麼多狀態要維護總是不好。
HTTP協議1.1版規定default行為是Keep-Alive,也就是會重用TCP連線傳輸多個 request/response,一個主要原因就是發現了這個問題。還有一個方法減緩TIME_WAIT壓力就是把系統的2*MSL時間減少,因為 240秒的時間實在是忒長了點,對於Windows,修改登錄檔,在HKEY_LOCAL_MACHINE/ SYSTEM/CurrentControlSet/Services/ Tcpip/Parameters上新增一個DWORD型別的值TcpTimedWaitDelay,一般認為不要少於60,不然可能會有麻煩。
對於大型的服務,一臺server搞不定,需要一個LB(Load Balancer)把流量分配到若干後端伺服器上,如果這個LB是以NAT方式工作的話,可能會帶來問題。假如所有從LB到後端Server的IP包的 source address都是一樣的(LB的對內地址),那麼LB到後端Server的TCP連線會受限制,因為頻繁的TCP連線建立和關閉,會在server上留 下TIME_WAIT狀態,而且這些狀態對應的remote address都是LB的,LB的source port撐死也就60000多個(2^16=65536,1~1023是保留埠,還有一些其他埠預設也不會用),每個LB上的埠一旦進入 Server的TIME_WAIT黑名單,就有240秒不能再用來建立和Server的連線,這樣LB和Server最多也就能支援300個左右的連線。 如果沒有LB,不會有這個問題,因為這樣server看到的remote address是internet上廣闊無垠的集合,對每個address,60000多個port實在是夠用了。
一開始我覺得用上LB會很大程度上限制TCP的連線數,但是實驗表明沒這回事,LB後面的一臺Windows Server 2003每秒處理請求數照樣達到了600個,難道TIME_WAIT狀態沒起作用?用Net Monitor和netstat觀察後發現,Server和LB的XXXX埠之間的連線進入TIME_WAIT狀態後,再來一個LB的XXXX埠的 SYN包,Server照樣接收處理了,而是想像的那樣被drop掉了。翻書,從書堆裡面找出覆滿塵土的大學時代買的《UNIX Network Programming, Volume 1, Second Edition: Networking APIs: Sockets and XTI》,中間提到一句,對於BSD-derived實現,只要SYN的sequence number比上一次關閉時的最大sequence number還要大,那麼TIME_WAIT狀態一樣接受這個SYN,難不成Windows也算BSD-derived?有了這點線索和關鍵字 (BSD),找到這個post,在NT4.0的時候,還是和BSD-derived不一樣的,不過Windows Server 2003已經是NT5.2了,也許有點差別了。
做個試驗,用Socket API編一個Client端,每次都Bind到本地一個埠比如2345,重複的建立TCP連線往一個Server傳送Keep-Alive=false 的HTTP請求,Windows的實現讓sequence number不斷的增長,所以雖然Server對於Client的2345埠連線保持TIME_WAIT狀態,但是總是能夠接受新的請求,不會拒絕。那 如果SYN的Sequence Number變小會怎麼樣呢?同樣用Socket API,不過這次用Raw IP,傳送一個小sequence number的SYN包過去,Net Monitor裡面看到,這個SYN被Server接收後如泥牛如海,一點反應沒有,被drop掉了。
按照書上的說法,BSD-derived和Windows Server 2003的做法有安全隱患,不過至少這樣至少不會出現TIME_WAIT阻止TCP請求的問題,當然,客戶端要配合,保證不同TCP連線的sequence number要上漲不要下降。
給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow
你好! 這是你第一次使用 **Markdown編輯器** 所展示的歡迎頁。如果你想學習如何使用Markdown編輯器, 可以仔細閱讀這篇文章,瞭解一下Markdown的基本語法知識。新的改變
我們對Markdown編輯器進行了一些功能拓展與語法支援,除了標準的Markdown編輯器功能,我們增加了如下幾點新功能,幫助你用它寫部落格:
- 全新的介面設計 ,將會帶來全新的寫作體驗;
- 在創作中心設定你喜愛的程式碼高亮樣式,Markdown 將程式碼片顯示選擇的高亮樣式 進行展示;
- 增加了 圖片拖拽 功能,你可以將本地的圖片直接拖拽到編輯區域直接展示;
- 全新的 KaTeX數學公式 語法;
- 增加了支援甘特圖的mermaid語法1 功能;
- 增加了 多螢幕編輯 Markdown文章功能;
- 增加了 焦點寫作模式、預覽模式、簡潔寫作模式、左右區域同步滾輪設定 等功能,功能按鈕位於編輯區域與預覽區域中間;
- 增加了 檢查列表 功能。
功能快捷鍵
撤銷:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜體:Ctrl/Command + I
標題:Ctrl/Command + Shift + H
無序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
檢查列表:Ctrl/Command + Shift + C
插入程式碼:Ctrl/Command + Shift + K
插入連結:Ctrl/Command + Shift + L
插入圖片:Ctrl/Command + Shift + G
合理的建立標題,有助於目錄的生成
直接輸入1次#,並按下space後,將生成1級標題。
輸入2次#,並按下space後,將生成2級標題。
以此類推,我們支援6級標題。有助於使用TOC
語法後生成一個完美的目錄。
如何改變文字的樣式
強調文字 強調文字
加粗文字 加粗文字
標記文字
刪除文字
引用文字
H2O is是液體。
210 運算結果是 1024.
插入連結與圖片
連結: link.
圖片:
帶尺寸的圖片:
當然,我們為了讓使用者更加便捷,我們增加了圖片拖拽功能。
如何插入一段漂亮的程式碼片
去部落格設定頁面,選擇一款你喜歡的程式碼片高亮樣式,下面展示同樣高亮的 程式碼片
.
// An highlighted block var foo = 'bar';
生成一個適合你的列表
- 專案
- 專案
- 專案
- 專案
- 專案1
- 專案2
- 專案3
- 計劃任務
- 完成任務
建立一個表格
一個簡單的表格是這麼建立的:
專案 | Value |
---|---|
電腦 | $1600 |
手機 | $12 |
導管 | $1 |
設定內容居中、居左、居右
使用:---------:
居中
使用:----------
居左
使用----------:
居右
第一列 | 第二列 | 第三列 |
---|---|---|
第一列文字居中 | 第二列文字居右 | 第三列文字居左 |
SmartyPants
SmartyPants將ASCII標點字元轉換為“智慧”印刷標點HTML實體。例如:
TYPE | ASCII | HTML |
---|---|---|
Single backticks | 'Isn't this fun?' |
‘Isn’t this fun?’ |
Quotes | "Isn't this fun?" |
“Isn’t this fun?” |
Dashes | -- is en-dash, --- is em-dash |
– is en-dash, — is em-dash |
建立一個自定義列表
- Markdown
- Text-to- HTML conversion tool
- Authors
- John
- Luke
如何建立一個註腳
一個具有註腳的文字。2
註釋也是必不可少的
Markdown將文字轉換為 HTML。
KaTeX數學公式
您可以使用渲染LaTeX數學表示式 KaTeX:
Gamma公式展示 是通過尤拉積分
你可以找到更多關於的資訊 LaTeX 數學表示式here.
新的甘特圖功能,豐富你的文章
gantt
dateFormat YYYY-MM-DD
title Adding GANTT diagram functionality to mermaid
section 現有任務
已完成 :done, des1, 2014-01-06,2014-01-08
進行中 :active, des2, 2014-01-09, 3d
計劃一 : des3, after des2, 5d
計劃二 : des4, after des3, 5d
- 關於 甘特圖 語法,參考 這兒,
UML 圖表
可以使用UML圖表進行渲染。 Mermaid. 例如下面產生的一個序列圖::
這將產生一個流程圖。:
- 關於 Mermaid 語法,參考 這兒,
FLowchart流程圖
我們依舊會支援flowchart的流程圖:
- 關於 Flowchart流程圖 語法,參考 這兒.
匯出與匯入
匯出
如果你想嘗試使用此編輯器, 你可以在此篇文章任意編輯。當你完成了一篇文章的寫作, 在上方工具欄找到 文章匯出 ,生成一個.md檔案或者.html檔案進行本地儲存。
匯入
如果你想載入一篇你寫過的.md檔案或者.html檔案,在上方工具欄可以選擇匯入功能進行對應副檔名的檔案匯入,
繼續你的創作。
註腳的解釋 ↩︎