• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            網絡服務器軟件開發/中間件開發,關注ACE/ICE/boost

            C++博客 首頁 新隨筆 聯系 聚合 管理
              152 Posts :: 3 Stories :: 172 Comments :: 0 Trackbacks

            #

            Base64編碼其實是將3個8位字節轉換為4個6位字節,( 3*8 = 4*6 = 24 ) 這4個六位字節
            其實仍然是8位,只不過高兩位被設置為0. 當一個字節只有6位有效時,它的取值空間為0
            到 2的6次方減1 即63,也就是說被轉換的Base64編碼的每一個編碼的取值空間為(0~63)

            事實上,0~63之間的ASCII碼有許多不可見字符,所以應該再做一個映射,映射表為
            ‘A‘ ~ ‘Z‘ ? ASCII(0 ~ 25)
            ‘a’ ~ ‘z‘ ? ASCII(26 ~ 51)
            ‘0’ ~ ‘9‘ ? ASCII(52 ~ 61)
            ‘+‘ ? ASCII(62)
            ‘/‘ ? ASCII(63)
            這樣就可以將3個8位字節,轉換為4個可見字符。
            具體的字節拆分方法為:(圖(畫得不好,領會精神 :-))
            aaaaaabb ccccdddd eeffffff
            ~~~~~~~~ ~~~~~~~~ ~~~~~~~~
            字節 1 字節 2 字節 3
            ||
            \/
            00aaaaaa 00bbcccc 00ddddee 00ffffff

            注:上面的三個字節位原文,下面四個字節為Base64編碼,其前兩位均為0。
            這樣拆分的時候,原文的字節數量應該是3的倍數,當這個條件不能滿足時,用全零字節
            補足,轉化時Base64編碼用=號代替,這就是為什么有些Base64編碼以一個或兩個等號結
            束的原因,但等號最多有兩個,因為:如果F(origin)代表原文的字節數,F(remain)代
            表余數,則
            F(remain) = F(origin) MOD 3 成立。
            所以F(remain)的可能取值為0,1,2.
            如果設 n = [F(origin) – F(remain)] / 3
            當F(remain) = 0 時,恰好轉換為4*n個字節的Base64編碼。
            當F(remain) = 1 時,由于一個原文字節可以拆分為屬于兩個Base64編碼的字節,為了
            讓Base64編碼是4的倍數,所以應該為補2個等號。
            當F(remain) = 2 時,由于兩個原文字節可以拆分為屬于3個Base64編碼的字節,同理,
            應該補上一個等號 


            posted @ 2007-04-05 16:43 true 閱讀(646) | 評論 (0)編輯 收藏

            一、      TinyXml的特點

            TinyXml是一個基于DOM模型的、非驗證的輕量級C++解釋器。

            1.      SAX和DOM

            目前XML的解析主要有兩大模型:SAX和DOM。

            其中SAX是基于事件的,其基本工作流程是分析XML文檔,當發現了一個新的元素時,產生一個對應事件,并調用相應的用戶處理函數。這種方式占用內存少,速度快,但用戶程序相應得會比較復雜。

            而DOM(文檔對象模型),則是在分析時,一次性的將整個XML文檔進行分析,并在內存中形成對應的樹結構,同時,向用戶提供一系列的接口來訪問和編輯該樹結構。這種方式占用內存大,速度往往慢于SAX,但可以給用戶提供一個面向對象的訪問接口,對用戶更為友好。

             

            2.      驗證和非驗證

            對于一個特定的XML文檔而言,其正確性分為兩個層次。首先是其格式應該符合XML的基本格式要求,比如第一行要有聲明,標簽的嵌套層次必須前后一致等等,符合這些要求的文件,就是一個合格的XML文件,稱作well-formatted。但除此之外,一個XML文檔因其內容的不同還必須在語義上符合相應的標準,這些標準由相應的DTD文件或者Schema文件來定義,符合了這些定義要求的XML文件,稱作valid。

            因此,解析器也分為兩種,一種是驗證的,即會跟據XML文件中的聲明,用相應的DTD文件對XML文件進行校驗,檢查它是否滿足DTD文件的要求。另一種是忽略DTD文件,只要基本格式正確,就可以進行解析。

            就我所知,驗證的解析器通常都是比較重量級的。TinyXml不支持驗證,但是體積很小,用在解析格式較為簡單的XML文件,比如配置文件時,特別的合適。

             

            二、 TinyXml的構建和使用
            1.      獲取

            TinyXml首頁在http://www.grinninglizard.com/tinyxml/index.html,從這里可以找到最新版本的源代碼,目前的版本是2.3.4。

            2.構建

            TinyXml在構建時可以選擇是否支持STL,選擇的話,則可以使用std::string,所以通常應該打開這個選項。

            在Windows上,TinyXml的源碼包里提供了VC6的工程文件,直接用它就可以生成兩個靜態庫(帶STL和不帶STL),非常容易。唯一需要注意的是,默認生成的庫是單線程的,如果用在多線程的項目中,需要改動一下配置,生成相應的多線程庫。

            在Unix平臺上,TinyXml的源碼包里只提供了一個Makefile,對于典型的Linux系統,或裝了gcc和gmake的其他Unix,這個Makefile足夠用了,我在RH9和RHEL4上測試,簡單的make就成功了。需要注意的有以下幾點:默認的編譯是不支持STL的,可以通過編輯Makefile的TINYXML_USE_STL := NO那一行,把NO改成YES就可以支持STL了;還有默認只生成了一個測試程序,沒有生成任何庫,如果要生成靜態庫的話,可以用ar命令,將生成的幾個目標文件打包就行了,如果要生成動態庫,則需要加上-fpic參數重新編譯。

            3.      使用

            構建了相應的庫之后,在使用了它們的工程中,只要在連接時把他們連上就行了。需要注意的是,如果需要STL支持,在編譯用到了TinyXml的文件時,需要定義一個宏TIXML_USE_STL,對gcc,可以使用參數-DTIXML_USE_STL,對cl.exe(VC),可以使用參數/DTIXML_USE_STL,如果嫌麻煩,可以直接定義在 tinyxml.h文件里。

             

            三、 TinyXml的編程模型1.      類之間的關系

            TinyXml實現的時DOM訪問模型,因此提供了一系列的類對應XML文件中的各個節點。主要類間的關系如下圖所示:

             

             

            TiXmlBase:其他類的基類,是個抽象類

            TiXmlNode:表示一個節點,包含節點的一般方法,如訪問自節點、兄弟節點、編輯自身、編輯子節電

            TiXmlDocument:表示整個XML文檔,不對應其中某個特定的節點。

            TiXmlElement:表示元素節點,可以包含子節點和TiXmlAttribute

            TiXmlComment:表示注釋

            TiXmlDeclaration:表示聲明

            TiXmlText:表示文本節點

            TiXmlUnknown:表示未知節點,通常是出錯了

            TiXmlAttribute:表示一個元素的屬性

            下面是一個簡單的例子:

            <?xml version="1.0" encoding="utf-8" ?>

             

             

            <!-This is only a sample-->

             

             

            <book>

             

             

                   <name>TinyXml How To</name>

             

             

                   <price unit=”RMB”>20</price>

             

             

                   <description>Some words…</description>

             

             

            </ book >

             

             

            整個文檔,對應TiXmlDocument

            book,name,price, description,都對應TiXmlElement

            第一行對應一個TiXmlDeclaration

            第二行對應一個TiXmlComment

            “TinyXml How To”對應一個TiXmlText

            unit則是price的一個TiXmlAttribute

            這些類與XML文件中的相應元素都有很好的對應關系,因此相信參照TinyXml的文檔,可以很容易的掌握各個方法的使用。

             

            2.  需要注意的問題

            各類之間的轉換

             

             

            由于各個節點類都從TiXmlNode繼承,在使用時常常需要將TiXmlNode*類型的指針轉換為其派生類的指針,在進行這種轉換時,應該首先使用由TiXmlNode類提供的一系列轉換函數,如ToElement(void),而不是c++的dynamic_cast

             

            檢查返回值

             

             

            由于TinyXml是一個非校驗的解析器,因此當解析一個文件時,很可能文件并不包含我們預期的某個節點,在這種情況下,TinyXml將返回空指針。因此,必須要對返回值進行檢查,否則將很容易出現內存訪問的錯誤。

             

            如何重頭建立一個XML文件

             

             

            先建立一個TiXmlDocument對象,然后,載入某個模板,或者直接插入一個節點作為根節點,接著就可以像打開一個已有的XML文件那樣對它進行操作了。

             

            四、總結

            TinyXml最大的特點就是它很小,可以很方便的靜態連接到程序里。對于像配置文件、簡單的數據文件這類文件的解析,它很適合。但是由于它是非驗證的,因此需要在程序里做許多檢查工做,加重了程序編寫的負擔。因此對于復雜的XML文件,我覺得最好還是用驗證的解析器來處理。
             



            Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1006509


            posted @ 2007-04-05 14:10 true 閱讀(580) | 評論 (1)編輯 收藏

            通用Makefile及部分解釋

            ######################################
            # Copyright (c) 1997 George Foot (george.foot@merton.ox.ac.uk)
            # All rights reserved.
            ######################################
            #目標(可執行文檔)名稱,庫(譬如stdcx,iostr,mysql等),頭文件路徑
            DESTINATION := test
            LIBS :=
            INCLUDES := .


            RM := rm -f
            #C,CC或CPP文件的后綴
            PS=cpp
            # GNU Make的隱含變量定義
            CC=g++
            CPPFLAGS = -g -Wall -O3 -march=i486
            CPPFLAGS += $(addprefix -I,$(INCLUDES))
            CPPFLAGS += -MMD

            #以下部分無需修改
            SOURCE := $(wildcard *.$(PS))
            OBJS := $(patsubst %.$(PS),%.o,$(SOURCE))
            DEPS := $(patsubst %.o,%.d,$(OBJS))
            MISSING_DEPS := $(filter-out $(wildcard $(DEPS)),$(DEPS))
            MISSING_DEPS_SOURCES := $(wildcard $(patsubst %.d,%.$(PS),$(MISSING_DEPS)))

            .PHONY : all deps objs clean rebuild

            all : $(DESTINATION)

            deps : $(DEPS)
                    $(CC) -MM -MMD $(SOURCE)


            objs : $(OBJS)

            clean :
                    @$(RM) *.o
                    @$(RM) *.d
                    @$(RM) $(DESTINATION)

            rebuild: clean all

            ifneq ($(MISSING_DEPS),)
            $(MISSING_DEPS) :
                    @$(RM) $(patsubst %.d,%.o,$@)
            endif

            -include $(DEPS)

            $(DESTINATION) : $(OBJS)
                    $(CC) -o $(DESTINATION) $(OBJS) $(addprefix -l,$(LIBS))
            #結束

            • 原作者是Gorge Foot,寫這個Makefile的時候還是一個學生
            • ":="賦值,和"="不同的是,":="在賦值的同時,會將賦值語句中所有的變量就地展開,也就是說,A:=$(B)后,B的值的改變不再影響A
            • 隱含規則。GUN Make在不特別指定的情況下會使用諸如以下編譯命令:$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@,這也是為什么這個Makefile最后一個命令沒有添加$(CPPFLAGS)的原因,因為缺省是包含這個變量的
            • 函數和變量很相似:"$ (函數名,空格,一列由逗號分隔的參數)"
            • SOURCES = $(wildcard *.cpp) 列出工作目錄下文件名滿足"*.cpp"條件的文件,以空格分隔,并將列表賦給SOURCE變量
            • patsubst函數:3個參數。功能是將第三個參數中的每一項(由空格分隔)符合第一個參數描述的部分替換成第二個參數制定的值
            • addprefix函數:2個參數。將源串(第2個參數,由空格分隔)中的每一項添加前綴(第1個參數)
            • filter-out函數:2個參數。從第二串中過濾掉包含在第一個串中的項
            • $(CC) -MM -MMD $(SOURCE) : 對每個源文件生成依賴(dependence,Make通過依賴規則來判斷是否需要重新編譯某個文件),"D"生成".d"文件,-MM表示去掉 depends里面的系統的頭文件(使用<>包含的頭文件)(若使用-M則全部包含,事實上,系統頭文件被修改的可能性極小,不需要執行依賴檢查)
            • .PHONY,不檢查后面制定各項是否存在同名文件
            • ifneg...else...endif,Makefile中的條件語句
            • -include $(DEPS) : 將DEPS中的文件包含進來,"-"表示忽略文件不存在的錯誤
            • @$(RM) *.o : 開頭的"@"表示在Make的時候,不顯示這條命令(GNU Make缺省是顯示的)
            • all : 作為第一個出現的目標項目,Make會將它作為主要和缺省項目("make"就表示"make all")
            • deps : 只生成依賴文件(.d文件)
            • objs : 為每一個源碼程序生成或更新 '.d' 文件和'.o'文件
            • clean : 刪除所有'.d','.o'和可執行文件
            • rebuild : clean然后重建
            • 內部變量$@, $< $^ : 分別表示目標名(:前面的部分,比如all),依靠列表(:后面的部分)中的第一個依靠文件,所有依靠文件

            posted @ 2007-04-05 12:01 true 閱讀(449) | 評論 (0)編輯 收藏

             
              · 閆健勇·CPCW
              
                當前,雖然Linux還不很普及,在Linux下編寫和編譯程序的人不多。但是我相信,隨著Linux性能的不斷提升和逐漸普及,會有許多自由軟件出現,也會有許多人成為Linux下的程序員。我結合自己的經驗,介紹一下Linux下編寫和編譯程序所要注意的幾個問題,奉獻給希望為Linux的發展作出貢獻的人們。
              
              
              Linux下怎樣編譯程序?
              
              大多數Linux程序都是由C語言編寫的并由GNU C編譯而成。現在GCC是各種發行套件的一部分。有關最新GCC編譯器的版本、文章和補丁請看ftp://ftp.gnu.org/pub/gnu/。
              
              由C++編寫的程序必須由GNU C++編譯,GNU C++也是各種發行套件的一部分,在以上網址也有最新版本和補丁。
              
              編譯2.0.x的內核需要2.7.2.x版本的GCC,如用象GCC 2.8.x, EGCS, or PGCC別的編譯器編譯內核可能產生不可預想的后果。
              
              
              怎樣移植其它Unix程序到Linux上?
              
              總得來說,Unix上的程序不需要做改動,只要簡單的按照提示就可以移植到Linux上,如果安裝過程中出現錯誤信息,而你又不知道怎么處理,你可以猜或略去,不過這樣產生的程序往往帶有bug。所以最好還是問一下有經驗的人。
              
              如果要從BSD-ish移植程序,試一試在編譯時加上-I/usr/include/bsd 和 ?lbsd命令。
              
              
              什么是ld.so,從哪可以找到它?
              
              ld.so是動態函數庫裝載器。過去,使用共享函數庫的程序在原代碼開頭使用約3K的空間來尋找和加載共享函數庫,現在,這段代碼被加進了一個特殊共享函數庫/lib/ld.so,所有的程序都可以使用該共享庫,這樣就節省了磁盤空間,而且升級方便。
              
              ld.so可以從以下網址得到tsx-11.mit.edu/pub/linux/packages/GCC/。
              
              
              怎樣升級庫函數而不使系統崩潰?
              
              注意:進行此操作應該養成做備份的習慣,因為這項操作很容易出錯。
              
              如果你升級象libc4這樣的老函數庫,這個過程會變得非常困難。而且你應該在該系統上讓libc4和libc5共存,因為,有些老程序還需要它。升級libc5也一樣。
              
              升級動態庫的問題常出現在當你移走老的函數庫時,用來升級的程序也運行不了了。有許多方法可以解決這個問題。一個方法就是暫時備份一下運行程序所需函數庫,它們一般在/lib/、/usr/lib/、 /usr/local/lib/、或其它的地方,在文件/etc/ld.so.conf中都有詳細記錄。
              
              例如,當你升級libc5時,目錄/lib/中有如下文件
              
              libc.so.5
              
              libc.so.5.4.33
              
              libm.so.5
              
              libm.so.5.0.9
              
              這些是C函數庫和數學庫,拷貝它們到文件/etc/ld.so.conf中含有的其它的目錄,如/usr/lib/中:
              
              cp -df /lib/libc.so.5* /usr/lib/
              
              cp -df /lib/libm.so.5* /usr/lib/
              
              ldconfig
              
              一定要記住運行ldconfig來升級函數庫的配置文件。
              
              文件libc.so.5 和 libm.so.5是實際庫文件的鏈接文件,當你升級的時候,如果老的鏈接文件存在,新的鏈接不會產生,除非你使用CP命令的-f選項。CP的-d選項只復制鏈接文件,不復制原文件。
              
              如果你需要直接覆蓋鏈接,使用ln命令的選項-f。
              
              例如,拷貝新的庫函數覆蓋舊的。先對新的函數庫做一個鏈接,然后把函數庫和鏈接一起拷貝到/lib/中,命令如下:
              
              ln -sf ./libm.so.5.0.48 libm.so.5
              
              ln -sf ./libc.so.5.0.48 libc.so.5
              
              cp -df libm.so.5* /lib
              
              cp -df libc.so.5* /lib
              
              重申一下,拷貝完別忘記運行ldconfig.
              
              如果一切工作順利的話,你可以刪除老的函數庫的備份。
              
              
              我能否把在486上編譯的代碼或編譯器拿到386上用?
              
              當然,除非你編譯的是內核。
              
              GCC用來在486上編譯的選項-m486 只是優化了所編譯程序,使其運行快一些。這些編譯的程序仍能很好的在386上運行,只是效果差一些。
              
              然而,從內核1.3.35以后,采用486或Pentium選項編譯的內核不能用于386的機器。
              
              GCC可以針對386和486進行配置。兩者區別在于,針對386配置的GCC把-m386作為缺省選項,而針對486配置的GCC把-m486作為缺省選項,僅此而已。
              
              
              gcc -O6可以干什么?
              
              目前,它和 -O2 (GCC 2.5) 或 -O3 (GCC 2.6, 2.7)功能一樣,所有版本大于它的功能也一樣。新版內核的Makefiles使用-O2,所以你也應該用-O2。
              
              
              linux/*.h 和asm/*.h在什么地方?
              
              目錄 /usr/include/linux/ 和 /usr/include/asm/低下的文件是內核頭文件的軟鏈接,內核頭文件其實在目錄/usr/src/kernel*/低下。
              
              
              怎樣作一個共享函數庫?
              
              對ELF, 命令如下:
              
              gcc -fPIC -c *.c
              
              gcc -shared -Wl,-soname,libfoo.so.1 -o libfoo.so.1.0 *.o
              
              對a.out,從 http://tsx-11.mit.edu/pub/linux/packages/GCC/src/ 下載n.nn.tar.gz,其中包含詳細說明。建議你將共享庫由a.out升級為ELF。
              
              
              為什么我編譯的可執行程序非常大?
              
              用ELF編譯器,生成可執行程序太大最可能的原因是沒有合適的.so庫與你使用的庫函數鏈接。對每一個象libc.so.5.2.18的函數庫,應該有一個象libc.so的鏈接。
              
              用a.out編譯器,生成可執行程序太大可能是使用了-g選項,這會生成靜態鏈接庫而不是動態鏈接庫。
              
              
              從哪可以得到對于Linux的‘lint’?
              
              大部分‘lint’的功能已經內置進了GCC,打開GCC的-Wall選項會打開許多有用的外部警告。
              
              還有一個叫`lclint'的軟件功能和傳統的lint差不多,原代碼可以在http://larch.lcs.mit.edu /pub/Larch/lclint/中找到。
                做人要厚道,請注明轉自酷網動力(www.ASPCOOL.COM)。
            posted @ 2007-04-05 10:34 true 閱讀(350) | 評論 (0)編輯 收藏

            摘要


            編寫連接數巨大的高負載服務器程序時,經典的多線程模式和select模式都不再適用。
            應當拋棄它們,采用epoll/kqueue/dev_poll來捕獲I/O事件。最后簡要介紹了AIO。


            由來


            網絡服務在處理數以萬計的客戶端連接時,往往出現效率低下甚至完全癱瘓,這被稱為
            C10K問題。隨著互聯網的迅速發展,越來越多的網絡服務開始面臨C10K問題,作為大型
            網站的開發人員有必要對C10K問題有一定的了解。本文的主要參考文獻是
            <http://www.kegel.com/c10k.htmlhttp://www.kegel.com/c10k.htmls

            C10K問題的最大特點是:設計不夠良好的程序,其性能和連接數及機器性能的關系往往
            是非線性的。舉個例子:如果沒有考慮過C10K問題,一個經典的基于select的程序能在
            舊服務器上很好處理1000并發的吞吐量,它在2倍性能新服務器上往往處理不了并發
            2000的吞吐量。

            這是因為在策略不當時,大量操作的消耗和當前連接數n成線性相關。會導致單個任務
            的資源消耗和當前連接數的關系會是O(n)。而服務程序需要同時對數以萬計的socket進
            行I/O處理,積累下來的資源消耗會相當可觀,這顯然會導致系統吞吐量不能和機器性
            能匹配。為解決這個問題,必須改變對連接提供服務的策略。


            基本策略


            主要有兩方面的策略:1.應用軟件以何種方式和操作系統合作,獲取I/O事件并調度多
            個socket上的I/O操作;2. 應用軟件以何種方式處理任務和線程/進程的關系。前者主
            要有阻塞I/O、非阻塞I/O、異步I/O這3種方案,后者主要有每任務1進程、每任務1線
            程、單線程、多任務共享線程池以及一些更復雜的變種方案。常用的經典策略如下:

            1.         Serve one client with each thread/process, and use blocking I/O
            這是小程序和java常用的策略,對于交互式的長連接應用也是常見的選擇(比如BBS)。
            這種策略很能難足高性能程序的需求,好處是實現極其簡單,容易嵌入復雜的交互邏
            輯。Apache、ftpd等都是這種工作模式。

            2.         Serve many clients with single thread, and use nonblocking I/O
            and readiness notification
            這是經典模型,datapipe等程序都是如此實現的。優點在于實現較簡單,方便移植,也
            能提供足夠的性能;缺點在于無法充分利用多CPU的機器。尤其是程序本身沒有復雜的
            業務邏輯時。

            3.         Serve many clients with each thread, and use nonblocking I/O and
            readiness notification
            對經典模型2的簡單改進,缺點是容易在多線程并發上出bug,甚至某些OS不支持多線程
            操作readiness notification。

            4.         Serve many clients with each thread, and use asynchronous I/O
            在有AI/O支持的OS上,能提供相當高的性能。不過AI/O編程模型和經典模型差別相當
            大,基本上很難寫出一個框架同時支持AI/O和經典模型,降低了程序的可移植性。在
            Windows上,這基本上是唯一的可選方案。

            本文主要討論模型2的細節,也就是在模型2下應用軟件如何處理Socket I/O。


            select 與 poll


            最原始的同步阻塞 I/O 模型的典型流程如下:

            同步阻塞 I/O 模型的典型流程

            從應用程序的角度來說,read 調用會延續很長時間,應用程序需要相當多線程來解決
            并發訪問問題。同步非阻塞I/O對此有所改進:

            經典的單線程服務器程序結構往往如下:


            do {

                     Get Readiness Notification of all sockets

                     Dispatch ready handles to corresponding handlers

                               If (readable) {
                                        read the socket

                                        If (read done)

                                                 Handler process the request
                               }

                               if (writable)

                                        write response

                               if (nothing to do)

                                        close socket

            } while(True)

            非阻塞 I/O 模型的典型流程:

            異步阻塞 I/O 模型的典型流程

            其中關鍵的部分是readiness notification,找出哪一個socket上面發生了I/O事件。
            一般從教科書和例子程序中首先學到的是用select來實現。Select定義如下:


            int select(int n, fd_set *rd_fds, fd_set *wr_fds, fd_set *ex_fds, struct
            timeval *timeout);

            Select用到了fd_set結構,從man page里可以知道fd_set能容納的句柄和FD_SETSIZE相
            關。實際上fd_set在*nix下是一個bit標志數組,每個bit表示對應下標的fd是不是在
            fd_set中。fd_set只能容納編號小于 FD_SETSIZE的那些句柄。

            FD_SETSIZE默認是1024,如果向fd_set里放入過大的句柄,數組越界以后程序就會垮
            掉。系統默認限制了一個進程最大的句柄號不超過1024,但是可以通過ulimit -n命令
            /setrlimit函數來擴大這一限制。如果不幸一個程序在FD_SETSIZE=1024的環境下編
            譯,運行時又遇到ulimit –n > 1024的,那就只有祈求上帝保佑不會垮掉了。


            在ACE環境中,ACE_Select_Reactor針對這一點特別作了保護措施,但是還是有recv_n
            這樣的函數間接的使用了select,這需要大家注意。

            針對fd_set的問題,*nix提供了poll函數作為select的一個替代品。Poll的接口如下:


            int poll(struct pollfd *ufds, unsigned int nfds, int timeout);

            第1個參數ufds是用戶提供的一個pollfd數組,數組大小由用戶自行決定,因此避免了
            FD_SETSIZE帶來的麻煩。Ufds是fd_set的一個完全替代品,從select到poll的移植很方
            便。到此為止,至少我們面對C10K,可以寫出一個能work的程序了。

            然而Select和Poll在連接數增加時,性能急劇下降。這有兩方面的原因:首先操作系統
            面對每次的select/poll操作,都需要重新建立一個當前線程的關心事件列表,并把線
            程掛在這個復雜的等待隊列上,這是相當耗時的。其次,應用軟件在select/poll返回
            后也需要對傳入的句柄列表做一次掃描來dispatch,這也是很耗時的。這兩件事都是和
            并發數相關,而I/O事件的密度也和并發數相關,導致CPU占用率和并發數近似成O(n2)
            的關系。


            epoll, kqueue, /dev/poll


            因為以上的原因,*nix的hacker們開發了epoll, kqueue, /dev/poll這3套利器來幫助
            大家,讓我們跪拜三分鐘來感謝這些大神。其中epoll是linux的方案,kqueue是
            freebsd的方案,/dev/poll是最古老的Solaris的方案,使用難度依次遞增。

            簡單的說,這些api做了兩件事:1.避免了每次調用select/poll時kernel分析參數建立
            事件等待結構的開銷,kernel維護一個長期的事件關注列表,應用程序通過句柄修改這
            個列表和捕獲I/O事件。2.避免了select/poll返回后,應用程序掃描整個句柄表的開
            銷,Kernel直接返回具體的事件列表給應用程序。

            在接觸具體api之前,先了解一下邊緣觸發(edge trigger)和條件觸發(level trigger)
            的概念。邊緣觸發是指每當狀態變化時發生一個io事件,條件觸發是只要滿足條件就發
            生一個io事件。舉個讀socket的例子,假定經過長時間的沉默后,現在來了100個字
            節,這時無論邊緣觸發和條件觸發都會產生一個read ready notification通知應用程
            序可讀。應用程序讀了50個字節,然后重新調用api等待io事件。這時條件觸發的api會
            因為還有50個字節可讀從而立即返回用戶一個read ready notification。而邊緣觸發
            的api會因為可讀這個狀態沒有發生變化而陷入長期等待。

            因此在使用邊緣觸發的api時,要注意每次都要讀到socket返回EWOULDBLOCK為止,否則
            這個socket就算廢了。而使用條件觸發的api時,如果應用程序不需要寫就不要關注
            socket可寫的事件,否則就會無限次的立即返回一個write ready notification。大家
            常用的select就是屬于條件觸發這一類,以前本人就犯過長期關注socket寫事件從而
            CPU 100%的毛病。

            epoll的相關調用如下:


            int epoll_create(int size)


            int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)


            int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int
            timeout)

            epoll_create創建kernel中的關注事件表,相當于創建fd_set。

            epoll_ctl修改這個表,相當于FD_SET等操作

            epoll_wait等待I/O事件發生,相當于select/poll函數

            epoll完全是select/poll的升級版,支持的事件完全一致。并且epoll同時支持邊緣觸
            發和條件觸發,一般來講邊緣觸發的性能要好一些。這里有個簡單的例子:


            struct epoll_event ev, *events;

            int kdpfd = epoll_create(100);

            ev.events = EPOLLIN | EPOLLET;  // 注意這個EPOLLET,指定了邊緣觸發

            ev.data.fd =listener;

            epoll_ctl(kdpfd, EPOLL_CTL_ADD, listener, &ev);

            for(;;) {

               nfds = epoll_wait(kdpfd, events, maxevents, -1);

             

               for(n = 0; n < nfds; ++n) {

                   if(events[n].data.fd == listener) {

                       client = accept(listener, (struct sockaddr *) &local,

                                       &addrlen);

                       if(client < 0){

                           perror("accept");

                           continue;

                       }

                       setnonblocking(client);

                       ev.events = EPOLLIN | EPOLLET;

                       ev.data.fd = client;

                       if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0) {

                           fprintf(stderr, "epoll set insertion error: fd=%d0,

                                   client);

                           return -1;

                       }

                   }

                   else

                       do_use_fd(events[n].data.fd);

               }

            }

            簡單介紹一下kqueue和/dev/poll

            kqueue是freebsd的寵兒,kqueue實際上是一個功能相當豐富的kernel事件隊列,它不
            僅僅是select/poll的升級,而且可以處理signal、目錄結構變化、進程等多種事件。
            Kqueue是邊緣觸發的

            /dev/poll是Solaris的產物,是這一系列高性能API中最早出現的。Kernel提供一個特
            殊的設備文件/dev/poll。應用程序打開這個文件得到操縱fd_set的句柄,通過寫入
            pollfd來修改它,一個特殊ioctl調用用來替換select。由于出現的年代比較早,所以
            /dev/poll的接口現在看上去比較笨拙可笑。

            C++開發:ACE 5.5以上版本提供了ACE_Dev_Poll_Reactor封裝了epoll和/dev/poll兩種
            api,需要分別在config.h中定義ACE_HAS_EPOLL和ACE_HAS_DEV_POLL來啟用。

            Java開發: JDK 1.6的Selector提供了對epoll的支持,JDK1.4提供了對/dev/poll的支
            持。只要選擇足夠高的JDK版本就行了。


            異步I/O以及Windows


            和經典模型不同,異步I/O提供了另一種思路。和傳統的同步I/O不同,異步I/O允許進
            程發起很多 I/O 操作,而不用阻塞或等待任何操作完成。稍后或在接收到 I/O 操作完
            成的通知時,進程就可以檢索 I/O 操作的結果。

            異步非阻塞 I/O 模型是一種處理與 I/O 重疊進行的模型。讀請求會立即返回,說明
            read 請求已經成功發起了。在后臺完成讀操作時,應用程序然后會執行其他處理操
            作。當 read 的響應到達時,就會產生一個信號或執行一個基于線程的回調函數來完成
            這次 I/O 處理過程。異步I/O 模型的典型流程:

            異步非阻塞 I/O 模型的典型流程

            對于文件操作而言,AIO有一個附帶的好處:應用程序將多個細碎的磁盤請求并發的提
            交給操作系統后,操作系統有機會對這些請求進行合并和重新排序,這對同步調用而言
            是不可能的——除非創建和請求數目同樣多的線程。

            Linux Kernel 2.6提供了對AIO的有限支持——僅支持文件系統。libc也許能通過來線
            程來模擬socket的AIO,不過這對性能沒意義。總的來說Linux的aio還不成熟

            Windows對AIO的支持很好,有IOCP隊列和IPCP回調兩種方式,甚至提供了用戶級異步調
            用APC功能。Windows下AIO是唯一可用的高性能方案,詳情請參考MSDN 

            Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1537545


            posted @ 2007-04-05 09:00 true 閱讀(1238) | 評論 (0)編輯 收藏

            在ACE中使用epoll
            2007-01-18 15:56

            星期四, 六月 8th, 2006 

                    很顯然,文章的標題決定了我們是在linux下使用ACE。我們知道ACE在linux下缺省是用select來實現Reactor的,epoll相對于select的好處這里就不再啰嗦了,我們直接講操作步驟:
                第一:重新編譯ACE庫
                 ACE庫中通過ACE_Dev_Poll_Reactor類來支持epoll,但是ACE庫缺省的安裝是沒有編譯這個類的,我們要做的就是將ACE_Dev_Poll_Reactor編譯連接到ACE庫中(faint,又要重新編譯ACE,在我那臺破服務器上編譯一次需要一個多小時).我的操作系統是Redhat linux AS4.0,ACE的版本是5.4.10。根據ACE壓縮包中的ACE-INSTALL.html,我是用”Building ACE with GNU Autoconf“這種方式來安裝的,安裝步驟如下(很簡單,就不翻譯了):
                   1 cd to the top-level ACE_wrappers directory.

                   2.Create a subdirectory to hold your build’s configuration and built ACE version,     and   then change to the new directory:

                   mkdir build

                   cd build

                 

                   3.Note that you do not run the create_ace_build.pl utility mentioned in the Cloning the Source Tree section. The configure script takes care of creating all files and links that are needed.

            Configure ACE for your platform by issuing the following command: c

                   ../configure [options]

                 
                  4.Build ACE by typing make.

                  5. Install ACE by typing make install.
                  好,現在終于可以講如何將ACE_Dev_Poll_Reactor編譯到ACE庫中去了。在上述的第一步和第二步之間修改ACE_wrappers/ace/config-linux.h,增加一行:#define ACE_HAS_EVENT_POLL,然后執行第2、3步,第3步../configure執行完之后,build目錄下會生成一些文件和目錄,打開ACE_wrappers/build/ace/config.h,增加一行:#define ACE_HAS_EVENT_POLL。然后執行第4步make和第5步make install.OK,在漫長的編譯以后,支持epoll的ACE庫總算完成了。

                 第二:修改應用程序
                    應用程序修改很簡單,兩行代碼搞掂,在應用程序初始化時(必須是在第一次使用ACE_Reactor::instance()之間)加入:
                   
                    m_pDevPollReactor=new ACE_Dev_Poll_Reactor;
                   ACE_Reactor::instance(new ACE_Reactor(m_pDevPollReactor));
                  
                   那么在后續的對ACE_Reactor::instance()的調用就是使用ACE_Dev_Poll_Reactor的實現了。
              
               第三:重新編譯應用程序
              
                    在應用程序的makefile中加入 -DACE_HAS_EVENT_POLL,重新make應用程序。OK,打完收工。
                    

            posted @ 2007-04-05 08:32 true 閱讀(2148) | 評論 (0)編輯 收藏

            我們通常把一些公用函數制作成函數庫,供其它程序使用。函數庫分為靜態庫和動態庫兩種。靜態庫在程序編譯時會被連接到目標代碼中,程序運行時將不再需要該靜態庫。動態庫在程序編譯時并不會被連接到目標代碼中,而是在程序運行是才被載入,因此在程序運行時還需要動態庫存在。本文主要通過舉例來說明在Linux中如何創建靜態庫和動態庫,以及使用它們。

            在創建函數庫前,我們先來準備舉例用的源程序,并將函數庫的源程序編譯成.o文件。

            第1步:編輯得到舉例的程序--hello.h、hello.c和main.c;

            hello.c(見程序2)是函數庫的源程序,其中包含公用函數hello,該函數將在屏幕上輸出"Hello XXX!"。hello.h(見程序1)為該函數庫的頭文件。main.c(見程序3)為測試庫文件的主程序,在主程序中調用了公用函數hello。

             #ifndef HELLO_H
             #define HELLO_H
             
             void hello(const char *name);
             
             #endif //HELLO_H
             程序1: hello.h
             
             #include <stdio.h>
             
             void hello(const char *name)
             {
              printf("Hello %s!\n", name);
             }
             程序2: hello.c
             
             #include "hello.h"
             
             int main()
             {
              hello("everyone");
              return 0;
             }
             程序3: main.c

            第2步:將hello.c編譯成.o文件;

            無論靜態庫,還是動態庫,都是由.o文件創建的。因此,我們必須將源程序hello.c通過gcc先編譯成.o文件。

            在系統提示符下鍵入以下命令得到hello.o文件。

            # gcc -c hello.c

            #

            (注1:本文不介紹各命令使用和其參數功能,若希望詳細了解它們,請參考其他文檔。)

            (注2:首字符"#"是系統提示符,不需要鍵入,下文相同。)

            我們運行ls命令看看是否生存了hello.o文件。

            # ls

            hello.c hello.h hello.o main.c

            #

            (注3:首字符不是"#"為系統運行結果,下文相同。)

            在ls命令結果中,我們看到了hello.o文件,本步操作完成。

            下面我們先來看看如何創建靜態庫,以及使用它。

            第3步:由.o文件創建靜態庫;

            靜態庫文件名的命名規范是以lib為前綴,緊接著跟靜態庫名,擴展名為.a。例如:我們將創建的靜態庫名為myhello,則靜態庫文件名就是libmyhello.a。在創建和使用靜態庫時,需要注意這點。創建靜態庫用ar命令。

            在系統提示符下鍵入以下命令將創建靜態庫文件libmyhello.a。

            # ar cr libmyhello.a hello.o

            #

            我們同樣運行ls命令查看結果:

            # ls

            hello.c hello.h hello.o libmyhello.a main.c

            #

            ls命令結果中有libmyhello.a。

            第4步:在程序中使用靜態庫;

            靜態庫制作完了,如何使用它內部的函數呢?只需要在使用到這些公用函數的源程序中包含這些公用函數的原型聲明,然后在用gcc命令生成目標文件時指明靜態庫名,gcc將會從靜態庫中將公用函數連接到目標文件中。注意,gcc會在靜態庫名前加上前綴lib,然后追加擴展名.a得到的靜態庫文件名來查找靜態庫文件。

            在程序3:main.c中,我們包含了靜態庫的頭文件hello.h,然后在主程序main中直接調用公用函數hello。下面先生成目標程序hello,然后運行hello程序看看結果如何。

            # gcc -o hello main.c -L. -lmyhello

            # ./hello

            Hello everyone!

            #

            我們刪除靜態庫文件試試公用函數hello是否真的連接到目標文件 hello中了。

            # rm libmyhello.a

            rm: remove regular file `libmyhello.a'? y

            # ./hello

            Hello everyone!

            #

            程序照常運行,靜態庫中的公用函數已經連接到目標文件中了。

            我們繼續看看如何在Linux中創建動態庫。我們還是從.o文件開始。

            第5步:由.o文件創建動態庫文件;

            動態庫文件名命名規范和靜態庫文件名命名規范類似,也是在動態庫名增加前綴lib,但其文件擴展名為.so。例如:我們將創建的動態庫名為myhello,則動態庫文件名就是libmyhello.so。用gcc來創建動態庫。

            在系統提示符下鍵入以下命令得到動態庫文件libmyhello.so。

            # gcc -shared -fPCI -o libmyhello.so hello.o

            #

            我們照樣使用ls命令看看動態庫文件是否生成。

            # ls

            hello.c hello.h hello.o libmyhello.so main.c

            #

            第6步:在程序中使用動態庫;

            在程序中使用動態庫和使用靜態庫完全一樣,也是在使用到這些公用函數的源程序中包含這些公用函數的原型聲明,然后在用gcc命令生成目標文件時指明動態庫名進行編譯。我們先運行gcc命令生成目標文件,再運行它看看結果。

            # gcc -o hello main.c -L. -lmyhello

            # ./hello

            ./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory

            #

            哦!出錯了。快看看錯誤提示,原來是找不到動態庫文件libmyhello.so。程序在運行時,會在/usr/lib和/lib等目錄中查找需要的動態庫文件。若找到,則載入動態庫,否則將提示類似上述錯誤而終止程序運行。我們將文件libmyhello.so復制到目錄/usr/lib中,再試試。

            # mv libmyhello.so /usr/lib

            # ./hello

            Hello everyone!

            #

            成功了。這也進一步說明了動態庫在程序運行時是需要的。

            我們回過頭看看,發現使用靜態庫和使用動態庫編譯成目標程序使用的gcc命令完全一樣,那當靜態庫和動態庫同名時,gcc命令會使用哪個庫文件呢?抱著對問題必究到底的心情,來試試看。

            先刪除除.c和.h外的所有文件,恢復成我們剛剛編輯完舉例程序狀態。

            # rm -f hello hello.o /usr/lib/libmyhello.so

            # ls

            hello.c hello.h main.c

            #

            在來創建靜態庫文件libmyhello.a和動態庫文件libmyhello.so。

            # gcc -c hello.c

            # ar cr libmyhello.a hello.o

            # gcc -shared -fPCI -o libmyhello.so hello.o

            # ls

            hello.c hello.h hello.o libmyhello.a libmyhello.so main.c

            #

            通過上述最后一條ls命令,可以發現靜態庫文件libmyhello.a和動態庫文件libmyhello.so都已經生成,并都在當前目錄中。然后,我們運行gcc命令來使用函數庫myhello生成目標文件hello,并運行程序 hello。

            # gcc -o hello main.c -L. -lmyhello

            # ./hello

            ./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory

            #

            從程序hello運行的結果中很容易知道,當靜態庫和動態庫同名時, gcc命令將優先使用動態庫。

            posted @ 2007-04-04 18:22 true 閱讀(736) | 評論 (0)編輯 收藏

                 摘要: TinyXml是一個基于DOM模型的、非驗證的輕量級C++解釋器, 用來讀寫配置文件是最好的,可從http://www.grinninglizard.com/tinyxml/index.html這里下載。構建TinyXML 時可以選擇是否支持STL, 一般建議選擇支持STL。TinyXml的源碼包里提供了VC6的工程文件,直接用它就可以生成靜態庫。然后把tinyxml.h, tinyst...  閱讀全文
            posted @ 2007-04-04 17:14 true 閱讀(1205) | 評論 (2)編輯 收藏

            轉貼-有關TinyXML使用的簡單總結
            floodking 發表于 2006-11-6 18:01:00

             這次使用了TinyXML后,覺得這個東西真是不錯,于是將使用方法坐下總結來和大家分享。
                該解析庫在開源網站(http://sourceforge.net )上有下載,在本Blog也提供下載(下載TinyXML
                TinyXML是一個開源的解析XML的解析庫,能夠用于C++,能夠在WindowsLinux中編譯。這個解析庫的模型通過解析XML文件,然后在內存中生成DOM模型,從而讓我們很方便的遍歷這課XML樹。
                注:DOM模型即文檔對象模型,是將整個文檔分成多個元素(如書、章、節、段等),并利用樹型結構表示這些元素之間的順序關系以及嵌套包含關系(理解html語言的讀者會很容易理解這種樹狀模型)。               
                如下是一個XML片段:
                <Persons>
                    <Person ID="1">
                        <name>周星星</name>
                        <age>20</age>
                    </Person>
                    <Person ID="2">
                        <name>白晶晶</name>
                        <age>18</age>
                    </Person>
                </Persons>
                在TinyXML中,根據XML的各種元素來定義了一些類:
                    TiXmlBase:整個TinyXML模型的基類。
                            TiXmlAttribute:對應于XML中的元素的屬性。
                            TiXmlNode:對應于DOM結構中的節點。
                                    TiXmlComment:對應于XML中的注釋。
                                    TiXmlDeclaration:對應于XML中的申明部分,即<?versiong="1.0" ?>。
                                    TiXmlDocument:對應于XML的整個文檔。
                                    TiXmlElement:對應于XML的元素。
                                    TiXmlText:對應于XML的文字部分。
                                    TiXmlUnknown:對應于XML的未知部分。 
                    TiXmlHandler:定義了針對XML的一些操作。
                那我們如何使用這些類以及他們的方法來操縱我們的XML呢?請看下面。
                一、讀取XML(假設我們的Xml文檔中的內容與上面的Xml內容一樣)
                //創建一個XML的文檔對象
                TiXmlDocument *myDocument = new TiXmlDocument("填上你的Xml文件名");
                myDocument->LoadFile();
                //獲得根元素,即Persons。
                TiXmlElement *RootElement = myDocument.RootElement();
                //輸出根元素名稱,即輸出Persons。
                cout << RootElement->Value() << endl;
                //獲得第一個Person節點。
                TiXmlElement *FirstPerson = RootElement->FirstChildElement();
                //獲得第一個Person的name節點和age節點和ID屬性。
                TiXmlElement *NameElement = FirstPerson->FirstChildElement();
                TiXmlElement *AgeElement = NameElement->NextSiblingElement();
                TiXmlAttribute *IDAttribute = FirstPerson->FirstAttribute();
                //輸出第一個Person的name內容,即周星星;age內容,即20;ID屬性,即1。
                cout << NameElement->FirstChild()->Value << endl;
                cout << AgeElement->FirstChild()->Value << endl;
                cout << IDAttribute->Value() << endl;

                
                看,讀取XML是不是很簡單阿,和Java的XML解析庫非常的相似,就是名字改了一下而已。
                二、生成XML內容
                //創建一個XML的文檔對象。
                TiXmlDocument *myDocument = new TiXmlDocument();
                //創建一個根元素并連接。
                TiXmlElement *RootElement = new TiXmlElement("Persons");
                myDocument->LinkEndChild(RootElement);
                //創建一個Person元素并連接。
                TiXmlElement *PersonElement = new TiXmlElement("Person");
                RootElement->LinkEndChild(PersonElement);
                //設置Person元素的屬性。
                PersonElement->SetAttribute("ID", "1");
                //創建name元素、age元素并連接。
                TiXmlElement *NameElement = new TiXmlElement("name");
                TiXmlElement *AgeElement = new TiXmlElement("age");
                PersonElement->LinkEndChild(NameElement);
                PersonElement->LinkEndChild(AgeElement);
                //設置name元素和age元素的內容并連接。
                TiXmlText *NameContent = new TiXmlText("周星星");
                TiXmlText *AgeContent = new TiXmlText("20");
                NameElement->LinkEndChild(NameContent);
                AgeElement->LinkEndChild(AgeContent);
                //保存到文件
                myDocument->SaveFile("要保存的xml文件名");
                這樣,便創建了一個如下的xml文件:
                <Persons>
                    <Person ID="1">
                        <name>周星星</name>
                        <age>20</age>
                    </Person>
                </Persons>
                
                是不是很簡單啊?在這里我只是簡單的對TinyXml的使用作了介紹,歡迎各位提問留言,我會盡力解答各位的問題。[By Lqbest]
            posted @ 2007-04-03 09:28 true 閱讀(911) | 評論 (0)編輯 收藏

            這篇文章摘自http://blog.csdn.net/kunp/archive/2004/06/30/30541.aspx
            幫我解決了從 string::c_str 到 char *類型的轉換問題。(注意,string::c_str返回的
            是const char* 類型。

            使用stringstream對象實現數據類型之間的轉換

            ???很多人都使用傳統的C 庫來進行數據類型之間的轉換,這將會導致很多問題,因為這樣的轉換方法存在很多危險的陷阱。比如itoa()這個函數在標準庫中是不存在的。標準的庫提供了一種更好的轉換選擇,因為這一方法更加安全,自動,直接。

            ???讓我們來看一個具體的范例。假設你想把一個int轉換為string。為了達到這一目的,你必須遵循如下的步驟:

            1. 建立一個stringstream對象,

            2.使用操作符<<插入int數據,

            3.使用操作符>>抽取前面插入到的數據到一個string對象中。

            以下代碼行演示了這些步驟:


            																//程序名:teststream.cpp //功能:將int類型數據通過stringstream對象轉成string 
            #i nclude <iostream>
            #i nclude <stdlib.h> ???// system()
            #i nclude <string>
            #i nclude <sstream>
            using namespace std;
            																

            int main(int argc, char *argv[])
            {
            ??? std::stringstream stream;
            ??? std::string result;
            ??? int num = 1000;
            ???
            ??? stream << num;????????????? ????????????//將int類型數據插入stream對象中
            ??? stream >> result;?????????? ????????????//取出之前插入的數據
            ??? cout << "num:\t" << num << endl;
            ??? cout << "result:\t" << result << endl;??? //打印 "1000"???????????????
            ???
            ??? system("PAUSE");?
            ??? return 0;
            }


            ???請注意我們沒有使用一個簡潔的cast操作或一個模式標志來實現stringstream轉換。操作符<<和>>會自動地刪除原始數據的類型和目標數據,并自動而安全地執行需要的轉換。

            庫不會只限于一些高水平的操作,比如std::string。你可以很方便地實現一個char *變量之間的轉換:

            												//程序名:teststream2.cpp //功能:將int類型數據通過stringstream對象轉成char[] #i nclude <iostream> 
            #i nclude <stdlib.h> ???// system()
            #i nclude <string>
            #i nclude <sstream>
            using namespace std;
            												

            int main(int argc, char *argv[])
            {
            ??? std::stringstream stream;
            ??? char result[12] = {'\0'};
            ??? stream << 1234;???????????????????? //insert int to stream
            ??? stream >> result;?????????????????? //extract previously inserted value
            ??? cout << result << endl;???????????? // print "1234"????????????
            ???
            ??? system("PAUSE");?
            ??? return 0;
            }

            ???如果你想通過使用同一stringstream對象實現多種類型的轉換,請注意在每一次轉換之后都必須調用clear()成員函數,例如:

            												//程序名:teststream3.cpp //功能:使用同一stringstream對象實現多種類型的轉換 #i nclude <iostream> 
            #i nclude <stdlib.h> ???// system()
            #i nclude <string>
            #i nclude <sstream>
            using namespace std;
            												

            int main(int argc, char *argv[])
            {
            ??? std::stringstream stream;
            ??? int n, m;
            ??? stream<< "456"; ??????//insert string
            ??? stream >> n; ?????????//extract to int
            ??? stream.clear(); ??????//reset stream before another conversion
            ??? stream<< true; ???????//insert bool value
            ??? stream >> m; ?????????//extract to int

            ??? cout << "n:\t" << n << endl;??? //print 456
            ??? cout << "m:\t" << m << endl;??? //print 1? ??????????
            ???
            ??? system("PAUSE");?
            ??? return 0;
            }

            ???事實上,stream對象可以接收多種類型輸入的特點給我們帶來一個好處,可以將int,char*等不同類型的輸入同時導入到一個stream對象,再通過該stream對象導出一個新值。

            												//程序名:teststream4.cpp //功能:將int類型數據和char*數據通過stringstream對象轉成char[] 
            #i nclude <iostream>
            #i nclude <stdlib.h> ???// system()
            #i nclude <string>
            #i nclude <sstream>
            using namespace std;
            												

            int main(int argc, char *argv[])
            {
            ??? std::stringstream stream;
            ??? char ip[16];
            ??? stream << 218; //insert int
            ??? stream << "."; //insert string
            ??? stream << 192; //insert int
            ??? stream << "."; //insert string
            ??? stream << 160; //insert int
            ??? stream << "."; //insert string
            ??? stream << 228; //insert int
            ???
            ??? stream >> ip;

            ??? cout << "ip:\t" << ip << endl;??? //print " 218.192.160.228" ???
            ??? system("PAUSE");?
            ??? return 0;

            posted @ 2007-03-29 16:25 true 閱讀(1427) | 評論 (0)編輯 收藏

            僅列出標題
            共15頁: First 7 8 9 10 11 12 13 14 15 
            久久久久亚洲AV无码去区首| 青青青国产成人久久111网站| 久久久久国产成人精品亚洲午夜| 97久久精品人人澡人人爽| 精品无码久久久久久久动漫| 久久精品亚洲福利| 日韩精品久久久久久久电影蜜臀| 99久久综合狠狠综合久久止| 亚洲国产精品狼友中文久久久| 亚洲中文久久精品无码ww16 | 久久99精品久久久久久久久久| 久久青草国产精品一区| 超级碰碰碰碰97久久久久| 无码AV波多野结衣久久| 久久无码人妻精品一区二区三区 | 久久久久久伊人高潮影院| 久久精品国产亚洲av麻豆小说 | 久久Av无码精品人妻系列 | 亚洲精品高清国产一久久| 久久久久亚洲AV无码专区首JN | 久久久久国色AV免费观看| 久久99国产精品尤物| 狠狠色丁香久久婷婷综合图片| 精品一区二区久久| 久久综合狠狠综合久久| 久久人人爽人人爽人人片AV高清| 91久久精品电影| 99久久精品国产毛片| av午夜福利一片免费看久久| 亚洲国产精品无码久久久不卡 | 无码人妻久久一区二区三区免费丨 | 国产A级毛片久久久精品毛片| 久久一区二区三区99| 亚洲国产精品久久| 久久久久久免费一区二区三区 | 亚洲Av无码国产情品久久| 伊人色综合久久| 久久国产免费观看精品| 久久精品9988| 国产精品永久久久久久久久久| 亚洲乱亚洲乱淫久久|