欧美,精品,综合,亚洲,好吊妞视频免新费观看,免费观看三级吃奶,一级a片女人自慰免费看

 首頁 > 新聞 > 專家觀點 >

《FreeSWITCH: VoIP實戰(zhàn)》:FreeSWITCH 架構(gòu)

2012-08-14 14:02:18   作者:杜金房   來源:FreeSWITCH    評論:0  點擊:


  總體結(jié)構(gòu)

  FreeSWITCH 由一個穩(wěn)定的核心及外圍模塊組成。

  FreeSWITCH 使用線程模型來處理并發(fā)請求,每個連接都在單獨的線程中進行處理。這不僅能提供最大強度的并發(fā),更重要的是,即使某路電話發(fā)生問題,也只影響到它所在的線程,而不會影響到其它電話。FreeSWITCH 的核心非常短小精悍,這也是保持穩(wěn)定的關(guān)鍵。所有其它功能都在外圍的模塊中。模塊是可以動態(tài)加載(以及卸載)的,在實際應(yīng)用中可以只加載用到的模塊。外圍模塊通過核心提供的 Public API 與核心進行通信,而核心則通過回調(diào)機制執(zhí)行外圍模塊中的代碼。

  核心

  FS Core 是 FreeSWITCH 的核心,它包含了關(guān)鍵的數(shù)據(jù)結(jié)構(gòu)和復(fù)雜的代碼,但這些代碼只出現(xiàn)在核心中,并保持了最大限度的重用。外圍模塊只能通過 API 調(diào)用核心的功能,因而核心運行在一個受保護的環(huán)境中,核心代碼都經(jīng)過精心的編碼和嚴格的測試,最大限度地保持了系統(tǒng)整體的穩(wěn)定。

  核心代碼保持了最高度的抽象,因而它可以調(diào)用不同功能,不同協(xié)議的模塊。同時,良好的 API 也使得編寫不同的外圍模塊非常容易。

  數(shù)據(jù)庫

  FreeSWITCH 的核心除了使用內(nèi)部的隊列、哈希表存儲數(shù)據(jù)外,也使用外部的 SQL 數(shù)據(jù)庫存儲數(shù)據(jù)。當前,系統(tǒng)的核心數(shù)據(jù)庫使用 SQLite,默認的存儲位置是 db/core.db 。 使用外部數(shù)據(jù)庫的好處是--查詢數(shù)據(jù)不用鎖定內(nèi)存數(shù)據(jù)結(jié)構(gòu),這不僅能提供性能,而且降低了死鎖的風險,保證了系統(tǒng)穩(wěn)定。命令 show calls、show channels 等都是直接從數(shù)據(jù)庫中讀取內(nèi)容并顯示的。由于 SQLite 會進行讀鎖定,因此不建議直接讀取核心數(shù)據(jù)庫。

  系統(tǒng)對數(shù)據(jù)庫操作做了優(yōu)化,在高并發(fā)狀態(tài)時,核心會盡量將幾百條 SQL 一齊執(zhí)行,這大大提高了性能。但在低并發(fā)的狀態(tài)下執(zhí)行顯得稍微有點慢,如一個 channel 已經(jīng)建立了,但還不能在 show channels 中顯示;或者,一個 channel 已經(jīng) destroy 了,還顯示在 show channels 中。但由于這些數(shù)據(jù)只用于查詢,而不用于決策,所以一般沒什么問題。

  除核心數(shù)據(jù)庫外,系統(tǒng)也支持使用 ODBC 方式連接其它數(shù)據(jù)庫,如 PostgreSQL、MySQL等。某些模塊,如 mod_sofia、mod_fifo等都有自己的數(shù)據(jù)庫(表)。如果在 *nix 類系統(tǒng)上使用 ODBC,需要安裝 UnixODBC,并進行正確的配置,如果編譯安裝的話還需要開發(fā)包 unixodbc-devel(CentOS) 或 unixodbc-dev(Debian/Ubuntu)。由于 PostgreSQL、MySQL 等都是 Client-Server 的結(jié)構(gòu),因此,外部程序可以直接查詢數(shù)據(jù)(但需要清楚數(shù)據(jù)的準確性,可能會比 FreeSWITCH 核心中的數(shù)據(jù)有所滯后)。

  模塊

  FreeSWITCH 主要分為以下幾個部分:

  終點

  End Points 是終結(jié) FreeSWITCH 的地方,也就是說再往外走就超出 FreeSWITCH 的控制了。它主要包含了不同呼叫控制協(xié)議的接口,如 SIP, TDM 硬件,H323 以及 Google Talk 等。這使得 FreeSWITCH 可以與眾多不同的電話系統(tǒng)進行通信。如,可以使用 mod_skypopen 與 Skype 網(wǎng)絡(luò)進行通信。另外,前面也講過,它還可以通過 portaudio 驅(qū)動本地聲卡,用作一個軟電話。

  撥號計劃

  Dialplan 主要是為了查找電話路由,主要的是 XML 描述的,但它也支持 Asterisk 格式的配置文件。另外它也持 ENUM 查詢。

  XML 接口

  XML Interface 支持多種獲取 XML 配置的方式,它可以是本地的配置文件,或從數(shù)據(jù)庫中讀取,甚至是一個能動態(tài)返回 XML 的遠程 HTTP 服務(wù)器。

  編解碼器

  FreeSWITCH 支持最廣泛的 Codec,除了大多數(shù) VoIP 系統(tǒng)支持的 G711、G722、G729、GSM 外,它還支持 iLBC,BV16/32、SILK、CELT等。它可以同時橋接不同采樣頻率的電話,以及電話會議等。

  語音識別

  支持語音自動識別(ASR)及文本-語音轉(zhuǎn)換(TTS)。

  文件格式

  支持不同的聲音文件格式,如 wav,mp3等。

  日志

  日志可以寫到控制臺、日志文件、系統(tǒng)日志(syslog)以及遠程的日志服務(wù)器。

   嵌入式語言

   通過 swig 包裝支持多種腳本語本語言控制呼叫流程,如 Lua、Javascript、Perl等。

  事件套接字

   使用 Event Socket 可以使用任何其它語言通過 Socket 方式控制呼叫流程、擴展 FreeSWITCH 功能。

   目錄結(jié)構(gòu)

   在 *nix 類系統(tǒng)上,F(xiàn)reeSWITCH 默認的安裝位置是 /usr/local/freeswitch,在 Windows 上可能是 C:\freeswitch,目錄結(jié)構(gòu)大致相同。

