• <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>
            牽牛散步
            仗劍走天涯

            2006年11月28日

            一、安裝

            1 linux 安裝好后已經裝好了 cvs ,可用 rpm -qa|grep cvs 查看。

            如果沒有安裝你可以在 Redhat 2 張光盤上找到,另外你也可以在網上下載到最新的 rpm 包。很容易找,其實不存在什么 linux 版本。

            ?

            2 、創建 cvs 使用的目錄: mkdir /home/mycvstest

            ?

            3 、創建 cvs 使用的用戶和組

            groupadd cvs

            useradd -g cvs -G cvs –d /home/mycvstest cvs

            passwd cvs

            ?

            4 、修改 mycvstest 的用戶: chown -R cvs:cvs /home/mycvstest

            ??????????????????????? chmod 755 /home/mycvstest

            ?

            5 、切換用戶: su cvs

            ?

            6 、創建源碼倉庫: mkdir /home/mycvstest/drcls drclgw

            ?

            7 、初始化源碼倉庫: cvs -d /home/mycvstest/drcls init

            ?????????????????? cvs -d /home/mycvstest/drclgw init

            ??????????????? ???chmod 755 /home/mycvstest/drcls drclgw

            初始化后可以在目錄下見到新增加的 CVSROOT 目錄, cvs 所有的控制信息都在這個目錄里。

            ?

            8 、退回到 root 用戶,建立 CVS 服務啟動文件, CVS 使用 xinetd 方式啟動。 ?

            ?

            vi /etc/xinetd.d/cvspserver

            ?

            service cvspserver

            {

            disable = no

            flags = REUSE

            socket_type = stream

            wait = no

            user = root

            server= /usr/bin/cvs

            server_args= -f --allow-root=/home/mycvstest/drcls

            --allow-root=/home/mycvstest/drclgw pserver

            log_on_failure += USERID

            }

            ?

            注:紅色的參數很重要,由于 xinetd server_args 長度限制 , 當你想運行很多的單個倉庫的時候 ( 就是有很多個模塊時,比如 drcrgw), 但在 server_args 中無法輸入這么多單個的倉庫, 可以采取以下方式解決(現在實驗室 90 上就用的這種方式):

            ?

            #> vi cvspserver

            ?

            service cvspserver

            {

            ??????? disable = no

            ??????? socket_type = stream

            ??????? wait = no

            ??????? user = root

            ??????? server = /usr/bin/cvs.run

            ??????? server_args =""

            }

            編寫 cvs.run 腳本

            vi /cvsroot/cvs.run

            #!/bin/bash

            /usr/bin/cvs -f

            --allow-root=/home/mycvstest/drcls

            --allow-root= /home/mycvstest/drclgw

            pserver

            ?

            chmod a+x cvs.run

            ?

            9 、加入 cvs 服務:

            ?

            #>vi /etc/services

            ?

            cvspserver 2401/tcp #pserver cvs service

            cvspserver 2401/udp #pserver cvs service

            ?

            10 、啟動 cvs 服務:

            ?

            #> service xinetd restart

            ?

            11 、檢查 cvspserver 服務是否已經啟動:

            ?

            #> netstat -l |grep cvspserver

            應該有如下結果:

            tcp 0 0 *:cvspserver *:* LISTEN

            ?

            ?

            二、使用和管理

            1 、創建 cvs 用戶:

            cd /home/mycvstest/drcls/CVSROOT

            htpasswd –c passwd zhangcan (htpasswd 命令要安裝 apache 后才有,此命令創建一個 passwd 文件,里面內容為新建 cvs 用戶的用戶名和密碼 )

            vi passwd ( 在新建的用戶后手工加入用戶屬于的組 cvs)

            例如: zhangcan:dgeagasdgasdr:cvs? 藍色字符串表示加密后的密碼。

            ?

            2 、賦予用戶讀寫權限

            手工在 CVSROOT 目錄中建立 readers writers 文件。 Readers 文件中的用戶只有讀權限, writers 中的用戶具有讀寫權限,一行只寫一個用戶名。

            ?

            3 、登錄 cvs

            在客戶機 Linux 下面用命令:

            export CVSROOT=:pserver:zhangcan@192.168.100.197:/home/mycvstest/drcls

            cvs login

            ?

            4 、向源碼倉庫中導入源碼

            首先進入你本機上安裝源碼的目錄,然后使用以下命令:

            cvs import –m “this is my soure code” drcls NISEC test start

            -m 表示在 cvs 歷史文件顯示的消息, drclst 為你指定的源碼目錄名, NISEC 為供應商標簽, test 為發行標簽(這兩項可以不要), start 必須要。

            ?

            5 checkout 出需要修改的源代碼

            cvs co drcls? 在你的當前目錄下會發現多了一個 drcls 目錄,要修改的源碼都在里面

            co checkout 的簡寫

            ?

            6 、提交修改

            假設剛才對 readme 文件進行了修改,現在提交

            cvs commit –m “modify a few of wrong words”? readme

            命令執行后會提示版本已經改為 1.2

            ?

            7 checkout 出以前的版本

            如果想檢出以前的版本可以用以下命令:

            cvs co –r 1.1 drcls/readme 或者 cvs –D yesterday drcls/readme

            ?

            8 、刪除文件

            若想從源碼倉庫中刪除 readme 文件,首先應把客戶機工作目錄中的 readme 文件刪除,然后使用 cvs 的刪除命令,最后提交刪除,流程如下:

            rm readme

            cvs rm readme

            cvs commit –m “ 現在不需要這個 readme 文件 ? readme

            如果系統類似錯誤: cannot remove file `INSTALL' which has a numeric sticky tag of `1.1'

            可以用命令 cvs update –A readme 后再刪除文件。


            以上為火山哥提供的,以下是我添加的部份:
            1.在reader和writes文件中添加使用用戶時要注意,當在reader中添加了某一只讀用戶后就不要在writers中添加此用戶,如果在兩個文件中都添加同一用戶的話,在使用cvs時,CVS服務器會把此用戶當做只讀用戶看待,當使用一些命令如import時會產生權限問題,以下是linux關于此問題的說明:
            /* This command has the potential to modify the repository, so
            ? ?* we check if the user have permission to do that.
            ? ?*
            ? ?* (Only relevant for remote users -- local users can do
            ? ?* whatever normal Unix file permissions allow them to do.)
            ? ?*
            ? ?* The decision method:
            ? ?*
            ? ?*? ? If $CVSROOT/CVSADMROOT_READERS exists and user is listed
            ? ?*? ? in it, then read-only access for user.
            ? ?*
            ? ?*? ? Or if $CVSROOT/CVSADMROOT_WRITERS exists and user NOT
            ? ?*? ? listed in it, then also read-only access for user.
            ? ?*
            ? ?*? ? Else read-write access for user.
            ? ?*/

            posted @ 2006-11-28 16:18 牽牛散步 閱讀(2031) | 評論 (3)編輯 收藏

            2006年9月18日

            剛學習ICE時,在VC++6.0下配置ICE工程簡直是個惡夢,死活配不來DEBUG版本的,開發全在RELEASE版本下.很痛苦.最近研究了下,成功的配置成功了,方法如下:

            1.Project Settings >> C/C++(Tab) >> Category:Code Generation >> User run-time library:Debug Multithreaded DLL

            2.Project Settings >> C/C++(Tab) >> Category:Preprocessor >> Additional include directories:(在此項中填入一個點".",表示根目錄)

            3.Project Settings >> C/C++(Tab) >> Category:C++ Language >> 勾選"Enable Run-Time Type Information(RTTI)"項

            4.Project Settings >> Link(Tab) >> Category:General>>在"Object/library modules:"框中的未首加入兩個包"iced.lib"和"iceutild.lib"


            這樣,整個ICE工程的DEBUG版本就建立完成了.
            以上方法在 VC++6.0 SP6 和 ICE3.0.0 下通過

            posted @ 2006-09-18 17:58 牽牛散步 閱讀(3793) | 評論 (6)編輯 收藏

            2006年4月21日

            對動態鏈接庫的概念其實還很模糊,自己的理解是:
            把一些常用的代碼,如函數,類等,編譯成一個"包"即DLL(WINDOWS下)或者SO(LINUX下)文件,
            然后供其它程序使用時直接調用里面封閉的函數即可,實現的代碼的重用,也節省了
            硬盤空間(這點可能是次要的吧).在WIDOWS下利用VC++可方便的生成DLL,在LINUX下則需要通過
            各種編譯命令來實現,對于像我這種菜鳥級程序員來說是個不小的挑戰.
            下面用個簡單的例子來說明生成一個.SO文件和如何使用它:
            1.我這有幾個文件:
            ConfigMap.cpp ConfigMap.h (讀配置文件類)GetWinState.cpp GetWinState.h(ICE接口文件,由SLICE生成) GetWinSysState.cpp GetWinSysState.h (遠程接口實現文件)
            SocDbInfo.cpp(封裝的一個類,用于調用ICE接口實現相關操作)
            說明:此程序是為了獲得遠程主機(WINDOWS)上一些系統信息,比如:CPU占用率,硬盤使用情況,數據
            庫連接狀態,內存使用情況等.
            我想利用這些文件生成動態鏈接庫.SO,主要是調用SocDbInfo.cpp里的類.然后可以拿到任意一個系統中(LINUX)去用.
            2.開始編譯:$c++ -I. -I$ICE_HOME/include -c *.cpp
            編譯后生成連接文件(我一直這樣叫,可能不對哈),即以.O結尾的
            3.生成動態鏈接庫:$c++ -shared -o?libMyApp.so *.o -L$ICE_HOME/lib -lIce -lIceUtil
            這樣就生成了libMyApp.so文件,即我們所要的
            4.使用動態鏈接庫:
            新建兩個文件Demo.cpp Demo.h(使用libMyApp.so提供的一些函數),
            在Demo.h中聲明了libMyApp.so中提供的函數和結構體,
            Demo.h:代碼如下:

            struct MemoryInf
            {
            ?int TotalMem;//×ü?ú′?′óD?
            ?int ValidMem;//?éê1ó??ú′?′óD?
            ?int VirtualMem;//Dé?a?ú′?′óD?
            };
            struct DiskInf
            {
            ?int TotalSpace;//ó2?ì′óD?
            ?int FreeSpace;//ê£óà????′óD?
            };
            struct DbInf
            {
            ?int DbStat;//êy?Y?a×′ì?:"0"±íê?êy?Y?a′|?ú1?±?×′ì?,"1"±íê?êy?Y?a?y3£′ò?a,"2"±íê?êy?Y?a′|?ú1ò?e×′ì?
            ?int DbConnNum;//êy?Y?aá??óêy
            };
            bool Inital( char *ResHostIP);//3?ê??ˉí¨D??÷
            bool DesIceCom();//?ú?ùICEí¨D??÷
            MemoryInf GetMemInf();//μ?μ??ú′?D??¢
            int GetCpuInf();//μ?μ?CPU??ó??ê
            DbInf GetDbStat();//μ?μ?êy?Y?aD??¢
            DiskInf GetDiskStat();//μ?μ?ó2?ìD??¢



            注:亂碼是由于我的LINUX下不支持中文哈,是注釋不用管它
            Demo.cpp:代碼如下:
            #include <string.h>
            #include <iostream>
            #include "Demo.h"


            int main( int argc , char* argv[])
            {

            ?MemoryInf mymem;
            ?DiskInf mydisk;
            ?DbInf mydb;
            ?Inital( argv[1]);
            ?mydisk = GetDiskStat();
            ?mymem = GetMemInf();
            ?mydb = GetDbStat();
            ?printf("disk total space:%d\n",mydisk.TotalSpace);
            ?printf("disk FreeSpace space:%d\n",mydisk.FreeSpace);
            ?printf("Memory TotalMem:%d\n",mymem.TotalMem);
            ?printf("ValidMem:%d\n",mymem.ValidMem);
            ?printf("VirtualMem:%d\n",mymem.VirtualMem);
            ?printf("DbConnNum:%d\n",mydb.DbConnNum);
            ?printf("DbStat:%d\n",mydb.DbStat);
            ?printf("cpu:%d\n",GetCpuInf());
            ?DesIceCom();
            ?return 1;
            }


            5.編譯文件生成可執行程序:
            用以下命令:
            $c++?-lMyApp?-o?Demo?Demo.cpp
            說明:-lMyApp參數表示,用動態鏈接庫libMyApp.so一起進行編譯,對了libMyApp.so最好放在/usr/lib目錄下哈
            如不出意外剛會生成名為Demo的可執行文件

            posted @ 2006-04-21 15:06 牽牛散步 閱讀(7157) | 評論 (0)編輯 收藏

            2006年4月14日

            #include <Ice/Ice.h>
            #include <iostream>
            #include <GetWinSysState.h>
            #include <Winbase.h>
            #include <conio.h>
            #include <stdio.h>
            #include <fstream>
            #include <iostream>
            #include <string>
            #include <direct.h>

            #define SystemBasicInformation?????? 0
            #define SystemPerformanceInformation 2
            #define SystemTimeInformation??????? 3

            #define Li2Double(x) ((double)((x).HighPart) * 4.294967296E9 + (double)((x).LowPart))
            //ICE預編譯語句

            #ifdef _DEBUG
            #pragma comment(lib, "iced.lib")
            #pragma comment(lib, "iceutild.lib")
            #else
            #pragma comment(lib, "ice.lib")
            #pragma comment(lib, "iceutil.lib")
            #endif

            typedef struct
            {
            ??? DWORD?? dwUnknown1;
            ??? ULONG?? uKeMaximumIncrement;
            ??? ULONG?? uPageSize;
            ??? ULONG?? uMmNumberOfPhysicalPages;
            ??? ULONG?? uMmLowestPhysicalPage;
            ??? ULONG?? uMmHighestPhysicalPage;
            ??? ULONG?? uAllocationGranularity;
            ??? PVOID?? pLowestUserAddress;
            ??? PVOID?? pMmHighestUserAddress;
            ??? ULONG?? uKeActiveProcessors;
            ??? BYTE??? bKeNumberProcessors;
            ??? BYTE??? bUnknown2;
            ??? WORD??? wUnknown3;
            } SYSTEM_BASIC_INFORMATION;

            typedef struct
            {
            ??? LARGE_INTEGER?? liIdleTime;
            ??? DWORD?????????? dwSpare[76];
            } SYSTEM_PERFORMANCE_INFORMATION;

            typedef struct
            {
            ??? LARGE_INTEGER liKeBootTime;
            ??? LARGE_INTEGER liKeSystemTime;
            ??? LARGE_INTEGER liExpTimeZoneBias;
            ??? ULONG???????? uCurrentTimeZoneId;
            ??? DWORD???????? dwReserved;
            } SYSTEM_TIME_INFORMATION;


            // ntdll!NtQuerySystemInformation (NT specific!)
            //
            // The function copies the system information of the
            // specified type into a buffer
            //
            // NTSYSAPI
            // NTSTATUS
            // NTAPI
            // NtQuerySystemInformation(
            //??? IN UINT SystemInformationClass,??? // information type
            //??? OUT PVOID SystemInformation,?????? // pointer to buffer
            //??? IN ULONG SystemInformationLength,? // buffer size in bytes
            //??? OUT PULONG ReturnLength OPTIONAL?? // pointer to a 32-bit
            //?????????????????????????????????????? // variable that receives
            //?????????????????????????????????????? // the number of bytes
            //?????????????????????????????????????? // written to the buffer
            // );
            typedef LONG (WINAPI *PROCNTQSI)(UINT,PVOID,ULONG,PULONG);

            PROCNTQSI NtQuerySystemInformation;

            /*
            ? 功能:得到CPU使用狀態
            ? 參數:無
            ? 返回值:內存占用率
            ? 作者:牽牛散步
            */
            int GetCpuStat()
            {
            ?SYSTEM_PERFORMANCE_INFORMATION SysPerfInfo;
            ??? SYSTEM_TIME_INFORMATION??????? SysTimeInfo;
            ??? SYSTEM_BASIC_INFORMATION?????? SysBaseInfo;
            ??? double???????????????????????? dbIdleTime;
            ??? double???????????????????????? dbSystemTime;
            ??? LONG?????????????????????????? status;
            ??? LARGE_INTEGER????????????????? liOldIdleTime = {0,0};
            ??? LARGE_INTEGER????????????????? liOldSystemTime = {0,0};
            ?int UsageCpu = 0;
            ??? NtQuerySystemInformation = (PROCNTQSI)GetProcAddress(
            ??GetModuleHandle("ntdll"),
            ??"NtQuerySystemInformation"
            ??);
            ?
            ??? if (!NtQuerySystemInformation)
            ??????? return 0;
            ?
            ??? status = NtQuerySystemInformation(SystemBasicInformation,&SysBaseInfo,sizeof(SysBaseInfo),NULL);
            ??? if (status != NO_ERROR)
            ??????? return 0;
            ????
            ?for( int t = 0 ; t < 2 ; t++ )
            ??? {
            ??status = NtQuerySystemInformation(SystemTimeInformation,&SysTimeInfo,sizeof(SysTimeInfo),0);
            ??????? if (status!=NO_ERROR)
            ??????????? return 0;
            ??
            ??????? status = NtQuerySystemInformation(SystemPerformanceInformation,&SysPerfInfo,sizeof(SysPerfInfo),NULL);
            ??????? if (status != NO_ERROR)
            ??????????? return 0;
            ??
            ??if (liOldIdleTime.QuadPart != 0)
            ??{
            ??????????? dbIdleTime = Li2Double(SysPerfInfo.liIdleTime) - Li2Double(liOldIdleTime);
            ??????????? dbSystemTime = Li2Double(SysTimeInfo.liKeSystemTime) - Li2Double(liOldSystemTime);
            ???
            ??????????? dbIdleTime = dbIdleTime / dbSystemTime;
            ???

            ??????????? dbIdleTime = 100.0 - dbIdleTime * 100.0 / (double)SysBaseInfo.bKeNumberProcessors + 0.5;
            ???UsageCpu = (int)dbIdleTime;

            ??}
            ??
            ??????? // store new CPU's idle and system time
            ??????? liOldIdleTime = SysPerfInfo.liIdleTime;
            ??????? liOldSystemTime = SysTimeInfo.liKeSystemTime;
            ??
            ??????? // wait one second
            ??????? Sleep(500);
            ??? }
            ?return UsageCpu;
            ?
            }


            /*
            ? 功能:得到內存使用狀態
            ? 參數:無
            ? 返回值:內存信息結構體[包括總的物理內存,還可使用內存,虛擬內存,單位為K]
            ? 作者:牽牛散步
            */
            MemoryInf MemorySta()
            {
            ?MemoryInf tmp;//在ICE SLICE里定義的信息結構體
            ?MEMORYSTATUS memStatus;
            ?GlobalMemoryStatus(&memStatus);
            ?DWORD tom=memStatus.dwTotalPhys/1024;
            ?DWORD mem=memStatus.dwAvailPhys/1024;
            ?DWORD res=memStatus.dwAvailVirtual/1024;
            ?tmp.TotalMem = (int)tom;
            ?tmp.ValidMem = (int)mem;
            ?tmp.VirtualMem = (int)res;
            ?return tmp;
            }


            /*
            ?功能:得到硬盤使用情況
            ?參數:無
            ?返回值:硬盤信息結構體
            ?作者:牽牛散步
            */
            DiskInf GetDiskSta()
            {
            ?ULARGE_INTEGER FreeAvailable,TotalNum,TotalFreeNum;

            ?char p[3];
            ?bool b_flag;
            ?DiskInf tmp;//ICE SLICE里定義的硬盤信息結構體
            ?tmp.TotalSpace = 0;
            ?tmp.FreeSpace = 0;
            ?//得到有效的驅動器名,即盤符
            ?for( int drive = 1; drive <= 26; drive++ )
            ?{
            ??if( !_chdrive( drive ) )
            ??{
            ???memset( p , 0 , sizeof(p));
            ???p[0] = drive + 'A' - 1;
            ???p[1] = ':';
            ???p[2] = '\0';
            ???b_flag = GetDiskFreeSpaceEx( p ,&FreeAvailable,&TotalNum,&TotalFreeNum );
            ???if( b_flag )
            ???{
            ????tmp.TotalSpace += (int)(TotalNum.QuadPart/(1024*1024));
            ????tmp.FreeSpace += (int)(FreeAvailable.QuadPart/(1024*1024));
            ???}
            ??}
            ?}
            ?return tmp;
            }

            int main()
            {
            ???return 1;
            }

            posted @ 2006-04-14 11:35 牽牛散步 閱讀(10698) | 評論 (3)編輯 收藏

            2006年2月28日

            文 CSDN 孟巖  轉載自2005年第11期《程序員》雜志

            不知不覺,C++迎來了自己20歲的生日。20年來,C++從一個實驗室語言成長為軟件產業一流主導語言,在實際應用中取得了巨大的成功,同時也催生了大量為技術人員耳熟能詳的經典技術著作,比如Bjarne Stroustrup的《TC++PL》和《D&E》,Stan Lippman的《C++ Primer》,Scott Meyers的《Effective C++》,GoF的《Design Patterns》,Andy Koenig的《Ruminations on C++》,Herb Sutter的《Exceptional C++》,Andrei Alexandrescu的《Modern C++ Design》,Addison Wesley的“C++ in Depth”系列等。C++領域的一些經典圖書不但對于C++語言的發展起到了巨大的推動作用,而且對于其他相關技術領域也起到了指導和促進作用。例如Scott Meyers的“Effective系列”,開辟了技術圖書寫作的新風格,而“Design Patterns”的影響,更是遠遠超出C++的范疇。這些經典的好書,已經成為C++輝煌歷史的一部分而被人們銘記。 

            20年后的今天,軟件產業的規模和環境已經發生了深刻的變化。如今企業級應用整合與開發的任務主要由JavaC#Visual Basic以及各種新型動態語言來承擔,而C++的應用場合也有所收縮,不再是像1990年代中期那樣從上到下包打天下,而是呈現出鮮明的應用領域特色。相應的,近期的C++技術圖書也更加注重在特色領域的發揮。下面我們分別從幾個角度來了解近期C++圖書的熱點,并且展望未來一年中C++技術圖書中值得注意的選題。 

               

            經典著作全面翻新 

            這一兩年,一大批經典技術圖書都經歷了一次更新換代,C++經典圖書自然也不例外。第一個要說的就是Scott Meyers的《Effective C++》。這本書在1991年推出第一版,1998年推出第二版,在C++技術的傳播與教育方面居功至偉。包括我在內的很多C++開發者都是通過閱讀這本書而尋得升堂入室的門徑。今年,《Effective C++》推出了第三版。這一版決不是第二版的簡單修訂,而是根據八年來C++所發生的巨大變化而進行的一次全面改寫,幾乎是一本全新的書。其內容涵蓋了舊版的精髓,也體現了諸如Boost庫、TR1標準等C++領域最新成果。如果說這本書的內容發生了重大的變化,那么可以說不變的是這本書的地位——它仍然是每個嚴肅的C++開發者都應當反復閱讀領悟的重要作品。 

            另一本翻新的著作是Stan Lippman的《C++ Primer》第四版。這本書的歷史地位無需贅言,迄今為止仍然是對C++介紹最全面的一本著作。第四版進行了大幅度的刪修,篇幅減少近300頁,而內容也大幅修改,以反映C++近年來的變化。對于希望系統學習C++的新手,或者希望擁有一本手冊以備查的老手來說,這本書的地位是不可取代的。 

            有傳言說GoF打算翻新“Design Patterns”。該書第一版于1995年初版,此后十年,設計模式領域的理論探討和實踐基本上仍然圍繞這本書中的觀點和內容進行,少有突破。已經有不少開發者對于這種情況表示了不滿,認為初版中的一些觀點已經過時,甚至對現在的一些技術應用構成思想上的羈絆。但是此書實在聲名太盛,真正的突破恐怕只能由GoF自己完成,因此本書新版如果推出,必將是技術界內的一樁大事。不過傳言亦稱,GoF對于新版“Design Patterns”是否繼續使用C++作為示范語言存在分歧。不管怎樣,相信廣大讀者會對這本書抱有高度的關注。 

            新銳佳作劍走偏鋒 

            近年來,一批C++技術新銳崛起,給我們帶來了一批令人耳目一新的C++好書。在技術上,這批圖書偏重于C++模板風格的巧妙運用,富于智趣而對實踐的關注略顯不足。這類圖書以2001Andrei Alexandrescu的《Modern C++ Design》為開端,之后又有《Boost Graphic Library》,《C++ Template Metap- rogramming》等。這些書一度以其新穎的思想和奇妙的技巧吸引了大家的目光,但由于缺乏實踐的支撐,逐漸褪去光環。這兩年,這類題材逐漸縮減。即使像Andrei Alexandrescu那樣的模板技術奇才,最近與Herb Sutter合作的《C++ Coding Standard》也并沒有過多地炫耀模板技巧,而是中規中矩地對C++編碼的經驗做了貼近實踐的總結。這本書與Steve McConnell的經典著作《Code Complete II》相配合,應成為每一位C++開發者必讀的基本著作,將幫助讀者奠定堅實的編碼和微觀設計技術基礎。 

            BoostC++社群盡人皆知的“準標準庫”,其中大量的組件已經基本成熟并可供應用。最近Bjorn Karlsson撰寫的《Beyond C++ Standard Library: An Introduction to Boost》是市面上第一本全面介紹Boost的著作。對于那些勇于吃螃蟹的C++先鋒開發者來說,這本書無疑是值得一讀的。不過在C++社群,對Boost庫還存在爭議。大部分C++實踐者比較保守,這本書對他們的吸引力恐怕將打折扣。 

            Stephen Dewhurst是近年來比較活躍的新銳作家,他的兩本書《C++ Gotchas》和《C++ Common Knowledge》,單獨來看都是值得一讀的好書,可惜現在不是1995年,而是2005年,Scott MeyersHerb Sutter煌煌巨著已然危急九五,Dewhurst只好自嘆施手略慢。不過我們希望他能夠再接再厲,找到創新點,突破前人。 

            相比之下,中文版即將問世的《Imperfect C++》就比較有新意,很值得一讀,以至于Bjarne Stroustrup都相當贊賞。這位C++之父表示,如果不是因為該書太厚,他一定會將其納入著名的“C++ in Depth”系列。這在事實上肯定了本書的高質量。我個人認為,這本書是一年來出版的最值得精讀的C++著作,其中對于不少實踐中經常遇到的難題進行了深入的分析,給出了實實在在的解決方案。作者Matt Wilson憑借此書以及其系列模板程序庫崛起為C++社群中引人注目的新星,實在可喜可賀。 

               

            關鍵領域期待佳作 

            未來C++將主要在系統級復雜應用程序、高性能、實時中間件及嵌入式領域施展,同時,隨著多核CPU的的普及和網絡安全重要性的空前提升,在并發程序設計和安全程序設計方面,C++也將獲得新的應用空間。因此,在這些具體領域的C++著作更值得關注。 

            1996年,John Lakos出版了《Large Scale C++》一書,該書與 “C++ In Depth”系列中的《Applied C++》一樣,是C++著作中極少數實戰派佳作,其中字字句句都來自于作者豐富的實際項目經驗,對于一線的C++開發者來說是特別值得咀嚼體味的好書。可惜這本書在國內一直沒有得到應有的重視,這可能跟本書中譯本出版時間過晚,且翻譯質量不佳有關。好消息是,John Lakos即將在2006年推出其新作《Scalable C++》。我本人將此書視為未來一年中C++領域最令人期待的作品。這一方面是出于我對于作者的高度信任,另一方面是由于該書題材的極端重要性。該書副標題為“基于組件的軟件開發”,而“組件化”這一主題,是C++十幾年來的一塊心病。在未來,無論C++應用在何種場合,“組件化”是必不可少的基本要求。而COM技術雖然在組件化方面比較成熟,但是不具有可移植性,而且對于無須跨語言的C++開發項目來說過于復雜。因此,C++社群需要自己探討經濟適用的組件化實用方案。John Lakos本人從事大型復雜應用軟件開發多年,在這方面的經驗無人能敵,由他來對這個主題進行深入剖析,并且給出實際解決方案,毫無疑問是再合適不過的了。雖然這本書還在寫作過程中,但是我們有理由對其抱有充分信心。在此我也呼吁本書中文版未來的出版者認真對待此書的翻譯制作,不要重蹈《Large Scale C++》的覆轍。 

            說到COM,自從.NET推出,COM的書幾乎一夜之間絕了種,幾乎無人再勇于炒這碗冷飯。但這次出版界的茶實在涼得快了些,事實上直到今天,COM開發仍然是Windows平臺上應用開發的一個重要方向。特別是在前兩年微軟Windows核心向.NET全面轉型的左傾冒險主義的嘗試以失敗告終后,在可見的未來,Windows操作系統將繼續構建在C/C++COM的基礎之上——這就確保了COMWindows平臺上的重要地位將至少延續到2011年之后。因此,COMATLWTL開發技術在未來幾年都還是頗具意義的出版題材。特別是近年來ATL/WTL的發展之快,相比之下,技術出版在這方面出現了一個空白點。此外,開源的Mozilla項目提供了一個可移植到所有主流平臺的COM實現,這對于C++開發者來說是很具有吸引力的,卻一直沒有出版資源的關注,令人遺憾。 

            C++近年來的一個應用熱點是復雜網絡應用的開發,ACE在這方面已經成為越來越流行的選擇,而ICE作為目前可用的最先進的高性能中間件產品,崛起的勢頭很猛。這兩方面目前都有一些好書,特別是最近出版的《ACE Programmers Guide》,對于ACE編程入門很有好處。而ICE 1.3版的手冊早已由馬維達先生譯成中文,可在網上自由獲得。不過坦率地說,這方面的圖書還遠遠不夠,ACE原作者Doug Schmidt所著的兩卷本《C++ Network Programming》可讀性和實用性不足,而ICE手冊深度和廣度都顯不夠,我們期望這方面能有更好的著作出現。 

            隨著多核CPU的普及,并發程序設計將成為C++技術上的一個新熱點。這方面目前的好書幾乎沒有,不知道這個巨大的空白將由何方神圣來填補。 

            同樣,網絡安全重要性的空前提升對C++開發提出了很多新的具體要求,很多C++老手面臨一個“再教育”的問題。這方面Microsoft Press的《Writing Safe Code》,OReilly的《Secure Programming Cookbook for C and C++》,以及最近Pearson出版的《Secure Coding in C and C++》都是不錯的參考。不過我發現目前C++開發者并未普遍重視這個問題。也許管理層還需要更加“血淋淋的教訓”來刺激一下,才會有革新的動力。 

            在嵌入式方面,由于應用復雜度的逐漸提升和Symbian OS/Windows CE等面向消費的高級嵌入式操作系統的廣泛應用,給C++提供了一個廣闊的發揮空間。可惜在這方面,真正堪稱經典的好書還是鳳毛麟角,看來還需要時間和經驗的積淀。不過有一本書特別值得一提,北航出版社去年引進的《嵌入式系統的微模塊化設計》被國際嵌入式開發領域公推為數年來年度最重要的嵌入式軟件開發技術著作,其中含有一些意義深遠的創新思想,非嵌入式開發者也有必要了解此書的大致思想。 

               

            總結 

            程序設計語言數以千計,能夠廣為流傳的不過幾十種,而能夠風光20年的更是屈指可數。Fortran已經問世50年,仍然是科學計算首選,C語言輝煌30年,至今老當益壯。C++順利地度過了自己的20歲生日,相信屬于它的日子還很長很長。作為C++開發者,我們也希望看到優秀的C++技術圖書不斷涌現。歷史證明,C++領域內的技術創新,不但對于C++開發具有重大意義,而且對于整個軟件開發技術都具有重大意義。我們有理由相信,在下一個十年里,以高水平C++技術專家和作家為代表的C++技術社群能夠繼續為軟件技術做出突出的貢獻。

             

            posted @ 2006-02-28 17:07 牽牛散步 閱讀(696) | 評論 (0)編輯 收藏
             

             

            在所有的預處理指令中,#Pragma 指令可能是最復雜的了,它的作用是設定編譯器的狀態或者是指示編譯器完成一些特定的動作。#pragma指令對每個編譯器給出了一個方法,在保持與C和C ++語言完全兼容的情況下,給出主機或操作系統專有的特征。依據定義,編譯指示是機器或操作系統專有的,且對于每個編譯器都是不同的。
            其格式一般為: #Pragma Para
            其中Para 為參數,下面來看一些常用的參數。


            (1)message 參數。 Message 參數是我最喜歡的一個參數,它能夠在編譯信息輸出窗
            口中輸出相應的信息,這對于源代碼信息的控制是非常重要的。其使用方法為:
            #Pragma message(“消息文本”)
            當編譯器遇到這條指令時就在編譯輸出窗口中將消息文本打印出來。
            當我們在程序中定義了許多宏來控制源代碼版本的時候,我們自己有可能都會忘記有沒有正確的設置這些宏,此時我們可以用這條指令在編譯的時候就進行檢查。假設我們希望判斷自己有沒有在源代碼的什么地方定義了_X86這個宏可以用下面的方法
            #ifdef _X86
            #Pragma message(“_X86 macro activated!”)
            #endif
            當我們定義了_X86這個宏以后,應用程序在編譯時就會在編譯輸出窗口里顯示“_
            X86 macro activated!”。我們就不會因為不記得自己定義的一些特定的宏而抓耳撓腮了


            (2)另一個使用得比較多的pragma參數是code_seg。格式如:
            #pragma code_seg( ["section-name"[,"section-class"] ] )
            它能夠設置程序中函數代碼存放的代碼段,當我們開發驅動程序的時候就會使用到它。

            (3)#pragma once (比較常用)
            只要在頭文件的最開始加入這條指令就能夠保證頭文件被編譯一次,這條指令實際上在VC6中就已經有了,但是考慮到兼容性并沒有太多的使用它。

            (4)#pragma hdrstop表示預編譯頭文件到此為止,后面的頭文件不進行預編譯。BCB可以預編譯頭文件以加快鏈接的速度,但如果所有頭文件都進行預編譯又可能占太多磁盤空間,所以使用這個選項排除一些頭文件。
            有時單元之間有依賴關系,比如單元A依賴單元B,所以單元B要先于單元A編譯。你可以用#pragma startup指定編譯優先級,如果使用了#pragma package(smart_init) ,BCB就會根據優先級的大小先后編譯。

            (5)#pragma resource "*.dfm"表示把*.dfm文件中的資源加入工程。*.dfm中包括窗體
            外觀的定義。

            (6)#pragma warning( disable : 4507 34; once : 4385; error : 164 )
            等價于:
            #pragma warning(disable:4507 34) // 不顯示4507和34號警告信息
            #pragma warning(once:4385) // 4385號警告信息僅報告一次
            #pragma warning(error:164) // 把164號警告信息作為一個錯誤。
            同時這個pragma warning 也支持如下格式:
            #pragma warning( push [ ,n ] )
            #pragma warning( pop )
            這里n代表一個警告等級(1---4)。
            #pragma warning( push )保存所有警告信息的現有的警告狀態。
            #pragma warning( push, n)保存所有警告信息的現有的警告狀態,并且把全局警告
            等級設定為n。
            #pragma warning( pop )向棧中彈出最后一個警告信息,在入棧和出棧之間所作的
            一切改動取消。例如:
            #pragma warning( push )
            #pragma warning( disable : 4705 )
            #pragma warning( disable : 4706 )
            #pragma warning( disable : 4707 )
            //.......
            #pragma warning( pop )
            在這段代碼的最后,重新保存所有的警告信息(包括4705,4706和4707)。
            (7)pragma comment(...)
            該指令將一個注釋記錄放入一個對象文件或可執行文件中。
            常用的lib關鍵字,可以幫我們連入一個庫文件。
            (8)用pragma導出dll中的函數

                傳統的到出 DLL 函數的方法是使用模塊定義文件 (.def),Visual C++ 提供了更簡潔方便的方法,那就是“__declspec()”關鍵字后面跟“dllexport”,告訴連接去要導出這個函數,例如:

            __declspec(dllexport) int __stdcall MyExportFunction(int iTest);

                把“__declspec(dllexport)”放在函數聲明的最前面,連接生成的 DLL 就會導出函數“_MyExportFunction@4”。

                上面的導出函數的名稱也許不是我的希望的,我們希望導出的是原版的“MyExportFunction”。還好,VC 提供了一個預處理指示符“#pragma”來指定連接選項 (不僅僅是這一個功能,還有很多指示功能) ,如下:

            #pragma comment(linker,"/EXPORT:MyExportFunction=_MyExportFunction@4")

                這下就天如人愿了:)。如果你想指定導出的順序,或者只將函數導出為序號,沒有 Entryname,這個預處理指示符 (確切地說是連接器) 都能夠實現,看看 MSDN 的語法說明:

            /EXPORT:entryname[,@ordinal[,NONAME]][,DATA]

               @ordinal 指定順序;NONAME 指定只將函數導出為序號;DATA 關鍵字指定導出項為數據項。



            每個編譯程序可以用#pragma指令激活或終止該編譯程序支持的一些編譯功能。例如,對循環優化功能:
            #pragma loop_opt(on) // 激活
            #pragma loop_opt(off) // 終止
            有時,程序中會有些函數會使編譯器發出你熟知而想忽略的警告,如“Parameter xxx is never used in function xxx”,可以這樣:
            #pragma warn —100 // Turn off the warning message for warning #100
            int insert_record(REC *r)
            { /* function body */ }
            #pragma warn +100 // Turn the warning message for warning #100 back on
            函數會產生一條有唯一特征碼100的警告信息,如此可暫時終止該警告。
            每個編譯器對#pragma的實現不同,在一個編譯器中有效在別的編譯器中幾乎無效。可從編譯器的文檔中查看。

            posted @ 2006-02-28 09:46 牽牛散步 閱讀(903) | 評論 (0)編輯 收藏

            2005年12月16日

            基于TCP/IP的多線程通信及其在遠程監控系統中的應用


              傳統的應用程序都是單線程的,即在程序運行期間,由單個線程獨占CPU的控制權,負責執行所有任務。在這種情況下,程序在執行一些比較費時的任務時,就無法及時響應用戶的操作,影響了應用程序的實時性能。在監控系統,特別是遠程監控系統中,應用程序往往不但要及時把監控對象的最新信息反饋給監視客戶(通過圖形顯示),還要處理本地機與遠程機之間的通信以及對控制對象的實時控制等任務,這時 ,僅僅由單個線程來完成所有任務,顯然無法滿足監控系統的實時性要求。在DOS系統下,這些工作可以由中斷來完成。而在Windows NT下,中斷機制對用戶是不透明的。為此,可引進多線程機制,主線程專門負責消息的響應,使程序能夠響應命令和其他事件。輔助線程可以用于完成其他比較費時的工作,如通信、圖形顯示和后臺打印等,這樣就不至于影響主線程的運行。

              1 Windows NT 多線程概述

              Windows NT是一個真正的搶占式多任務操作系統。在 Windows NT中,啟動一個應用程序就是啟動該應用程序的一個實例,即進程。進程由一個或多個線程構成,擁有內存和資源,但自己不能執行自己,而是進程中的線程被調度執行。進程至少要有一個線程,當創建一個進程時,就創建了一個線程,即主線程。主線程可以創建其他輔助線程,由主線程創建的線程又可創建線程。每個線程都可指定優先級,操作系統根據線程的優先級調度線程的執行。

              Windows NT中使用多線程的方法有三種:

              · 使用C多線程庫函數;

              · 使用CreateThread() 等Win32函數;

              · 使用MFC類。

              本文采用第三種方法。在Visual C++5.0 中,MFC應用程序用CWinThread 對象表示線程。基本操作如下:

              · 創建新線程:調用MFC全局函數AfxBeginThread ()創建新線程。AfxBeginThread()啟動新線程并返回控制,然后,新線程和調用AfxBeginThread()的線程同時運行。它的返回值為指向CWinThread對象的指針;

              · 暫停/恢復線程:調用CWinThread類成員函數SuspendThread()暫停線程的運行,調用ResumeThread()成員函數恢復線程的運行;

              · 終止線程:在線程內部可調用全局函數AfxBeginThread()終止線程的運行,否則,線程執行結束后,線程自動從線程函數返回并釋放線程占有的資源。

              2 基于TCP/IP的多線程編程

              TCP/IP是lnternet上廣泛使用的一種協議,可用于異種機之間的互聯。TCP/IP協議本身是非常復雜的,然而在網絡編程中,程序員不必考慮TCP/IP的實現細節,只需利用協議的網絡編程接口Socket(亦稱套接字)即可。在 Windows 中,網絡編程接口是 Windows Socket它包含標準的Berkley Sockets的功能調用的集合,以及為 Windows 所做的一些擴展。TCP/IP協議的應用一般采用客戶/服務器模式,面向連接的應用調用如圖1所示。

              根據上述順序調用函數建立連接后,通信雙方便可交換數據。然而,在調用帶*號的函數時,操作常會阻塞,特別是當套接字工作在同步阻塞模式(Blocking Mode)時。這時,程序無法響應任何消息。為了避免出現這種情況,本文引進輔助線程。在執行含有可能阻塞的函數的任務時,動態創建新的線程,專門處理該任務。主線程把任務交給輔助線程后,不再對輔助線程加以控制與調度。本文分別針對connect()、accept()、receive()、send()等可能阻塞的函數創建了相應的線程,如表1所示。

              多線程編程常常還要考慮線程間的通信。線程間的通信可以采用全局變量、指針參數和文件映射等方式。本文采用指針參數方式。在調用AfxBeginThread()函數時,通過傳遞指針參數的方式在主線程與輔助線程間通信。

              AfxBeginThread()函數的用法如下:

              CWinThread*AfxBeginThread (AFX_THREADPROC pfnThreadproc,

              LPVOID pParam,

              int nPriority=THREAD_PRIORITY_NORMAL,

              UINT nStackSixe=0,

              DWORD dwCreateFlags=0,

              LPSECURITY_ATTRIBUTES pSecurityAttrs=NULL);

              參數pfnThreadProc指定線程函數必須如下定義:

              UINT MyControllingFunction(LPVOID pParam); 

              參數pParam 是調用線程傳遞給線程函數pfThreadProc的參數;

              其他參數一般只需采用缺省值。

              指針參數通信方式就是通過參數pParam在線程間通信的,它可為指向任何數據類型的指針。本文中,定義了一個名叫EXCHANGE_INFO的結構如下:

              typedef struct

              { SOCKET sServerSocket;

              SOCKET *pcCoientSocket;

              SOCKADDR_IN *pClientAddr;

              BOOL *pbConnected;

              unsigned char *pucBuffer;

              int *pnMessageLen;

              } EXCHANGE_INFO;

              在需要通信時,先聲明一個結構變量,再把變量的指針作為pParam參數,調用AfxBeginThread((AFX_THREADPROC) CSocketThread::WaitFor ConnectThread,(LPVOID)& m_Exchangeinfo)函數即可。

              為了利用面向對象技術編程所具有的模塊性強、便于修改、可移植性好等優點,本文還把表1中的線程封裝為父類為CWinThread的自定義類CSocketThread中。還自定義了一個叫CSocketComm的新類,封裝了一些函數,如CreateSocket、ConnectToServer、WaitForClient、ReadMessage、SendMessage等,這些函數屏蔽了面向連接的通信程序的實現細節,如創建、連接、發送和接收等,在這些函數里,動態創建輔助線程。

              下面以CSocketComm類中的等待客戶連接請求的函數WaitForClient()為例,注釋說明多線程編程的具體細節。

              BOOL CSocketComm::WaitForClient

              {

              if(m_bConnected)return( TRUE );

              //配置bind函數的參數即服務器的套接字地址結構

              SOCKADDR_IN Addr;

              memset(&Addr,0,sizeof(SOCKADDR_IN));

              Addr.sin_family=AF_INET;

              ADDR.SIN_port= htonl(m_nPort); 

              Addr.sin_addr.s_addrr = htonl(INADDR_ANY); 

              //將套接字地址結構賦予套接字(綁定),以指定本地半相關

              int nReturnValue;

              nReturnValue =::bind( m_sSserverSocket,( LPSOCKADDR)&Addr,sizeof (SOCKADDR_IN )); 

              if(nReturnValue == SOCKET_ERROR)  returu( FALSE );

              //配置傳給WaitForConnectThread線程函數的參數m_Exchangeinfo

              m_Exchangeinfo.sServerSocket = m_serverSocket;

              m_Exchangeinfo.psClientSocket = &m_sClientSocket;

              m_Exchangeinfo.pClientAddr = &m_lientAddr;

              m_Exchangeinfo.pbConnected = &m_bConnected;

              //以m_Exchangeinfo的指針為參數調用WaitforConnectThread線程等待客戶端連接

              AfxBeginThread((AFX_THREADPROC)CSocketThread::

              WaitForConnectThread,(LPVOID) &m_Exchanginfo); 

              returi( TRUE )

              }

              //等待連接線程

              UINT CSocketThread::WaitForConnectThread(LPVOIDpParam)

              {

              EXCHANGE_INFO*pExchangelnfo=(EXCHANGE_INFO*) pParam;

              int nReturnValue, nClientAddrSize= Sizeof( SOCKADDR_IN);

              //偵聽連接

              nReturnValue=:: listen(pExchangelnfo ->sServerSocket, 1); 

              if( nReturnValue == SOCKET_ERROR )return(0);

              //阻塞調用accept,直至有客戶連接請求

              *pExchangelnfo->psClitentSocket=:: accept(pExchangelnfo->sServerSocket, (LPSOCKADDR) pEchangelnfo ->pClientAddr,&nClientAddrSize); 

              if(( *pExchangelnfo->psClitentSocket)!= INVALID_SOCKET)

              //通過pExchangelnfo的指針在線程間通信

              * pExchangelnfo->pbConnected TRUE;

              return( 0 );



              3 應用實例-高層協議的設計

              在電廠和電站中,為了保證安全工作,保護系統必不可少。保護系統的電源供應通常使用兩種方式。一般情況下,使用交流電系統對保護系統進行供電;當交流電系統出現故障時立即使用后備的蓄電池系統對保護系統進行供電。為了對蓄電池系統進行監控和管理,以保證蓄電池在關鍵時刻能正常工作,設計了在Windows NT環境下具有遠程通訊功能和動態人機界面的智能蓄電池遠程監控系統 。該系統由蓄電池智能管理、充電機控制、母線絕緣在線檢測、聲光報警、系統組態、遠程通信等子系統組成,實現對蓄電池/充電機智能化遠程管理和控制,對整個系統的運行狀態進行實時監控,具有多媒體報警、事件處理、動態數據庫、趨勢畫面和動態畫面顯示、操作提前提醒等功能。系統框圖如圖2所示。在遠程通信模塊中,遠程監控機需把監控客戶的操作命令及時傳給本地機,本地機根據命令控制充電機,使之按照一定的方式工作,而本地機需定時向遠程監控機反饋實時的充電機狀態信息。它們之間的通信是基于TCP/IP的廣域網通信,而且,我們引進了多線程機制以保證系統具有良好的實時性。

              下面以其中的充電機控制系統為例談談如何使用CSocketComm類進行遠程通信。為簡單起見,假定本地機與遠程監控機之間通信的信息僅有下面三種類型:

              ·本地機接收到該命令后,控制充電機按照穩壓模式運行,輸出電壓為電壓給定值;

              ·本地機接收到該命令后,控制充電機按照穩流定時模式運行,輸出電流為電流給定值;

              ·本地機向遠程監控機發送充電機的實時狀態數據(包括輸出電壓、輸出電流、狀態指示和故障類型指示)。

              在基于TCP/IP的面向連接的網絡通信中,客戶與服務器之間傳送的是有序可靠的字節流(Byte Stream),所以程序員有必要在傳輸層TCP上定義自己的高層協議,設計幀結構,將字節流變成有意義的信息。在CSocketComm類中由AssembleMessage()函數把數據組合成一定的幀結構。幀結構為:



              其中@為幀起始標志,#為幀終結標志

              對應的結構定義如下:

            typedef struct

            { int MessageType; //信息類型

            int ChargerNo; //充電機編號

            int DataNo; //數據類型

            float Data; //數據

            } MessageStruct;

              需要通信時,先聲明一個 MessageStruct變量,根據信息內容對各成員變量賦值,傳給 AssembleMessage()函數組合成幀,再調用SendMessage()函數發送給接受方。接受方接到數據后,對數據內容的解釋,是由CsocketComm類中的AnalyzeMessage()函數完成的。AnalyzeMessage()函數返回一個 MessageStruct變量。應用程序就可根據它的各成員變量控制充電機或動態顯示充電機的狀態。

              總之,把多線程機制引進通信,有利于提高應用程序的實時性,充分利用系統資源。對于大型的工程應用來說,不同的線程完成不同的任務,也有利于提高程序的模塊化,便于維護和擴展。本文給出了一種在Windows NT下基于TCP/IP協議的多線程通信的基本方法,根據該方法進行修改和擴充,便可設計出符合具體應用的高質量的多線程通信程序。
            posted @ 2005-12-16 17:27 牽牛散步 閱讀(1863) | 評論 (0)編輯 收藏

            2005年12月15日

              //string 轉換為 char 型
              char* str = strdup ( SendData.strSql.c_str() );
              cout << str << endl;

              char 轉換為 string 型
              char* str = "char 轉換為 string 型";
              SendData.strSql = str;

            //SendData.strSql 為std::string型

            posted @ 2005-12-15 15:27 牽牛散步 閱讀(33382) | 評論 (6)編輯 收藏

            2005年12月13日

            Slice語言

            首先,請大家讀ICE中文手冊中的Slice語言一章。 這一部分除了model(模塊),在 ICE 1.3中文手冊中都有描述

            圖 2.1. ice網絡編程示意圖(服務器端和客戶端采用同種編程語言C++)

            ice網絡編程示意圖(服務器端和客戶端采用同種編程語言C++)

            圖 2.2. ice網絡編程示意圖(服務器端和客戶端采用不同編程語言)

            ice網絡編程示意圖(服務器端和客戶端采用不同編程語言)

            基礎知識

            含有Slice 定義的文件必須以.ice 擴展名結尾,例如, Clock.ice就是一個有效的文件名。編譯器拒絕接受其他擴展名。

            Slice 支持#ifndef、#define、#endif,以及#include 預處理指令。它們的使用方式有嚴格的限制:你只能把#ifndef、#define,以及#endif 指令用于創建雙包括(double-include)塊。例如:

            #ifndef _CLOCK_ICE
            #define _CLOCK_ICE
            // #include 文件 here...
            //定義 here...
            #endif _CLOCK_ICE
            

            我們強烈建議你在所有的Slice 定義中使用雙包括(double-include)塊(所上),防止多次包括同一文件。

            #include 指令只能出現在Slice 源文件的開頭,也就是說,它們必須出現在其他所有Slice 定義的前面。此外,在使用#include 指令時,只允許使用<> 語法來指定文件名,不能使用""。例如:

             #include <File1.ice> // OK
            #include "File2.ice" // 不支持!
            

            你不能把這些預處理指令用于其他目的,也不能使用其他的C++ 預處理指令 (比如用\ 字符來連接行、token 粘貼,以及宏展開,等等)。

            在Slice 定義里,既可以使用C 的、也可以使用C++ 的注釋風格:

            Slice 關鍵字必須小寫。例如, class 和dictionary 都是關鍵字,必須按照所示方式拼寫。這個規則有兩個例外:Object 和LocalObject 也是關鍵字,必須按照所示方式讓首字母大寫。

            標識符以一個字母起頭,后面可以跟任意數目的字母或數字。Slice 標識符被限制在ASCII 字符范圍內,不能包含非英語字母,與C++ 標識符不同, Slice 標識符不能有下劃線。這種限制初看上去顯得很苛刻,但卻是必要的:保留下劃線,各種語言映射就獲得了一個名字空間,不會與合法的Slice 標識符發生沖突。于是,這個名字空間可用于存放從Slice 標識符派生的原生語言標識符,而不用擔心其他合法的Slice 標識符會碰巧與之相同,從而發生沖突 。

            標識符(變量名等等)是大小寫不敏感的,但大小寫的拼寫方式必須保持一致(看了后面的話,再理解一下)。例如,在一個作用域內, TimeOfDay 和TIMEOFDAY 被認為是同一個標識符。但是,Slice 要求你保持大小寫的一致性。在你引入了一個標識符之后,你必須始終一致地拼寫它的大寫和小寫字母;否則,編譯器就會將其視為非法而加以拒絕。這條規則之所以存在,是要讓Slice 既能映射到忽略標識符大小寫的語言,又能映射到把大小寫不同的標識符當作不同標識符的語言。(可以這樣理解,變量名區分大小寫,并且不可以是相同的單詞)

            是關鍵字的標識符:你可以定義在一種或多種實現語言中是關鍵字的Slice 標識符。例如,switch是完全合法的Slice標識符,但也是C++和Java的關鍵字。語言映射定義了一些規則來處理這樣的標識符。要解決這個問題,通常要用一個前綴來使映射后的標識符不再是關鍵字。例如, Slice 標識符switch 被映射到C++ 的_cpp_switch ,以及Java 的_switch。對關鍵字進行處理的規則可能會產生難以閱讀的源碼。像native、throw,或export 這樣的標識符會與C++ 或Java(或兩者)的關鍵字發生沖突。為了讓你和別人生活得更輕松一點,你應該避免使用是實現語言的關鍵字的Slice 標識符。要記住,以后Ice 可能會增加除C++ 和Java 以外的語言映射。盡管期望你總結出所有流行的編程語言的所有關鍵字并不合理,你至少應該盡量避免使用常用的關鍵字。使用像self、import,以及while 這樣的標識符肯定不是好主意。

            轉義的標識符:在關鍵字的前面加上一個反斜線,你可以把Slice 關鍵字用作標識符,例如:

            struct dictionary { // 錯誤!
            // ...
            };
            struct \dictionary { // OK
            // ...
            };
            

            反斜線會改變關鍵字通常的含義;在前面的例子中, \dictionary 被當作標識符dictionary。轉義機制之所以存在,是要讓我們在以后能夠在Slice 中增加關鍵字,同時盡量減少對已有規范的影響:如果某個已經存在的規范碰巧使用了新引入的關鍵字,你只需在新關鍵字前加上反斜線,就能夠修正該規范。注意,從風格上說,你應該避免用Slice 關鍵字做標識符(即使反斜線轉義允許你這么做)。

            保留的標識符:Slice 為Ice 實現保留了標識符Ice 及以Ice (任何大小寫方式)起頭的所有標識符。例如,如果你試圖定義一個名為Icecream 的類型, Slice 編譯器會發出錯誤警告3。以下面任何一種后綴結尾的Slice 標識符也是保留的:Helper、Holder、Prx,以及Ptr。Java 和C++ 語言映射使用了這些后綴,保留它們是為了防止在生成的代碼中發生沖突。

            (注:ICE 1.3的中文手冊上沒有“模塊”這一部分)模塊來組織一組相關的語句是為了解決名字沖突。模塊可以包含所有合法的Slice語句和子模塊。你可以用一些不常用的詞來給最外層的模塊命名,比如公司名、產品名等等。

            module ZeroC {
            
            	module Client {
            	// Definitions here...
            	};
            
            	module Server {
            	// Definitions here...
            	};
            };
            

            Slice要求所有的定義都是模塊的一部分,比如,下面的語句就是非法的。

            interface I { // 錯誤:全局空間中只可以有模塊
            // ...
            };
            

            多個文件可以共享同一個模塊,比如:

            module ZeroC {
            // Definitions here...
            };
            
            //另一個文件中 :
            module ZeroC { // OK, reopened module
            // More definitions here...
            };
            

            把一個大的模塊放到幾個文件中去可以方便編譯(你只需重新編譯被修改的文件,而沒有必要編譯整個模塊)。

            模塊將映射的語言中的相應結構,比如 C++, C#, 和 Visual Basic, Slice的modules被映射為namespaces;java中被映射為package.

            除了少數與特定的程序語言相關的調用之外,ice的絕大部分API(應用程序接口)都是用Slice來定義的 。這樣做的好處是可以用一個ICE API定義文件來支持所有的程序語言。

            [注意] 注意
            為了保證代碼的簡潔,以后文章中提及的Slice定義沒有寫出包含的模塊,你要假定該語句是在一個模塊中。

            表 2.1. Slice的數據類型

            類型 取值范圍 大小(單位:bit)
            bool false or true ≥ 1
            byte -128-127或0-255 ≥ 8
            short 2-15至215-1 ≥ 16
            int 2-31至231-1 ≥ 32
            long 2-63至263-1 ≥ 64
            float IEEE的單精度 ≥ 32 bits
            double IEEE的雙精度 ≥ 64 bits
            string 所有Unicode 字符,除了所有位為零的字符 變長

            用戶定義的類型

            • 枚舉:enum Fruit { Apple, Pear, Orange };

              這個定義引入了一種名為Fruit 的類型,這是一種擁有自己權利的新類型。關于怎樣把順序值(ordinal values)賦給枚舉符的問題, Slice 沒有作出定義。例如,你不能假定,在各種實現語言中,枚舉符Orange 的值都是2。Slice 保證枚舉符的順序值會從左至右遞增,所以在所有實現語言中,Apple 都比Pear 要小。與C++ 不同, Slice 不允許你控制枚舉符的順序值(因為許多實現語言不支持這種特性):

              enum Fruit { Apple = 0, Pear = 7, Orange = 2 }; // 出錯
              

              在實踐中,只要你不在地址空間之間傳送枚舉符的順序值,你就不用管枚舉符使用的值是多少。例如,發送值0 給服務器來表示Apple 可能會造成問題,因為服務器可能沒有用0 表示Apple。相反,你應該就發送值Apple 本身。如果在接收方的地址空間中, Apple 是用另外的順序值表示的, Ice run time 會適當地翻譯這個值。

              與在C++ 里一樣, Slice 枚舉符也會進入圍繞它的名字空間,所以下面的定義是非法的:

              enum Fruit { Apple, Pear, Orange };
              enum ComputerBrands { Apple, IBM, Sun, HP }; // Apple已經被定義!
              

              Slice 不允許定義空的枚舉。

            • 結構

              Slice 支持含有一個或多個有名稱的成員的結構,這些成員可以具有任意類型,包括用戶定義的復雜類型。例如:
              struct TimeOfDay {
              short hour; // 0 - 23
              short minute; // 0 - 59
              short second; // 0 - 59
              };
              
              與在 C++ 里一樣,這個定義引入了一種叫作TimeOfDay 的新類型。結構定義會形成名字空間,所以結構成員的名字只需在圍繞它們的結構里是唯一的。在結構內部,只能出現數據成員定義,這些定義必須使用有名字的類型。例如,你不可能在結構內定義結構:
              struct TwoPoints {
              struct Point { //錯誤!
              short x;
              short y;
              };
              Point coord1;
              Point coord2;
              };
              
              這個規則大體上適用于Slice:類型定義不能嵌套(除了模塊支持嵌套)。其原因是,對于某些目標語言而言,嵌套的類型定義可能會難以實現,而且,即使能夠實現,也會極大地使作用域解析規則復雜化。對于像Slice 這樣的規范語言而言,嵌套的類型定義并無必要——你總能以下面的方式編寫上面的定義(這種方式在風格上也更加整潔):
              struct Point {
              short x;
              short y;
              };
              struct TwoPoints { // Legal (and cleaner!)
              Point coord1;
              Point coord2;
              }
              
            • 序列

              序列是變長的元素向量:

              sequence<Fruit> FruitPlatter;
              

              序列可以是空的——也就是說,它可以不包含元素;它也可以持有任意數量的元素,直到達到你的平臺的內存限制。

              序列包含的元素自身也可以是序列。這種設計使得你能夠創建列表的列表:

              sequence<FruitPlatter> FruitBanquet;
              

              序列可用于構建許多種collection,比如向量、列表、隊列、集合、包(bag),或是樹(次序是否重要要由應用決定;如果無視次序,序列充當的就是集合和包)。

              序列的一種特別的用法已經成了慣用手法,即用序列來表示可選的值。例如,我們可能擁有一個Part 結構,用于記錄小汽車的零件的詳細資料。這個結構可以記錄這樣的資料:零件名稱、描述、重量、價格,以及其他詳細資料。 備件通常都有序列號,我們用一個long 值表示。但有些零件,比如常用的螺絲釘,常常沒有序列號,那么我們在螺絲釘的序列號字段里要放進什么內容?要處理這種情況,有這樣一些選擇:

              • 用一個標記值,比如零,來指示“沒有序列號”的情況。

                這種方法是可行的,只要確實有標記值可用。盡管看起來不大可能有人把零用作零件的序列號,這并非是不可能的。而且,對于其他的值,比如溫度值,在其類型的范圍中的所有值都可能是合法的,因而沒有標記值可用。

              • 把序列號的類型從long 變成string。

                串自己有內建的標記值,也就是空串,所以我們可以用空串來指示.“沒有序列號”的情況。這也是可行的,但卻會讓大多數人感到不快:我們不應該為了得到一個標記值,而把某種事物自然的數據類型變成string

              • 增加一個指示符來指示序列號的內容是否有效.

                struct Part {
                string name;
                string description;
                // ...
                bool serialIsValid; // true if part has serial number
                long serialNumber;
                };
                

                對于大多數人而言,這也讓人討厭,而且最終肯定會讓你遇到麻煩:遲早會有程序員忘記在使用序列號之前檢查它是否有效,從而帶來災難性的后果。

              • 用序列來建立可選字段

                這種技術使用了下面的慣用手法:

                sequence<long> SerialOpt;
                struct Part {
                string name;
                string description;
                // ...
                SerialOpt serialNumber; // optional: zero or one element
                };
                

                按照慣例, Opt 后綴表示這個序列是用來建立可選值的。如果序列是空的,值顯然就不在那里;如果它含有一個元素,這個元素就是那個值。這種方案明顯的缺點是,有人可能會把不止一個元素放入序列。為可選值增加一個專用的Slice 成分可以糾正這個問題。但可選值并非那么常用,不值得為它增加一種專門的語言特性(我們將看到,你還可以用類層次來建立可選字段)。

            • 詞典

              詞典是從鍵類型到值類型的映射。例如:

              struct Employee {
              long number;
              string firstName;
              string lastName;
              };
              dictionary<long, Employee> EmployeeMap;
              

              這個定義創建一種叫作EmployeeMap 的詞典,把雇員號映射到含有雇員詳細資料的結構。你可以自行決定鍵類型(在這個例子中是long 類型的雇員號)是否是值類型(在這個例子中是Employee 結構)的一部分——就Slice 而言,你無需讓鍵成為值的一部分。

              詞典可用于實現稀疏數組,或是具有非整數鍵類型的任何用于查找的數據結構。盡管含有鍵-值對的結構的序列可用于創建同樣的事物,詞典要更為適宜:

              • 詞典明確地表達了設計者的意圖,也就是,提供從值的域(domain)到值的范圍(range)的映射(含有鍵-值對的結構的序列沒有如此明確地表達同樣的意圖)。

              • 在編程語言一級,序列被實現成向量(也可能是列表),也就是說,序列不大適用于內容稀疏的域,而且要定位具有特定值的元素,需要進行線性查找。而詞典被實現成支持高效查找的數據結構(通常是哈希表或紅黑樹),其平均查找時間是O(log n),或者更好。詞典的鍵類型無需為整型。例如,我們可以用下面的定義來翻譯一周每一天的名稱:

                dictionary<string, string> WeekdaysEnglishToGerman;
                

                服務器實現可以用鍵-值對Monday–Montag、Tuesday–Dienstag,等等,對這個映射表進行初始化。

              • 詞典的值類型可以是用戶定義的任何類型。但詞典的鍵類型只能是以下類型之一:

                • 整型(byte、short、int、long、bool,以及枚舉類型)

                • string

                • 元素類型為整型或string 的序列

                • 數據成員的類型只有整型或string 的結構

                復雜的嵌套類型,比如嵌套的結構或詞典,以及浮點類型(float和double),不能用作鍵類型。之所以不允許使用復雜的嵌套類型,是因為這會使詞典的語言映射復雜化;不允許使用浮點類型,是因為浮點值在跨越機器界線時,其表示會發生變化,有可能導致成問題的相等語義。

            • 常量定義與直接量

              Slice 允許你定義常量。常量定義的類型必須是以下類型中的一種:

              • 整型(bool、byte、short、int、long,或枚舉類型)

              • float 或double

              • string

              下面有一些例子:

              const bool AppendByDefault = true;
              const byte LowerNibble = 0x0f;
              const string Advice = "Don't Panic!";
              const short TheAnswer = 42;
              const double PI = 3.1416;
              enum Fruit { Apple, Pear, Orange };
              const Fruit FavoriteFruit = Pear;
              

              直接量(literals)的語法與C++ 和Java 的一樣(有一些小的例外):

              • 布爾常量只能用關鍵字false和true初始化(你不能用0和1來表示false和true)。

              • 和C++ 一樣,你可以用十進制、八進制,或十六進制方式來指定整數直接量。例如:

                const byte TheAnswer = 42;
                const byte TheAnswerInOctal = 052;
                const byte TheAnswerInHex = 0x2A; // or 0x2a
                

                [注意] 注意
                如果你把byte 解釋成數字、而不是位模式,你在不同的語言里可能會得到不同的結果。例如,在C++ 里, byte 映射到char,取決于目標平臺, char 可能是有符號的,也可能是無符號的。
                [注意] 注意
                用于指示長常量和無符號常量的后綴(C++ 使用的l、L、u、U)是非法的:
                const long Wrong = 0u; // Syntax error
                const long WrongToo = 1000000L; // Syntax error
                
                • 整數直接量的值必須落在其常量類型的范圍內,否則編譯器就會發出診斷消息。

                • 浮點直接量使用的是C++語法,除了你不能用l或L后綴來表示擴展的浮點常量;但是, f 和F 是合法的(但會被忽略)。下面是一些例子:

                  const float P1 = -3.14f; // Integer & fraction, with suffix
                  const float P2 = +3.1e-3; // Integer, fraction, and exponent
                  const float P3 = .1; // Fraction part only
                  const float P4 = 1.; // Integer part only
                  const float P5 = .9E5; // Fraction part and exponent
                  const float P6 = 5e2; // Integer part and exponent
                  

                • 浮點直接量必須落在其常量類型(float 或double)的范圍內;否則編譯器會發出診斷警告。

                • 串直接量支持與C++ 相同的轉義序列。下面是一些例子:

                  const string AnOrdinaryString = "Hello World!";
                  const string DoubleQuote = "\"";
                  const string TwoSingleQuotes = "'\'"; // ' and \' are OK
                  const string Newline = "\n";
                  const string CarriageReturn = "\r";
                  const string HorizontalTab = "\t";
                  const string VerticalTab = "\v";
                  const string FormFeed = "\f";
                  const string Alert = "\a";
                  const string Backspace = "\b";
                  const string QuestionMark = "\?";
                  const string Backslash = "\\";
                  70 Slice 語言
                  const string OctalEscape = "\007"; // Same as \a
                  const string HexEscape = "\x07"; // Ditto
                  const string UniversalCharName = "\u03A9"; // Greek Omega
                  和在 C++ 里一樣,相鄰的串直接量會連接起來:
                  const string MSG1 = "Hello World!";
                  const string MSG2 = "Hello" " " "World!"; // Same message
                  /*
                  * Escape sequences are processed before concatenation,
                  * so the string below contains two characters,
                  * '\xa' and 'c'.
                  */
                  const string S = "\xa" "c";
                  

                  [注意] 注意
                  Slice 沒有null 串的概念
                  const string nullString = 0; // Illegal!
                  
                  null 串在Slice 里根本不存在,因此,在Ice 平臺的任何地方它都不能用作合法的串值。這一決定的原因是, null 串在許多編程語言里不存在

            接口、操作,以及異常

            見手冊........抄書好累.........

            posted @ 2005-12-13 12:00 牽牛散步 閱讀(3536) | 評論 (0)編輯 收藏
             

            初讀代碼

            這一節大部分內容整理自ICE中文手冊,在這里我特別感謝馬維達同志的翻譯給我們的學習帶來了方便。

            讀服務端代碼

            文件server.cpp. 

            #include <Ice/Ice.h> 		
            #include "../print.h"
            using namespace std;
            using namespace Demo;
            
            //慣例,用后綴I 表示這個類實現一個接口
            class PrinterI : public Printer {
            public:
            	virtual void printString(const string& s, const Ice::Current&);
            };	 
            /*
            打開print.h,看看PrinterI父類的定義
            
            namespace Demo {
            class Printer : virtual public Ice::Object {
            public:
            
            //純虛函數,不能實例化
            virtual void printString(const std::string&,
            //第二個參數有缺省值,實現中可以不使用
            const Ice::Current&= Ice::Current()) = 0;
            };
            };
            */
            
            void PrinterI::printString(const string& s, const Ice::Current&)
            {
            	cout << s << endl;
            }
            
            int  main(int argc, char* argv[])
            {	
            	//程序的退出時的狀態,就是否成功執行
            	int status = 0; 
            
            	 //來包含Ice run time 的主句柄	(main handle)
            	Ice::CommunicatorPtr ic;
            
            	try {
            	//初始化Ice run time (argc和argv是run time命令參數;
            	//就這個例子而言,服務器不需要任何命令行參數)。
                //initialize 返回一個指向Ice::Communicator對象的智能指針,
            	//這個指針是Ice run time 的主句柄。
            		ic = Ice::initialize(argc, argv);
            	 
            	 //調用Communicator 實例上的createObjectAdapterWithEndpoints,
            	 //創建一個對象適配器(比如:網卡就是一種適配器)。
            	 //參數是"SimplePrinterAdapter" (適配器的名字)
            	 //和"default -p 10000"(用缺省協議(TCP/IP),偵聽端口10000 的請求。)
            	 //顯然,在應用中硬編碼對象標識和端口號,是一種糟糕的做法,
            	 //但它目前很有效;我們將在以后看到在架構上更加合理的做法。
            		Ice::ObjectAdapterPtr adapter
            			= ic->createObjectAdapterWithEndpoints(
            			"SimplePrinterAdapter", "default -p 10000");
            
            	  //服務器端run time 已經初始化,實例化一個PrinterI 對象,
            	  //為我們的Printer 接口創建一個servant(serv 服務+-ant人,背一下單詞)。
            		Ice::ObjectPtr object = new PrinterI;
            
            	  //我們調用適配器的add,告訴它有了一個新的servant ;
            	  //傳給add 的參數是剛才實例化的servant,再加上一個標識符。
            	  //在這里,"SimplePrinter" 串是servant 的名字
            	  //(如果我們有多個打印機,每個打印機都可以有不同的名字,
            	  //更正確的說法是,都有不同的對象標識)。
            		adapter->add(object,
            			Ice::stringToIdentity("SimplePrinter"));
            
            		//調用適配器的activate 方法激活適配器
            		//(適配器一開始是在暫停(holding)狀態創建的;
            		//這種做法在下面這樣的情況下很有用:
            		//我們有多個servant,它們共享同一個適配器,
            		//而在所有servant實例化之前我們不想處理請求)。
            		//一旦適配器被激活,服務器就會開始處理來自客戶的請求。
            		adapter->activate();
            
            		//最后,我們調用waitForShutdown。
            		//這個方法掛起發出調用的線程直到服務器實現終止
            		//——或者是通過發出一個調用關閉run time,
            		ic->waitForShutdown();
            	} 
            	catch (const Ice::Exception& e) {
            			cerr << e << endl;
            		status = 1;
            	} catch (const char* msg) {
            		cerr << msg << endl;
            		status = 1;
            	}
            	if (ic) {
            		try {
            
            		//必須調用Communicator::destroy結束Ice run time。
            		//destroy 會等待任何還在運行的操作調用完成。
            		//此外, destroy 還會確保任何還未完成的線程都得以匯合(joined),
            		//并收回一些操作系統資源,比如文件描述符和內存。
            		//決不要讓你的main 函數不調用destroy 就終止,
            		//否則,后果無法想象。
            			ic->destroy();
            
            		} catch (const Ice::Exception& e) {
            			cerr << e << endl;
            			status = 1;
            		}
            	}
            	return status;
            }
            

            注意,盡管以上的代碼不算少,但它們對所有的服務器都是一樣的。你可以把這些代碼放在一個輔助類里,然后就無需再為它費心了(Ice 提供了這樣的輔助類,叫作Ice::Application,參見 10.3.1 節) 。就實際的應用代碼而言,服務器只有幾行代碼:六行代碼定義PrinterI 類,再加上三2 行代碼實例化一個PrinterI 對象,并向對象適配器注冊它。

            讀客戶端代碼

            文件client.cpp. 

            #include <Ice/Ice.h>
            #include "..\print.h"
            using namespace std;
            using namespace Demo;
            int main(int argc, char* argv[])
            {
            	int status = 0;
            	Ice::CommunicatorPtr ic;
            	try {
            		ic = Ice::initialize(argc, argv);
            
            		//stringToProxy 返回的代理(Proxy)類型是Ice::ObjectPrx,
            		//這種類型位于接口和類的繼承樹的根部(接口的基類)。
            		 Ice::ObjectPrx base 
            		=ic->stringToProxy(	"SimplePrinter:default -p 10000");
            
            		//但要實際要與我們的打印機交談,
            		//我們需要的是Printer 接口、不是Object 接口的代理。
            		//為此,需要調用PrinterPrx::checkedCast 進行向下轉換(向下轉型)。
            		//這個方法會發送一條消息給服務器,
            		//詢問“這是Printer 接口的代理嗎?”
            		//如果回答“是”,就會返回Printer 的一個代理;
            		//如果代理代表的是其他類型的接口,返回一個空代理
            		PrinterPrx printer = PrinterPrx::checkedCast(base);
            		
            		//測試向下轉型是否成功,若不成功,就拋出出錯消息并終止客戶。
            		if (!printer) 	throw "Invalid proxy";
            
            		//現在,我們在我們的地址空間里有了一個激活的代理,
            		//可以調用printString 方法,
            		//把享譽已久的 "Hello World!" 串傳給它。
            		//服務器會在它的終端上打印這個串。
            		printer->printString("Hello World!");
            	}
            	catch (const Ice::Exception& ex) {
            		cerr << ex << endl;
            		status = 1;
            	} catch (const char* msg) {
            		cerr << msg << endl;
            		status = 1;
            	}
            	if (ic)
            		ic->destroy();
            	return status;
            }
            

            如果出現任何錯誤,客戶會打印一條出錯消息。例如,如果我們沒有先啟動服務器就運行客戶,我們會得到:

            Network.cpp:471: Ice::ConnectFailedException:
            connect failed: Connection refused
            

            (由于windows下的命令行窗口在出錯后會一閃就消失,不過我們可以在client.cpp的main函數的return status;之前加上system("PAUSE");然后再在VS2003.net中把client設置為啟動項目,重新編譯,運行。OK,可以看到結果了。)

            posted @ 2005-12-13 11:58 牽牛散步 閱讀(3194) | 評論 (0)編輯 收藏
            僅列出標題  下一頁
             
            久久成人精品| 久久er热视频在这里精品| 亚洲国产二区三区久久| 久久亚洲私人国产精品| 国产成年无码久久久免费| 久久亚洲AV无码精品色午夜| 欧美日韩中文字幕久久久不卡| 久久se这里只有精品| 99热都是精品久久久久久| 久久国产精品免费一区二区三区| 色综合合久久天天综合绕视看| 精品久久777| 久久久精品视频免费观看| 久久久久免费视频| 久久久噜噜噜久久中文字幕色伊伊| 性欧美大战久久久久久久| 97视频久久久| 国产V综合V亚洲欧美久久| 狠狠精品干练久久久无码中文字幕 | 国产成人无码久久久精品一| 99久久99久久精品免费看蜜桃| 久久精品国产半推半就| 久久精品亚洲精品国产欧美| 国产精品久久久久a影院| 日产精品久久久一区二区| 色噜噜狠狠先锋影音久久| 亚洲日本久久久午夜精品| 国内精品久久久久久久97牛牛| 国产女人aaa级久久久级| 久久中文字幕人妻丝袜| 精品亚洲综合久久中文字幕| 性做久久久久久久久老女人| 国产精品免费看久久久| 亚洲婷婷国产精品电影人久久| 久久久久人妻精品一区| 久久午夜福利电影| 成人免费网站久久久| 久久无码国产专区精品| 国产精品久久久99| 久久久久99精品成人片直播| 欧美久久综合九色综合|