• <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>
            Creative Commons License
            本Blog采用 知識共享署名-非商業性使用-禁止演繹 3.0 Unported許可協議 進行許可。 —— Fox <游戲人生>

            游戲人生

            游戲人生 != ( 人生 == 游戲 )
            站點遷移至:http://www.yulefox.com。請訂閱本博的朋友將RSS修改為http://feeds.feedburner.com/yulefox
            posts - 62, comments - 508, trackbacks - 0, articles - 7

            2010年1月7日

            本文同步自游戲人生

            最近有點忙,本來要用autoconf+automake把自己的代碼梳理一下的,因為工作停了近兩周。

            本想看看有什么工具可以自動生成Makefile.am,答案是:Automake不支持通配符,而且還口口聲聲,振振有詞。既然說的這么言詞鑿鑿,情深意切,我想我也沒有必要用shell生成Makefile.am了。

            用著用著,我有點懷疑人生了:不知道什么時候需要用autoconf和automake。如果我只是平時自己寫一些toy codes的話,感覺用autoconf和automake有點大炮打蚊子的感覺,而且每次新加代碼或者是移除代碼、甚至是更改目錄,都要重新執行 autoconf、automake(不知道我說的對與不對)。對于一個大型項目,執行一次configure和make是很痛苦的一件事,make的中 間目標文件或者庫文件、執行文件倒是不一定非得完全rebuild,configure的配置檢查呢?是不是也有類似機制?反正我在用ogre或者 cegui的時候,每次執行./configure是重新配置了的。

            實際在開源項目里面也不可能維護兩套makefile吧。

            看了一下googletest的配置,倒是清爽的很,最大的特點是只有一個Makefile.am,這樣在一個項目里面只需要維護一個Makefile.am就夠了。

            cegui比較常規,每個子目錄都會維護一個Makefile.am。

            需要特別注意的是ogre從1.7.0開始已經開始使用cmake了……

            請聽題:管理中小型項目,你傾向于下面哪個工具?

            o make:鉆木取火,玩的就是個技術,編譯代碼,只用裝B的,不用牛B的,你要是用什么cmake,你都不好意思跟別人打招呼,這么經典的東西,精通需要多久?要我說怎么著也得個把倆月吧,個把倆月?那是入門,至少半年,就這還得有Feldman的悟性,不舍晝夜;

            o autoconf+automake:既有群眾基礎,又有技術含量,你是那樣拉轟的男人,不管在什么地方,就好像漆黑中的螢火蟲一樣,那樣的鮮明,那樣的 出眾。你那憂郁的眼神,稀噓的胡喳子,神乎其技的指法;既可以恥笑原始人的生產力低下,還可以鄙視現代人的不學無術。

            o cmake:在MSVCers面前抬不起頭,在UNIXers面前似乎更抬不起頭;而cmake對WINDOWS和UNIX平臺的完美支持,足以讓所有的 MSVCers和UNIXer在你面前抬不起頭,你是公雞中的戰斗機。所以你還是可以趾高氣昂的丟下一句:走NB的路,讓SB說去吧。


            posted @ 2010-01-07 01:32 Fox 閱讀(3994) | 評論 (4)編輯 收藏

            2009年12月23日

                 摘要: 從接觸和使用make以來,前前后后寫了不少Makefile(添添減減、修修補補,累計上千行是有的),今天在重新整理代碼的組織結構之后,突然就想:我為什么不使用Autotools呢?

            在開始體驗功能強大的Autotools之前,簡單(詳細)回憶總結一下我使用make的經歷和思考的過程,反省一下看看自己在接觸這些新鮮事物的時候到底走了多少彎路。  閱讀全文

            posted @ 2009-12-23 02:18 Fox 閱讀(6920) | 評論 (5)編輯 收藏

            2009年12月6日

            本文同步自游戲人生

            Writen by Fox(yulefox.at.gmail.com)

            在具體討論之前,本文先厘清UUID(Universally Unique IDentifier)與GUID(Globally Unique IDentifier)的關系。

            在分布式、網絡、單機環境下,為了能夠使用具有某種形式的ID唯一標識系統中的任一元素,這樣的ID可以不依賴中心認證自動生成,于是UUID就誕生了。

            UUID標準的歷史沿革和具體實現在RFC 4122ITU-T Rec. X.667ISO/IEC 9834-8:2008中均有詳細描述。ITU和ISO采用的標準和RFC 4122都是在UUID的早期版本基礎上完成,各版本之間具有一致性和兼容性。

            因為不能保證UUID的唯一性,ITU和ISO針對UUID的使用都有免責聲明

            GUID一般是指Microsoft對于UUID標準的實現,UUID的實現則多見于其他系統(*NIX、MAC OS等)中。在了解了這一區別后,本文將統一使用UUID來指代對應的原理、算法及實現。

            文中關于UUID的討論全部基于RFC 4122和ITU-T Rec. X.667以及OSF、IETF、ITU-T、ISO、FIPS的各種標準文檔。而UUID的細節(如結構、表示、算法、實現等)均以ITU-T Rec. X667為唯一藍本,文中“本標準”即指代該藍本。

            o 介紹

            UUID是長度為16-byte(128-bit)的ID,一般以形如f81d4fae-7dec-11d0-a765-00a0c91e6bf6的字符串作為URN(Uniform Resource Name,統一資源名稱)。

            o 動機

            無須中心認證,自動生成,支持一臺機器每秒生成10M次(100納秒級,其隱含原因是指能夠區分的最小時間單位為100ns,將時間作為因子時,連續生成兩個UUID的時間至少要間隔100ns)。方便存取、分配、排序、查找。

            o 結構


               76543210765432107654321076543210
               + – - – = – - – = – - – = – - – +
            15 |            TimeLow            | 12
            11 |    TimeMid    |   Version..   |  8
            7  |Vari.. |Clock..|     Node      |  4
            3  |             Node              |  0
               + – - – = – - – = – - – = – - – +
            15 – 12: TimeLow 時間值的低位
            11 – 10: TimeMid 時間值的中位
            09 – 08: VersionAndTimeHigh 4位版本號和時間值的高位
            07: VariantAndClockSeqHigh 2位變體(ITU-T)和時鐘序列高位
            06: ClockSeqLow 時鐘序列低位
            05 – 00: Node 結點
            hexOctet = hexDigit hexDigit
            hexDigit =
            “0″ / “1″ / “2″ / “3″ / “4″ / “5″ / “6″ / “7″ / “8″ / “9″ /
            “a” / “b” / “c” / “d” / “e” / “f” /
            “A” / “B” / “C” / “D” / “E” / “F”
            UUID =
            TimeLow
            “-” TimeMid
            “-” VersionAndTimeHigh
            “-” VariantAndClockSeqHigh ClockSeqLow
            “-” Node

            UUID由上述6個域構成,每個域編碼為若干字節,并以16進制數表示這128位的UUID,相鄰域以減號“-”分隔 (VariantAndClockSeqHigh和ClockSeqLow對應的兩個字節例外,如上所示)。該結構中包含版本(Version)、變體 (Variant)、時間(Time)、時鐘序列(Clock Sequence)、節點(Note)信息(以無符號整型值表示)。

            o 合法性

            除判斷variant位設置是否正確、基于時間生成的UUID時間值是否為未經分配的將來時間外,實際應用中沒有其他機制可以判定UUID是否合法。

            o 變體

            Variant位是UUID第7字節(VariantAndClockSeqHigh)的最高3位,

            7 6 5  Description
            0 – –  NCS向后兼容
            1 0 –  本標準
            1 1 0  Microsoft向后兼容
            1 1 1  ITU-T Rec. X.667保留

            o 版本

            UUID的生成有時間、名稱、隨機數三種策略,以第9字節(VersionAndTimeHigh)的最高4位表示。

            目前UUID定義有5個版本:

            7 6 5 4  Ver  Description
            0 0 0 1  1    基于時間的版本(本標準)
            0 0 0 0  2    使用嵌入式POSIX(DCE安全版本)
            0 0 1 1  3    使用MD5哈希的基于名稱的版本(本標準)
            0 1 0 0  4    基于隨機數的版本(本標準)
            0 1 0 1  5    使用SHA-1的基于名稱的版本(本標準)

            o 時間

            時間是一個60位的整型值(除4位版本號外的前8字節),對應UTC(格林尼治時間1582年10月15日午夜始)的100ns時間間隔計數。

            對于ver 4和5,該值分別對應一個隨機數和一個全局唯一的名稱。

            o 時鐘序列

            對基于時間的UUID版本,時間序列用于避免因時間向后設置或節點值改變可能造成的UUID重復,對基于名稱或隨機數的版本同樣有用:目的都是為了防止UUID重復。

            如果前一時鐘序列已知,通過自增實現時鐘序列值的改變;否則,通過密碼學(偽)隨機數設置新的時鐘序列值。

            o 節點

            對基于時間的UUID版本,節點由48位的單播MAC地址構成。對于沒有MAC地址的系統,節點值為一個密碼學(偽)隨機數(為防止與MAC地址發生碰撞,需設置多播位)。


            o 基于時間的UUID生成算法

            o 確定UTC時間(60位 Time)和時間序列值(14位 ClockSequence);

            o 設置TimeLow(對應Time的31-0位);

            o 設置TimeMid(對應Time的47-32位);

            o 設置VersionAndTimeHigh(4位版本號及Time的59-48位);

            o 設置VariantAndClockSeqHigh(變體位及對應ClockSequence的13-8位);

            o 設置ClockSeqLow(對應ClockSequence的7-0位);

            o 設置Node(對應48位MAC地址)。

            o 基于名稱的UUID生成算法

            o 針對相應的命名空間(如DNS、URL、OID等)分配一個UUID作為所有UUID的命名空間標識;

            o 將名稱轉換為字節數列;

            o 使用MD5或SHA-1算法對與名稱關聯的命名空間標識進行計算,產生16字節哈希結果;

            o 設置TimeLow(對應哈希值的3-0字節);

            o 設置TimeMid(對應哈希值的5-4字節);

            o 設置VersionAndTimeHigh(對應哈希值的7-6字節),以相應版本號重寫對應位(第9字節的高4位);

            o 設置VariantAndClockSeqHigh(對應哈希值的第8字節),重寫變體對應位(第7字節的高2位,本標準對應值為10);

            o 設置ClockSeqLow(對應哈希值的第9字節);

            o 設置Node(對應哈希值的15-10字節)。

            由 于MD5碰撞問題,MD5只用于向后兼容的UUID生成,不再被推薦使用。由于SHA-1哈希結果為160位(20字節),本算法中,需要將FIPS PUB 180-2中的SHA-1算法的哈希值字節順序反轉(字節內順序不變),UUID使用其15-0字節,19-16字節被丟棄。

            o 基于隨機數的UUID生成算法

            o 設置VariantAndClockSeqHigh的變體位值為10;

            o 設置VersionAndTimeHigh的4位版本號;

            o 設置剩余位為隨機值。

            本文中討論的密碼學隨機數,主要根據系統可以提供的信息(內存、硬盤、句柄、程序運行的線程、進程、句柄、堆棧等),利用SHA-1等哈希算法得到。

            其他關于密碼學隨機數的描述,我曾在這篇文章中簡單提到。


            具體算法實現可以參考文檔和開源代碼。

            posted @ 2009-12-06 15:07 Fox 閱讀(21530) | 評論 (2)編輯 收藏

            2009年9月22日

            本文同步自游戲人生

            以前曾經討論過Singleton的實現,這次在對照ACE和Boost代碼的時候,又重新審視了一下二者對Singleton不同的實現。其間的差別也體現了不同的編程哲學:ACE的實現更加偏重多線程中的安全和效率問題;Boost的實現則偏重于使用語言自身的特性滿足Singleton模式的基本需求。

            o ACE的實現

            Douglas C. Schmidt在Double-Checked Locking: An Optimization Pattern for Efficiently Initializing and Accessing Thread-safe Objects一文中對double-check lock(一般譯為雙檢鎖)進行了詳細的闡述。

            ACE的Singleton使用Adapter模式實現對其他類的適配,使之具有全局唯一的實例。由于C++標準并非明確指定全局靜態對象的初始化順序,ACE使用double-check lock保證線程安全,并使之不受全局靜態對象初始化順序的影響,同時也避免了全局靜態實現方式的初始化后不使用的開銷。

            如果你能夠準確的區分以下三種實現的弊端和隱患,對double-check lock也就有了足夠的了解。

            // -------------------------------------------
            class Singleton
            {
            public:
                static Singleton *instance (void)
                {
                    // Constructor of guard acquires
                    // lock_ automatically.
                    Guard<Mutex> guard (lock_);
                    // Only one thread in the
                    // critical section at a time.
                    if (instance_ == 0)
                        instance_ = new Singleton;
                    return instance_;
                    // Destructor of guard releases
                    // lock_ automatically.
                }
            private:
                static Mutex lock_;
                static Singleton *instance_;
            };

            // ---------------------------------------------
            static Singleton *instance (void)
            {
                if (instance_ == 0) {
                    Guard<Mutex> guard (lock_);
                    // Only come here if instance_
                    // hasn’t been initialized yet.
                    instance_ = new Singleton;
                }
                return instance_;
            }

            // ---------------------------------------------
            class Singleton
            {
            public:
                static Singleton *instance (void)
                {
                    // First check
                    if (instance_ == 0)
                    {
                        // Ensure serialization (guard
                        // constructor acquires lock_).
                        Guard<Mutex> guard (lock_);
                        // Double check.
                        if (instance_ == 0)
                            instance_ = new Singleton;
                    }
                    return instance_;
                    // guard destructor releases lock_.
                }
            private:
                static Mutex lock_;
                static Singleton *instance_;
            };

            更多詳情,見Schmidt老師的原文和ACE_Singleton實現。

            o Boost的實現

            Boost的Singleton也是線程安全的,而且沒有使用鎖機制。當然,Boost的Singleton有以下限制(遵從這些限制,可以提高效率):

            o The classes below support usage of singletons, including use in program startup/shutdown code, AS LONG AS there is only one thread running before main() begins, and only one thread running after main() exits.

            o This class is also limited in that it can only provide singleton usage for classes with default constructors.

            // T must be: no-throw default constructible and no-throw destructible
            template <typename T>
            struct singleton_default
            {
            private:
                struct object_creator
                {
                    // This constructor does nothing more than ensure that instance()
                    //  is called before main() begins, thus creating the static
                    //  T object before multithreading race issues can come up.
                    object_creator() { singleton_default<T>::instance(); }
                    inline void do_nothing() const { }
                };
                static object_creator create_object;

                singleton_default();

            public:
                typedef T object_type;

                // If, at any point (in user code), singleton_default<T>::instance()
                //  is called, then the following function is instantiated.
                static object_type & instance()
                {
                    // This is the object that we return a reference to.
                    // It is guaranteed to be created before main() begins because of
                    //  the next line.
                  static object_type obj;

                  // The following line does nothing else than force the instantiation
                  //  of singleton_default<T>::create_object, whose constructor is
                  //  called before main() begins.
                  create_object.do_nothing();

                  return obj;
                }
            };
            template <typename T>
            typename singleton_default<T>::object_creator
            singleton_default<T>::create_object;

            對于多數Singleton使用,Boost提供的版本完全能夠滿足需求。為了效率,我們有必要對其使用作出一定的限制。

            而在多線程編程中,則有必要使用double-check lock降低頻繁加鎖帶來的開銷。

            -------------------------------------------------------------------------------

            PS: 欣賞Soft的一句話:經得起誘惑,耐得住寂寞

            posted @ 2009-09-22 00:38 Fox 閱讀(7439) | 評論 (9)編輯 收藏

            2009年9月12日

            本文同步自游戲人生

            在使用IOCP時,最重要的幾個API就是GetQueueCompeltionStatus、WSARecv、WSASend,數據的I/O及其完成狀態通過這幾個接口獲取并進行后續處理。

            GetQueueCompeltionStatus attempts to dequeue an I/O completion packet from the specified I/O completion port. If there is no completion packet queued, the function waits for a pending I/O operation associated with the completion port to complete.

            BOOL WINAPI GetQueuedCompletionStatus(
              __in   HANDLE CompletionPort,
              __out  LPDWORD lpNumberOfBytes,
              __out  PULONG_PTR lpCompletionKey,
              __out  LPOVERLAPPED *lpOverlapped,
              __in   DWORD dwMilliseconds
            );

            If the function dequeues a completion packet for a successful I/O operation from the completion port, the return value is nonzero. The function stores information in the variables pointed to by the lpNumberOfBytes, lpCompletionKey, and lpOverlapped parameters.

            除了關心這個API的in & out(這是MSDN開頭的幾行就可以告訴我們的)之外,我們更加關心不同的return & out意味著什么,因為由于各種已知或未知的原因,我們的程序并不總是有正確的return & out。

            If *lpOverlapped is NULL and the function does not dequeue a completion packet from the completion port, the return value is zero. The function does not store information in the variables pointed to by the lpNumberOfBytes and lpCompletionKey parameters. To get extended error information, call GetLastError. If the function did not dequeue a completion packet because the wait timed out, GetLastError returns WAIT_TIMEOUT.

            假設我們指定dwMilliseconds為INFINITE。

            這里常見的幾個錯誤有:

            WSA_OPERATION_ABORTED (995): Overlapped operation aborted.

            由于線程退出或應用程序請求,已放棄I/O 操作。

            MSDN: An overlapped operation was canceled due to the closure of the socket, or the execution of the SIO_FLUSH command in WSAIoctl. Note that this error is returned by the operating system, so the error number may change in future releases of Windows.

            成因分析:這個錯誤一般是由于peer socket被closesocket或者WSACleanup關閉后,針對這些socket的pending overlapped I/O operation被中止。

            解決方案:針對socket,一般應該先調用shutdown禁止I/O操作后再調用closesocket關閉。

            嚴重程度輕微易處理

            WSAENOTSOCK (10038): Socket operation on nonsocket.

            MSDN: An operation was attempted on something that is not a socket. Either the socket handle parameter did not reference a valid socket, or for select, a member of an fd_set was not valid.

            成因分析:在一個非套接字上嘗試了一個操作。

            使用closesocket關閉socket之后,針對該invalid socket的任何操作都會獲得該錯誤。

            解決方案:如果是多線程存在對同一socket的操作,要保證對socket的I/O操作邏輯上的順序,做好socket的graceful disconnect。

            嚴重程度輕微易處理

            WSAECONNRESET (10054): Connection reset by peer.

            遠程主機強迫關閉了一個現有的連接。

            MSDN: An existing connection was forcibly closed by the remote host. This normally results if the peer application on the remote host is suddenly stopped, the host is rebooted, the host or remote network interface is disabled, or the remote host uses a hard close (see setsockopt for more information on the SO_LINGER option on the remote socket). This error may also result if a connection was broken due to keep-alive activity detecting a failure while one or more operations are in progress. Operations that were in progress fail with WSAENETRESET. Subsequent operations fail with WSAECONNRESET.

            成因分析:在使用WSAAccpet、WSARecv、WSASend等接口時,如果peer application突然中止(原因如上所述),往其對應的socket上投遞的operations將會失敗。

            解決方案:如果是對方主機或程序意外中止,那就只有各安天命了。但如果這程序是你寫的,而你只是hard close,那就由不得別人了。至少,你要知道這樣的錯誤已經出現了,就不要再費勁的繼續投遞或等待了。

            嚴重程度輕微易處理

            WSAECONNREFUSED (10061): Connection refused.

            由于目標機器積極拒絕,無法連接。

            MSDN: No connection could be made because the target computer actively refused it. This usually results from trying to connect to a service that is inactive on the foreign host—that is, one with no server application running.

            成因分析:在使用connect或WSAConnect時,服務器沒有運行或者服務器的監聽隊列已滿;在使用WSAAccept時,客戶端的連接請求被condition function拒絕。

            解決方案:Call connect or WSAConnect again for the same socket. 等待服務器開啟、監聽空閑或查看被拒絕的原因。是不是長的丑或者錢沒給夠,要不就是服務器拒絕接受天價薪酬自主創業去了?

            嚴重程度輕微易處理

            WSAENOBUFS (10055): No buffer space available.

            由于系統緩沖區空間不足或列隊已滿,不能執行套接字上的操作。

            MSDN: An operation on a socket could not be performed because the system lacked sufficient buffer space or because a queue was full.

            成因分析:這個錯誤是我查看錯誤日志后,最在意的一個錯誤。因為服務器對于消息收發有明確限制,如果緩沖區不足應該早就處理了,不可能待到send/recv失敗啊。而且這個錯誤在之前的版本中幾乎沒有出現過。這也是這篇文章的主要內容。像connect和accept因為緩沖區空間不足都可以理解,而且危險不高,但如果send/recv造成擁堵并惡性循環下去,麻煩就大了,至少說明之前的驗證邏輯有疏漏。

            WSASend失敗的原因是:The Windows Sockets provider reports a buffer deadlock. 這里提到的是buffer deadlock,顯然是由于多線程I/O投遞不當引起的。

            解決方案:在消息收發前,對最大掛起的消息總的數量和容量進行檢驗和控制。

            嚴重程度嚴重

            本文主要參考MSDN

            ************* 說明 *************

            Fox只是對自己關心的幾個錯誤和API參照MSDN進行分析,不提供額外幫助。

            posted @ 2009-09-12 00:20 Fox 閱讀(8801) | 評論 (9)編輯 收藏

            2009年9月10日

            本文同步自游戲人生

            周偉明老師應該是多核計算領域的老人了。

            這幾日因為想找找無鎖(lock-free)方面的信息,就打開了周老師的blog。看到多核系統中三種典型鎖競爭的加速比分析這篇文章時,覺得老師強調多核計算效率是有必要的,但拿Amdahl 定律和Gustafson定律作對比有點不恰當。

            按照我的理解,這兩個定律所刻畫的內容是完全一致的,只是對加速比的定義不一樣罷了。這里,我們都以S(n)表示n核系統對具體程序的加速比,K表示串行部分計算時間比例。

            Amdahl 定律的加速比:S(n) = 使用1個處理器的串行計算時間 / 使用n個處理器的并行計算時間

            S(n) = 1/(K+(1-K)/n) = n/(1+(n-1)K)

            Gustafson定律的加速比:S(n) = 使用n個處理器的并行計算量 / 使用1個處理器的串行計算量

            S(n) = K+(1-K)n

            通俗的講,Amdahl 定律將工作量看作1,有n核也只能分擔1-K的工作量;而Gustafson定律則將單核工作量看作1,有n核,就可以增加n(1-K)的工作量。

            這兩個計算公式都沒有將鎖開銷考慮在內,是理想化的。周老師提到設計不當造成并行變串行的問題與這兩個公式計算無關。因為任何多核計算都存在對串行和并行的設計考量,這正是程序員在使用多核并行時最關心的事情。

            總之,二者的區別只在于態度的不同:一個消極悲觀,一個積極樂觀,充其量是一個冷笑話,而于多核計算沒有任何關聯。

            我說這些也與多核計算沒有關聯,絲毫沒有質疑多核效率的意思。相反,我期待能夠通過技術層面提高多核的有效負載。

            最后一句題外話,周老師使用Word的水平一般:所有來自Word的截圖都是在頁面視圖直接截,換行符和光標隨處可見。

            posted @ 2009-09-10 12:04 Fox 閱讀(6305) | 評論 (0)編輯 收藏

            2009年9月1日

            本文同步自游戲人生

            o *__ 序 __* o

            在閱讀ACE代碼和C++NPv1, v2, APG的時候,我意識到一個問題:雖然稍有C++和網絡基礎的同學都可以讀懂ACE,但如果你對OS(五大管理模塊都包含在內)、TCP/IP、C++、Design Patterns了解越多,你就越能體會ACE為什么需要這么龐雜,雖然它不夠完美(但至少我還沒有資格來批評這一點,我現在最常想做的一個動作就是五體投地)。

            而且我隱約感覺到,我現在所寫的很多東西在以后(對于有些人或許就是現在)看來會相當不深刻、相當不嚴謹,但對于一段學習歷程,這個過程是必然的、必需的。

            在C++NPv1中,Douglas C. Schmidt把原始socket及其API的缺陷有些妖魔化了,比如一段加上注釋、空行在內的35行的代碼,被指出有10處錯誤之多。這就像很多其他語言的倡導者或反傳統C/C++指針者在批評指針時的說法一樣。長期使用原始socket和指針的同學對此感覺很不舒服,何況socket API提供了大量錯誤檢測的接口,至多是不夠友好罷了。你好就好了,沒必要抓住別人一頓痛批吧,『本是同根生,相煎何太急』。

            雖然Solaris、Linux的很多版本及Windows對起源于Berkeley的socket API進行了重寫,但不可否認,由于歷史原因和POSIX標準的存在,對于使用者而言,我們可以無視這些API的實現差異。只是一旦我們從socket通信擴展到其他IPC通信的話,就需要正視各種I/O細節的差異了。

            由于UNIX中,對于socket, file, pipe, device的大多數操作,描述符都是通用的(這一點,OS上面講的更清楚些)。而Windows中,句柄大多不能互換(socket對于MS來說是舶來品)。系統和標準的不一致導致地址、協議和API的混雜甚至混亂。

            UNIX下的描述符和Windows的句柄可以看作是同一個概念,只是應用環境不一樣,所描述的內容也時常不一樣,再簡單了說,它們都是一個整型的ID。

            ACE的源碼中使用了大量預處理指令,尤其在跨平臺/編譯環境的部分更加明顯。鑒于C/C++標準的博大胸懷,有些指令需要閱讀相關編譯器提供的幫助文檔:

            o #pragma: GCC, MSVC

            o #define (#, #@, ##) : GCC, MSVC

            其中有若干代碼文件以.inl為后綴,里面是對部分函數的內聯實現,以使代碼結構看上去更加簡潔。如果確定使用內聯函數的話,*.inl將被包含于*.h的最后,如果不使用,則像*.h一樣,包含于*.cpp的頭部。

            ACE采用doxygen輸出文檔,在閱讀代碼注釋時能夠感受到差異,但基本不會影響閱讀。

            o * __ 關于第3章(C++NPv1)__ * o

            ACE抽象的地址類ACE_Addr擁有ACE_DEV_Addr, ACE_FILE_Addr, ACE_INET_Addr, ACE_SPIPE_Addr, ACE_UNIX_Addr五個子類。對于狹義上的網絡通信(TCP/IP)而言,ACE_INET_Addr對應于我們熟悉的sockaddr_in。

            ACE_IPC_SAP是IPC(interprocess communication)I/O操作類的root類。

            從編碼的角度看,這個類漂亮的地方在于示例了抽象類的另一種實現方式。

            一提到抽象類,大多數人的第一反應是pure virtual function。當一個基類確定需要使用virtual function時,這是一個不錯的選擇。但我們都知道虛擬函數有開銷。而且對于一個結構簡單的抽象基類和其繼承子類(尤其是大量使用時),一個虛函數表帶來的開銷會讓整個設計顯得十分蹩腳。

            我們都知道如何強制讓一個類無法使用default constructor(protected)。如果對基類使用該方法,僅使子類具有public的default constructor,這就達到了定義抽象基類的效果。

            virtual destructor的意義在于防止delete父類指針(指向子類對象)時未調用子類destructor。在此例中,為避免這種情況,同樣將destructor聲明為protected即可。

            從設計實現的角度看,相較于socket API,ACE_IPC_SAP的子類ACE_SOCK提供了編譯時對句柄合法性的檢測。

            從邏輯功能層面劃分,socket有三種角色:

            o active connection role (connector):主動連接

            o passive connection role (acceptor):被動連接

            o communication role (stream):數據通信

            但socket API畢竟不是OOD出來的,對于一個socket描述符,也完全沒有必要去限制其擔負的功能,更不可能搞成三種不同的socket。而OOD的ACE則可以輕易實現對socket對象及其操作的封裝。

            工廠類ACE_SOCK_Connector是一個主動創建通信端的工廠類。socket API中的connect接口只是為一個socket建立與其它peer的網絡連接,而不產生新的socket實例,也不依賴于任何其它socket。同樣,ACE_SOCK_Connector只是為一個ACE_SOCK_Stream對象(對用于數據通信的socket的封裝)連接到ACE_Addr(對struct sockaddr的封裝)提供接口,也不含對ACE_SOCK_Stream對象的其它操作。

            工廠類ACE_SOCK_Acceptor是一個被動創建通信端的工廠類。當監聽到新的網絡連接后,為該連接初始化一個ACE_SOCK_Stream對象。和connector不同的是,acceptor依賴于一個已經存在的充當監聽功能的socket句柄(ACE_SOCK),因此,ACE_SOCK_Acceptor是ACE_SOCK的一個子類。

            ACE_SOCK_Stream是只負有通信傳輸功能的socket,對應connection-oriented的TCP通信格式stream,和UDP的CE_SOCK_CODgram相呼應。ACE_SOCK_Stream只是socket的通信載體,在兩個工廠ACE_SOCK_Connector和ACE_SOCK_Acceptor中初始化。這樣一個類除支持最基本的數據發送(send)和接收(recv)和阻塞(blocking)、非阻塞(nonblocking)及定時(timed)的I/O模式外,還支持分散讀取(scatter-read)和集中寫入(gather-write)。

            對于一個簡單的『網絡課程作業:寫一個有連接的IM小程序』,上面這些內容已經足夠了。當然即使使用對應的幾個socket API也已經足夠了。但我們顯然更加關心如此龐大的一個庫,是如何解決復雜的網絡應用的,我尤其關心的是多線程并發如何更好的處理。

            所以,我準備跑到第8、9章了。

            posted @ 2009-09-01 14:22 Fox 閱讀(3893) | 評論 (3)編輯 收藏

            2009年8月28日

            本文同步自游戲人生

            我發現我最近成了Cygwin下的小白鼠,寫完Cygwin下安裝ACE,寫ACE在cygwin下的使用。現在又寫doxygen

            之前提到在Cygwin下讀代碼的不習慣,后來回到VS下看。沒過幾天,覺得VS下還是不夠直觀,于是就直接看ACE的doxygen了……

            doxygen好是好,用起來還是要慢慢習慣才行,需要在寫注釋和代碼的時候注意一些,掌握的細節和技巧越多,出來的文檔越豐富(當然,這和代碼質量是兩碼事)。

            我自然是把doxygen安裝在Cygwin下了,由于doxygen沒有提供info,Info doxygen時就自動打開了doxygen的man,和man doxygen、doxygen --help一個效果。

            如果希望閱讀更詳盡的使用方法,只有自己down一個manual了

            在Cygwin下,doxygen采用GNU的libiconv進行文字編碼的轉換,以UTF-8作為默認編碼。

            使用doxygen生成config-file模板后,可以在config-file中進行一些項目設置(有注釋的,看的懂)。

            為了支持中文,我DOXYFILE_ENCODING用的是EUC-CN,但輸出文檔的語言OUTPUT_LANGUAGE卻選了English。兩點原因:

            o EUC-CN(各種漢字編碼知識就不在此普及了,你可以認為簡體字編碼都是EUC-CN)和UTF-8不同,但OUTPUT_LANGUAGE的各種語言都是使用的UTF-8,所以兩種編碼不可能同時顯示,當然,你可以把EUC-CN全轉成UTF-8。編碼不是高級的技術,但對于非英語用戶絕對是一個噩夢后來發現是我自己學藝不精,DOXYFILE_ENCODING只是配置文件的編碼格式而已,而識別中文文檔只需要修改INPUT_ENCODING成EUC-CN即可,OUTPUT_LANGUAGE自然設置成Chinese也不會有問題,因為doxygen采用UTF-8輸出,使用中文輸出不會有亂碼問題

            o 雖然我的英文很蹩腳,雖然我的文檔中多有中文注釋。但像doxygen中文輸出的文檔中把class、public都給你翻譯成中文,你也受不了,這也英文水平無關。

            config-file中的其他內容我現在也用不到,就沒有仔細看。

            因為Kevin會在公司里講一下doxygen,doxygen的manual也講的很詳細,我就省點時間,不翻譯文檔了。

            簡單的一個Doxygen的測試在這里。

            posted @ 2009-08-28 17:14 Fox 閱讀(2914) | 評論 (5)編輯 收藏

            2009年8月24日

            本文同步自游戲人生

            我屈服了,還是VS用的方便。

            之前在Cygwin下已經可以使用的ACE,因為閱讀代碼太不方便(對于一個WinEr來說),上午在VS下面花了幾分鐘就把ACE配好了,而且使用$(ACE_ROOT)\examples\C++NPv1的代碼跟蹤調試,太習慣了。

            按照$(ACE_ROOT)\ACE-INSTALL.html的安裝說明:

            o 選擇并打開$(ACE_ROOT)\ace\ace_vc9.sln

            o 添加config.h并加入以下內容:

                #define ACE_HAS_STANDARD_CPP_LIBRARY 1
                #include "ace/config-win32.h"

            o F7

            -----------------------------------------------

            OK,現在$(ACE_ROOT)\lib下面已經生成了ACEd.dll、ACEd.lib,再設置一下系統環境變量(運行程序必需)和VC++目錄(調試程序必需)。可以使用了:

            o 選擇并打開$(ACE_ROOT)\examples\C++NPv1

            o F7

            o for (; ; ) { F12, F9, F5, F10, F11 }

            -----------------------------------------------

            半個小時就搞定了當時一個星期的折騰……

            結論:對于一個不忠實的Win Coder,在MinGW, Cygwin, UNIX…下面裝B是要付出代價的。

            當然,家里的機器就讓它還一直跑Cygwin吧。

            posted @ 2009-08-24 11:59 Fox 閱讀(2525) | 評論 (6)編輯 收藏

            2009年8月19日

            本文同步自游戲人生

            /*--------- Hello.cc ---------*/

            /** Hello.cc:
            * @File:   Hello.cc
            * @Author: Fox <yulefox at gmail dot com>
            * @Date:   Aug. 19th, 2009
            * @Brief:  Test ACE log module application
            */

            #define ACE_NTRACE 0            /// trace the calling position

            #include "ace/Log_Msg.h"        /// include log module

            int ACE_TMAIN(int, ACE_TCHAR *[])
            {
                 ACE_TRACE(ACE_TEXT("main"));

                 ACE_DEBUG((LM_INFO, ACE_TEXT("%IStart\n")));
                 ACE_DEBUG((LM_INFO, ACE_TEXT("%IEnd\n")));

                 return 0;
            }

            /*--------- makefile ---------*/

            BIN     = hello                       # src & exe file name
            SRC     = $(addsuffix .cc, $(BIN))    # src file suffix
            LIBS    = -lACE                       # libACE.dll under cygwin

            include $(ACE_ROOT)/include/makeinclude/wrapper_macros.GNU
            include $(ACE_ROOT)/include/makeinclude/macros.GNU
            include $(ACE_ROOT)/include/makeinclude/rules.common.GNU
            include $(ACE_ROOT)/include/makeinclude/rules.nonested.GNU
            include $(ACE_ROOT)/include/makeinclude/rules.bin.GNU
            include $(ACE_ROOT)/include/makeinclude/rules.local.GNU

            /*--------- Compilation ---------*/

            GNUmakefile: /home/fox/ace/GNUmakefile MAKEFLAGS=k

            g++ -Wpointer-arith -mthreads -mtune=pentiumpro -O3 -g -pipe    -pipe   -I/usr/\
            share/ace -DACE_HAS_EXCEPTIONS -DACE_NO_INLINE  -c -o .obj/hello.o hello.cc
            g++ -Wpointer-arith -mthreads -mtune=pentiumpro -O3 -g -pipe    -pipe   -I/usr/\
            share/ace -DACE_HAS_EXCEPTIONS -DACE_NO_INLINE  -Wl,--enable-auto-import -Wl,-E\
            -L/usr/share/ace/lib -o hello .obj/hello.o  -lACE

            Compilation finished at Wed Aug 19 00:35:42

            /*--------- Result ---------*/

            $ ./hello.exe
            (14417928) calling main in file `hello.cc' on line 13
                Start
                End
            (14417928) leaving main

            -------------------------------------------------------

            更多內容請參考C++NP(C++ Network Programming) vol.1 & vol.2和APG(The ACE Progrmmer's Guide)

            忙活了一晚上,終于知道怎么包含頭文件了,在gcc的編譯選項中用 -I或/I$(ACE_ROOT):

            本例中是:-I/usr/share/ace

            結果后面庫又鏈接不上,聯想以前使用OpenGL庫的LIBS,終于靠一個-lACE搞定。

            因為不愿意用MPC,總感覺再多花些時間去弄又只是離題更遠了,有興趣的同學自然是可以通過ACE的官網找到所有問題的答案。

            這樣一來,ACE在cygwin下從安裝到使用也就告一段落了,后面的問題就比較easy了,無非是你用ACE做什么。而我也不會再就ACE && cygwin寫什么心得了,總算見證了這兩天的搗騰。

            posted @ 2009-08-19 10:05 Fox 閱讀(2602) | 評論 (4)編輯 收藏

            久久99国产精一区二区三区| 麻豆国内精品久久久久久| 精品熟女少妇a∨免费久久| 69久久夜色精品国产69| 成人a毛片久久免费播放| 日本久久久久久久久久| 久久青青草原精品国产| 99久久精品免费看国产免费| 人妻无码精品久久亚瑟影视| avtt天堂网久久精品| 亚洲AV伊人久久青青草原| 精品综合久久久久久888蜜芽| 国产激情久久久久影院| 国产偷久久久精品专区| 精品国产91久久久久久久a| 亚洲国产精品无码久久一线| 久久久久久毛片免费看| 久久亚洲精品视频| 日韩人妻无码精品久久免费一| 久久婷婷五月综合成人D啪| 99久久这里只有精品| 人妻精品久久久久中文字幕69| 久久综合亚洲色HEZYO国产| 日韩精品久久久久久| 久久精品国产99久久无毒不卡| 久久久久久国产a免费观看黄色大片 | 久久伊人精品一区二区三区| 国产69精品久久久久9999| 好属妞这里只有精品久久| 男女久久久国产一区二区三区| 国产99久久久国产精品小说| 久久本道久久综合伊人| 丰满少妇人妻久久久久久4| 久久久综合九色合综国产| 久久se精品一区精品二区| 久久婷婷国产麻豆91天堂| 久久99国内精品自在现线| 久久精品国产福利国产秒| 国产精品久久久久无码av| 99久久精品免费看国产| 狠狠色伊人久久精品综合网|