bin         可執(zhí)行程序
db          系統(tǒng)數(shù)據(jù)庫(sqlite),F(xiàn)reeSWITCH 把呼叫信息存放到數(shù)據(jù)庫里以便在查詢時無需對核心數(shù)據(jù)結(jié)構(gòu)加鎖
htdocs      HTTP Web srver 根目錄
lib         庫文件
mod         可加載模塊
run         運行目錄,存放 PID
sounds      聲音文件,使用 playback() 時默認的尋找路徑
grammar     語法
include     頭文件
log         日志,CDR 等
recordings  錄音,使用 record() 時默認的存放路徑
scripts     嵌入式語言寫的腳本,如使用 lua()、luarun()、jsrun 等默認尋找的路徑
storage     語言留言(Voicemail)的錄音
conf        配置文件,詳見下節(jié)

   配置文件

   配置文件由許多 XML 文件組成。在系統(tǒng)裝載時,XML解析器會將所有XML文件組織在一起,并讀入內(nèi)存,稱為XML注冊表。這種設(shè)計的好處在于其非常高的可擴展性。由于XML文檔本身非常適合描述復(fù)雜的數(shù)據(jù)結(jié)構(gòu),在 FreeSWITCH 中 就可以非常靈活的使用這些數(shù)據(jù)。并且,外部應(yīng)用程序也可以很簡單地生成XML,F(xiàn)reeSWITCH 在需要時可以動態(tài)的裝載這些 XML。另外,系統(tǒng)還允許在某些 XML 節(jié)點上安裝回調(diào)程序(函數(shù)),當這些節(jié)點的數(shù)據(jù)變化時,系統(tǒng)便自動調(diào)用這些回調(diào)程序。

使用 XML 唯一的不足就是手工編輯這些 XML 比較困難,但正如其作者所言,他絕對不是 XML 的粉絲,但這一缺點與它所帶來的好處相比是微不足道的。而且,將來也許會有圖形化的配置工具,到時候只所高級用戶會去看這些XML了。

   目錄結(jié)構(gòu)

   配置文件的的目錄結(jié)構(gòu)如下(其中結(jié)尾有 “/” 的為目錄):

autoload_configs/
dialplan/
directory/
extensions.conf
freeswitch.xml
fur_elise.ttml
jingle_profiles/
lang/
mime.types
notify-voicemail.tpl
sip_profiles/
tetris.ttml
vars.xml
voicemail.tpl
web-vm.tpl

   其中最重要的是 freeswitch.xml,就是它將所有配置文件“粘”到一起。只要有一點 XML 知識,這些配置是很容易看懂的。其中,X-PRE-PROCESS標簽稱為預(yù)處理命令,它用來設(shè)置一些變量和裝入其它配置文件。在 XML 加載階段,F(xiàn)reeSWITCH 的 XML 解析器會先將預(yù)處理命令進行展開,生成一個大的 XML 文件 log/freeswitch.xml.fsxml。該文件是一個內(nèi)存鏡像,用戶不應(yīng)該手工編輯它。但它對調(diào)試非常有用,假設(shè)你不慎弄錯了某個標簽,又不知道它在哪個地方,F(xiàn)reeSWITCH 在加載時就報 XML 的某一行出錯,在該文件中就行容易找到。

   整個XML文件分為幾個重要的部分:configuration (配置)、dialplan (撥號計劃)、directory(用戶目錄)及phrase(分詞)。每一部分又分別裝入不同的 XML。

    小知識:XML

XML由標簽(Tag)和屬性構(gòu)成。<tag> 和 </tag>組成一對標簽,如果該標簽有相關(guān)屬性,剛以
<tag attr="value"></tag> 形式指定。有些標簽無須配對,則必須以 “/>”關(guān)閉該標簽定義,
如<other\_tag attr="value"/>。

freeswitch.xml
<?xml version="1.0"?>
<document type="freeswitch/xml">
    <!-- #comment 這是一個配置文件,本行是注釋 -->

    <X-PRE-PROCESS cmd="include" data="vars.xml"/>

    <section name="configuration" description="Various Configuration">
        <X-PRE-PROCESS cmd="include" data="autoload_configs/*.xml"/>
    </section>
</document>

   上面是一個精減了的 freeswitch.xml。它的根是 document,在 document 中,有許多 section,每個 section 都對應(yīng)一部分功能。其中有兩個 X-PRE-PROCESS 預(yù)處理指令,它們的作用是將 data 參數(shù)指定的文件包含(include)到本文件中來。由于它是一個預(yù)處理指令,F(xiàn)reeSWITCH 在加載階段只對其進行簡單替換,并不進行語法分析,因此,對它進行注釋是沒有效果的,這是一個新手常犯的錯誤。假設(shè) vars.xml 的內(nèi)容如下,它是一個合法的 XML:

<!-- this is vars.xml -->
<var>xxxxx</var>

若你在調(diào)試階段想把一條 X-PRE-PROCESS 指令注釋掉:

<!-- <X-PRE-PROCESS cmd="include" data="vars.xml"/> -->

   當 FreeSWITCH 預(yù)處理時,還沒有到達 XML 解析階段,也就是說它還不認識 XML 注釋語法,而僅會機械地將預(yù)處理指令替換為 vars.xml 里的內(nèi)容:

<!-- <!-- this is vars.xml -->
<var>xxxxx</var> -->                                                 

   由于 XML 的注釋不能嵌套,因此便產(chǎn)生錯誤的XML。解決辦法是破壞掉 X-PRE-PROCESS 的定義,如我常用下面兩種方法:

<xX-PRE-PROCESS cmd="include" data="vars.xml"/>
<XPRE-PROCESS cmd="include" data="vars.xml"/>

   由于 FreeSWITCH 不認識 xX-PRE-PROCESS 及 XPRE-PROCESS,因此它會忽略掉該行,相當于注釋掉了。

   vars.xml

   vars.xml 主要通過 X-PRE-PROCESS 指令定義了一些全局變量。全局變量以 $${var} 表示,臨時變量以 ${var} 表示。有些變量是系統(tǒng)在運行時自動獲取的,如默認情況下 $${base_dir}=/usr/local/freeswitch, $${local_ip_v4}=你機器的IP地址等。

   autoload_configs 目錄

   autoload_configs目錄下面的各種配置文件會在系統(tǒng)啟動時裝入。一般來說都是模塊級的配置文件,每個模塊對應(yīng)一個。文件名一般以 模塊名.conf.xxml 方式命名。其中 modules.conf.xml 決定了 FreeSWITCH 啟動時自動加載哪些模塊。

    dialplan 目錄

   定義 XML 撥號計劃,我們會有專門的章節(jié)講解撥號計劃。

   directory 目錄

   它里面的配置文本決定了 FreeSWITCH 作為注冊服務(wù)器時哪些用戶可以注冊上來。FreeSWITCH 支持多個域(Domain),每個域可以寫到一個 XML 文件里。默認的配置包括一個 default.xml,里面定義了 1000 ~ 1019 一共 20 個用戶。

   sip_profiles

   它定義了 SIP 配置文件,實際上它是由 mod_sofia 模塊在 autoload_configs/sofia.conf.xml 中加載的。但由于它本身比較復(fù)雜又是核心的功能,因此單列了一個目錄。我們將會在后面加以詳細解釋。

   XML 用戶目錄

   XML 用戶目錄決定了哪些用戶可以注冊到 FreeSWITCH 上。當然,SIP 并不要求一定要注冊才可以打電話,但是用戶認證仍需要在用戶目錄中配置。

   用戶目錄的默認配置文件在 conf/directory/,系統(tǒng)自帶的配置文件為 default.xml(其中 dial-string 一行由于排版要求人工換行,實際上不應(yīng)該有換行):

<domain name="$${domain}">
  <params>
    <param name="dial-string" value="{presence_id=${dialed_user}@${dialed_domain}}
        ${sofia_contact(${dialed_user}@${dialed_domain})}"/>
  </params>

  <variables>
    <variable name="record_stereo" value="true"/>
    <variable name="default_gateway" value="$${default_provider}"/>
    <variable name="default_areacode" value="$${default_areacode}"/>
    <variable name="transfer_fallback_extension" value="operator"/>
  </variables>

</domain>

   該配置文件決定了哪些用戶能注冊到 FreeSWITCH 中。一般來說,所有用戶都應(yīng)該屬于同一個 domain(除非你想使用多 domain,后面我們會有例子)。這里的 $${domain} 全局變量是在 vars.xml 中設(shè)置的,它默認是主機的 IP 地址,但也可以修改,使用一個域名。params 中定義了該 domain 中所有用戶的公共參數(shù)。在這里只定義了一個 dial-string,這是一個至關(guān)重要的參數(shù)。當你在使用 user/user_name 或 sofia/internal/user_name 這樣的呼叫字符串時,它會擴展成實際的 SIP 地址。其中 sofia_contact 是一個 API,它會根據(jù)用戶的注冊地址擴展成相應(yīng)的呼叫字符串。

   variables 則定義了一些公共變量,在用戶做主叫或被叫時,這些變量會綁定到相應(yīng)的 Channel 上形成 Channel Variable。

    在 domain 中還定義了許多組(group),組里面包含很多用戶(user)。

<groups>
  <group name="default">
    <users>
      <X-PRE-PROCESS cmd="include" data="default/*.xml"/>
    </users>
  </group>
</groups>              

   在這里,組名 default 并沒有什么特殊的意義,它只是隨便起的,你可以修改成任何值。在用戶標簽里,又使用預(yù)處理指令裝入了 default/ 目錄中的所有 XML 文件。你可以看到,在 default/ 目錄中,每個用戶都對應(yīng)一個文件。

   你也可以定義其它的用戶組,組中的用戶并不需要是完整的 XML 節(jié)點,也可以是一個指向一個已存在用戶的“指針”,如下圖,使用 type="pointer" 可以定義指針。

  <group name="sales">
    <users>
      <user id="1000" type="pointer"/>
      <user id="1001" type="pointer"/>
      <user id="1002" type="pointer"/>
    </users>
  </group>

   雖然我們這里設(shè)置了組,但使用組并不是必需的。如果你不打算使用組,可以將用戶節(jié)點(users)直接放到 domain 的下一級。但使用組可以支持像群呼、代接等業(yè)務(wù)。使用 group_call 可以同時或順序的呼叫某個組的用戶。

   實際用戶相關(guān)的設(shè)置也很直觀,下面顯示了 alice 這個用戶的設(shè)置:

<user id="alice">
  <params>
    <param name="password" value="$${default_password}"/>
    <param name="vm-password" value="alice"/>
  </params>
  <variables>
    <variable name="toll_allow" value="domestic,international,local"/>
    <variable name="accountcode" value="alice"/>
    <variable name="user_context" value="default"/>
    <variable name="effective_caller_id_name" value="Extension 1000"/>
    <variable name="effective_caller_id_number" value="1000"/>
    <variable name="outbound_caller_id_name" value="$${outbound_caller_name}"/>
    <variable name="outbound_caller_id_number" value="$${outbound_caller_id}"/>
    <variable name="callgroup" value="techsupport"/>
  </variables>
</user>

   由上面可以看到,實際上 params 和 variables 可以出現(xiàn)在 user 節(jié)點中,也可以出現(xiàn)在 group 或 domain 中。 當它們有重復(fù)時,優(yōu)先級順序為 user,group,domain。

   當然,用戶目錄還有一些更復(fù)雜的設(shè)置,我們留待以后再做研究。

   呼叫流程及相關(guān)概念

   再復(fù)習一下,F(xiàn)reeSWITCH是一個B2BUA,我們還是以第四章中的圖為例: 

   主要呼叫流程有以下兩種:

   bob 向 FreeSWITCH 發(fā)起呼叫,F(xiàn)reeSWTICH 接著啟動另一個 UA 呼叫 alice,兩者通話;

   FreeSWITCH 同時呼叫 bob 和 alice,兩者接電話后 FreeSWITCH 將 a-leg 和 b-leg 橋接(bridge)到一起,兩者通話。

   其中第二種又有一種變種。如市場上有人利用上、下行通話的不對稱性賣電話回撥卡獲取不正當利潤:bob 呼叫 FreeSWITCH,F(xiàn)reeSWITCH 不應(yīng)答,而是在獲取 bob 的主叫號碼后直接掛機;然后 FreeSWITCH 回撥 bob;bob 接聽后 FreeSWITCH 啟動一個 IVR 程序指示 bob 輸入 alice 的號碼;然后 FreeSWITCH 呼叫 Alice……

   在實際應(yīng)用中,由于涉及回鈴音、呼叫失敗等,實際情況要復(fù)雜的多。

   Session 與 Channel

   對每一次呼叫,F(xiàn)reeSWITCH 都會啟動一個 Session(會話,它包含SIP會話,SIP會在每對UAC-UAS之間生成一個 SIP Session),用于控制整個呼叫,它會一直持續(xù)到通話結(jié)束。其中,每個 Session 都控制著一個 Channel(信道),Channel 是一對 UA 間通信的實體,相當于 FreeSWITCH 的一條腿(leg),每個 Channel 都有一個唯一的 UUID。另外,Channel 上可以綁定一些呼叫參數(shù),稱為 Channel Variable(信道變量)。Channel 中可能包含媒體(音頻或視頻流),也可能不包含。通話時,F(xiàn)reeSWITCH 的作用是將兩個 Channel(a-leg 和 b-leg,通常先創(chuàng)建的或占主動的叫 a-leg)橋接(bridge)到一起,使雙方可以通話。

   通話中,媒體(音頻或視頻)數(shù)據(jù)流在 RTP 包中傳送(不同于 SIP, RTP是另外的協(xié)議)。一般來說,Channel是雙向的,因此,媒體流會有發(fā)送(Send/Write)和接收(Receive/Read)兩個方向。

   回鈴音與 Early Media

A  ------ |a 交換機 | ---X--- | 交換機 b| -------- B

   為了便于說明,我們假定A與B不在同一臺服務(wù)器上(如在PSTN通話中可能不在同一座城市),中間需要經(jīng)過多級服務(wù)器的中轉(zhuǎn)。

   假設(shè)上圖是在 PSTN 網(wǎng)絡(luò)中,A 呼叫 B,B 話機開始振鈴,A 端聽回鈴音(Ring Back Tone)。在早期,B 端所在的交換機只給 A 端交換機送地址全(ACM)信號,證明呼叫是可以到達 B 的,A 端聽到的回鈴音鈴流是由 A 端所在的交換機生成并發(fā)送的。但后來,為了在 A 端能聽到 B 端特殊的回鈴音(如“您撥打的電話正在通話中…” 或 “對方暫時不方便接聽您的電話” 尤其是現(xiàn)代交換機支持各種個性化的彩鈴 - Ring Back Color Tone 等),回鈴音就只能由 B 端交換機發(fā)送。在 B 接聽電話前,回鈴音和彩鈴是不收費的(不收取本次通話費。彩鈴費用一般是在 B 端以月租或套餐形式收取的)。這些回鈴音就稱為 Early Media(早期媒體)。它是由 SIP 的183(帶有SDP)消息描述的。

   理論上講,B 接聽電話后交換機 b 可以一直不向 a 交換機發(fā)送應(yīng)答消息,而將真正的話音數(shù)據(jù)偽裝成 Early Media,以實現(xiàn)“免費通話”。

   Channel Variable

   在每一個 Channel 上都可以設(shè)置好多 Variable,稱為信道變量。FreeSWITCH 呼叫過程中,會根據(jù)這些變量控制 Channel 的行為。

   $${var} 與 ${var}

   ${var} 是在 dialplan、application 或 directory 中設(shè)置的變量,它會影響呼叫流程并且可以動態(tài)的改變。而 $${var} 則是全局的變量,它僅在預(yù)處理階段(系統(tǒng)啟動時,或重新裝載 - reloadxml時)被求值。后者一般用于設(shè)置一些系統(tǒng)一旦啟動就不會輕易改變的量,如 $${domain} 或 $${local_ip_v4}等。所以,兩者最大的區(qū)別是,$${var} 只求值一次,而 ${var} 則在每次執(zhí)行時求值(如一個新電話進來時)。

   $variable_xxxx

   你會發(fā)現(xiàn),有些變量在顯示時(可以使用dp_tools 中的 info() 顯示,后面會講到)是以“variable_”開頭的,但在實際引用時要去掉這開頭的“variable_”。如“variable_user_name”,引用時要使用“${user_name}”。http://wiki.freeswitch.org/wiki/Channel_Variables#variable_xxxx 列舉了一些常見的變量顯示與引用時的對應(yīng)關(guān)系。

   給 Variable 賦值

   在 dialplan 中,有兩個程序可以給 Variable 賦值:

<action application="set" data="my_var=my_value"/>
<action application="export" data="my_var=my_value"/>

以上兩條命令都可以設(shè)置 my_var 變量的值為 my_value。不同的是 -- set 程序僅會作用于“當前”的 Channel (a-leg),而 export 程序則會將變量設(shè)置到兩個 Channel (a-leg 和 b-leg)上,如果當時 b-leg 還沒有創(chuàng)建,則會在創(chuàng)建時設(shè)置。另外,export 還可以只將變量設(shè)置到 b-leg 上:

<action appliction="export" data="nolocal:my_var=my_value"/>

在實際應(yīng)用中,如果 a-leg 上已經(jīng)有一些變量的值(如 var1、var2、var3),而想同時把這些變量都復(fù)制到 b-leg 上,可以使用以下幾種辦法:

<action application="export" data="var1=$var1"/>
<action application="export" data="var2=$var2"/>
<action application="export" data="var3=$var3"/>

或者使用如下等價的方式:

<action application="set" data="export_vars=var1,var2,var3">

所以,其實 set 也具有能往 b-leg 上賦值的能力,其實,它和 export 一樣,都是操作 export_vars 這個特殊的變量。

取消 Variable 定義
取消 Variable 定義只需對它賦一個特殊的值_undef_”:

<action application="set" data="var1=_undef_">

截取 Variable 的一部分
可以使用特殊的語法取一個 Variable 的子串,格式是“${var:位置:長度}”。其中 “位置” 從 0 開始計烽,若為負數(shù)則從字符串尾部開始計數(shù);如果“長度”為 0 或小于 0,則會從當前“位置”一直取到字符串結(jié)尾(或開頭,若“位置”為負的話)。例如 var 的值為 1234567890,那么:

${var}      = 1234567890
${var:0:1}  = 1
${var:1}    = 234567890
${var:-4}   = 7890
${var:-4:2} = 78
${var:4:2}  = 56

   小結(jié)

    本章描述了 FreeSWITCH 的架構(gòu)。到這里,讀者應(yīng)該對 FreeSWITCH 有了一個總體的了解。我們也提到了一些基本元素和概念,簡單介紹了配置文件的基本結(jié)構(gòu),由于脫離了實際單講配置會比較抽象,因此,我們把具體的配置也寫到后面的章節(jié)里,即,用到時再說。


 

分享到: 收藏

專題