• <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>

            依舊的博客

            技術學習

            C++博客 首頁 新隨筆 聯系 聚合 管理
              17 Posts :: 1 Stories :: 2 Comments :: 0 Trackbacks

            #

            1. 冒泡排序

            ?思想:

            1. 從現有元素中取出最大的元素,移到相應的位置,直到所有元素都就位。
            2. 通過比較和交換逐步調整現有序列,最終找出最大元素并使其就位。

            ?設計:

            ? 輸入是待排數組及其長度,輸出排序后的數組。
            ??在冒泡過程中對數組的有序情況進行檢查,在數組已經有序時便結束算法。

            代碼:

            void BubbleSort(int nArray[], int nLength)
            {
            ???? bool bSorted = false;
            ???
            ???? if (nArray == NULL)
            ???????? throw -1;
            ???
            ???? if (nLength < 2)
            ??????? ?return;
            ???
            ??? for (int i = nLength; !bSorted && i > 1; i--)
            ??? {
            ??????? ?bSorted = true;
            ???????
            ???????? for (int j = 1; j < i; j++)
            ??????? {
            ???????????? if (nArray[j] < nArray[j-1])
            ??????????? {
            ???????????????? int n;
            ???????????????? n = nArray[j];
            ???????????????? nArray[j] = nArray[j-1];
            ???????????????? nArray[j-1] = n;
            ???????
            ???????????????? bSorted = false;
            ???????????? }//if
            ???????? }
            ?????}

            }

            2. 雙向冒泡排序

            void BiBubbleSort(int nArray[], int nLength)
            {
            ????int? low, high;
            ?
            ????if (nArray == NULL)
            ???????throw -1;

            ????if (nLength < 2)
            ???????returnt;

            ??? low = 0;
            ????high = nLength - 1;
            ??? while (low < high)
            ?? {
            ???????int t;

            ???????t = low;
            ???????for (int i = low; i < high; i++)
            ?????? {
            ?????????? if (nArray[i] > nArray[i+1])
            ????????? {
            ????????????? int n;
            ????????????? n = nArray[i];
            ????????????? nArray[i] = nArray[i+1];
            ????????????? nArray[i+1] = n;

            ????????????? t = i + 1;
            ????????? }
            ?????? }
            ?????? high = t - 1;

            ????? t = high;
            ???? ?for (int j = high; j > low; j--)
            ????? {
            ????????? if (nArray[j] < nArray[j-1])
            ?????? ?? {
            ???????????? int n;
            ???????????? n = nArray[j];
            ???????????? nArray[j] = nArray[j-1];
            ???????????? nArray[j-1] = n;
            ????????????
            ???????????? t = j - 1;
            ????????? }
            ????? }

            ???? low = t + 1;

            ? }//while

            }

            3. 快速排序

            ?思想:

            ?選一個樞軸元素,把待排序列劃分成兩段,前一段不大于樞軸,?后一段不小于樞軸。如果這兩段分別有序,原序列也隨之有序。通過劃分,一個段的排序可以轉化為兩個子段的排序,即同樣性質但較小規模的問題。當段的長度為1時,本身是有序的,轉化可以終止。

            設計:

            用一個遞歸函數來實現快速排序算法,遞歸終止條件是段的長度小于等于1。
            一次劃分過程設計如下:取段的第一個元素為樞軸,從最后一個元素向前與樞軸比較,發現小于樞軸的元素時,與樞軸交換位置,從第二個元素向后與樞軸比較,這樣兩端是已完成劃分的部分,中間是待劃分的部分,樞軸始終處于中間部分的一端,比較從另一端向該端進行,發現分類不同的元素就同樞軸交換。隨著比較和交換的進行,中間部分不斷收縮(每次長度縮短1),當收縮到長度為1時,劃分終止。

            實現要點:

            遞歸函數的參數是待排序列及前后邊界。
            劃分過程需要用兩個變量記錄中間部分的邊界。

            代碼:

            void QuickSort(int nArray[], int low, int high)
            {
            ???? int pivot = nArray[low];
            ???? int?i = low,j = high;
            ???
            ???? if (high < low)
            ???????????return;???
            ????
            ???? while (i < j)
            ???? {
            ????????? while (i <?j && nArray[j] >= pivot) j--;
            ????????? if (i < j)?
            ?????????????? nArray[i++] = nArray[j];
            ?
            ????????? while (i <?j && nArray[i] <= pivot) i++;
            ????????? if (i < j)?
            ?????????????? nArray[j--] = nArray[i];
            ???? }
            ????
            ???? nArray[i] = pivot;
            ???
            ???? QuickSort(nArray, low,?i - 1);
            ???? QuickSort(nArray,?i + 1, high);
            }

            測試要點:

            1. 遞歸終止條件。必須是high < low,而不能是 high?== low。遞歸的終止是很重要的邊界情況,在實現之前有一個概念上的終止條件,但在實現時處理必須準確。終止條件和遞推方式有關,需要結合實際的遞推方式來確定。
            2. 遞歸的遞推方式。
            3. 分劃的終止條件。分劃過程在i == j時終止,雖然在比較的過程中可能進行交換,但是每次未分劃部分的長度減1,用該長度控制分劃的終止。
            4. 分劃過程中改變方向時的交接。

            算法分析:

            假設原序列有2n個元素,每次分劃把一個段等分成兩段,則經過n級遞歸算法終止,每一級遞歸的比較總數為n, 所以QuickSort()的時間為O(nlog(n)),這是平均情況。當原序列本身有序時,QuickSort()出現最壞情況,時間為O(n2)。

            posted @ 2006-05-09 16:37 依舊的博客 閱讀(363) | 評論 (0)編輯 收藏

            最原始的多線程通信機制是全局變量,但是兩個線程對同一變量的并發操作可能是沖突的,所謂沖突是指一種并發調度不等價于任何串行調度。可以通過特定的系統調用保證線程對全局變量的操作具有原子性。這時全局變量成為一種可用的通信機制,根據通信的需要設計通信的協議,兩個線程分別執行,就可以完成合作。比如輔線程重復某個過程直到主線程通知它終止,可以約定全局變量為特定值時輔線程終止,讓輔線程在循環中檢測全局變量,主線程設置全局變量通知輔線程終止。這種機制的缺點是接收通知的一方必須不斷檢測全局變量,事件機制由此進行了改進。接收方可以在事件上阻塞,等待特定的通知,等待的時間可以設定,如果設為0,則事件退化為全局變量。事件只有兩種狀態,有信號或無信號,這相對全局變量是一個限制。事件實現了單向的消息,這是最基本的機制。在實際通信活動中還有許多同步問題,每一方不但根據特定的消息采取相應的動作,還在收到消息后發出反饋消息。通信是一個交互和持續的過程。臨界段機制就是為此而提供的。臨界段的局限是只有兩種狀態:鎖定和解鎖,在很多的通信中,需要進一步量化的狀態,這就產生了信號量機制。
            posted @ 2006-05-05 00:15 依舊的博客 閱讀(837) | 評論 (0)編輯 收藏

            1. 接口

            接口是一組函數的集合(更一般情況下,是一組函數和變量的集合),對象和客戶(程序的兩個不同部分)可通過它進行通信。接口有特定的內存結構,一個接口指針指向一個虛表(vtbl)指針,虛表是一個函數指針的數組,每項指向一個接口函數。

            接口是概念性的程序元素,它具有繼承和多態性。繼承性是指子接口繼承了基接口的所有函數,子接口可以轉型為基接口。在實現上,子接口的虛表包括了基接口的虛表,子接口的虛表指針可以轉型為基接口的虛表指針。多態性是指一個基接口的不同子接口可以有不同的行為。

            2. COM接口(組件模型對接口的要求)

            COM作為一種二進制組件模型,要求對象和客戶盡可能分離,它們的一切聯系都通過接口進行。一個對象可以有多個接口,那么,客戶在獲得第一個接口指針后,應當可以從一個接口指針查詢下一個接口指針,以保持對象的使用。客戶應當可以通過接口管理對象的生命期,以結束對象的使用。作為一種設計,COM規定從對象的一個接口可以查詢它的所有接口,對象生命期管理的責任分散到每個接口(只要客戶為每個接口進行生命期管理,就可以實現對象的生命期管理)。在實現上,COM將接口查詢和生命期管理的責任集中到一個IUnknown接口,所有接口都從IUnknown派生。COM接口就是從IUnknown派生的接口。

            2. COM的面向對象特征

            COM在二進制上提供了一種軟件結構模型,并且帶有面向對象的特征。

            1. 封裝

              COM對象是有狀態的,數據和操作封裝在一起。COM接口和普通API函數的不同,就在于COM對象是有狀態的。比如一個宇宙飛船對象(實現IMotion接口,IMotion包含void Fly(double dTime)和double GetPosition()函數),讓它飛行一段時間(通過IMotion接口調用Fly()函數)以后它的位置就改變了(在飛行前后調用GetPosition()得到不同結果)。

            2. 多態

              同樣的接口可以由不同的COM對象實現,客戶程序用統一的方法進行處理,卻可以得到不同的結果。接口也可以派生,不同的子接口對基接口的函數有不同的實現。

              在這里解釋一下MFC實現COM對象的機制。一個COM對象可以實現多個接口,而這些接口都是IUnknown的子接口,它們對QueryInterface(),? AddRef(),? Release()各有一份實現代碼,而在同一對象內,這三個函數的內容完全相同,因此可以抽出來,委派給該對象。又由于對任何COM對象,AddRef()和Release()的實現本質上也相同,因此可以進一步,抽取這兩個函數及其操作的數據(m_Ref),放到CCmdTarget中去。QueryInterface()的情況有所不同,它操作的數據是依賴于具體COM對象的接口映射表,可以在把函數放進CCmdTarget的同時,實現一個返回接口映射表的虛函數,QueryInterface()調用此函數獲得具體的接口映射表。

            3. 重用

              COM對象可以用包容和聚合兩種方式重用已有的COM對象。

              聚合方式實現重用比較復雜。

              在實現對象聚合時,要解決的一個主要問題是在接口查詢上對用戶保持透明。客戶從暴露出來的內部對象接口進行查詢,應當查到的是外部對象的接口。那么收到查詢時,內部對象的IUnknown應當去委托外部對象的IUnknown。但是內部對象也可能不被用于聚合,應該有一個正常的IUnknown。這樣可以考慮把內部對象最初收到查詢的IUnknown設成一個代理,它根據聚合與否把查詢請求轉交給外部對象IUnknown或內部對象的正常IUnknown,即內部對象實現兩個IUnknown,作為代理的委托IUnknown和正常的非委托IUnknown。內部對象還要知道外部對象IUnknown,并且能判別自身是否被聚合。可以在創建內部對象時把外部對象IUnknown指針傳給它,不是聚合時傳遞一個空指針,這樣內部對象就得到了足夠信息。

              引用計數的管理也是一樣,內部對象的委托IUnknown區別被聚合與否,調用外部對象IUnknown或自身的非委托IUnknown。

              當然,從外部對象接口要能查到內部對象接口。外部對象需要知道內部對象的IUnknown,以查詢所要暴露給客戶程序的接口。這個IUnknown應當是內部對象的非委托IUnknown。

            posted @ 2006-05-03 21:54 依舊的博客 閱讀(1164) | 評論 (0)編輯 收藏

            如果有一個隨機排列的整數表,怎樣將它排序呢?這是生活中也經常碰到的問題。比如給一組牌排序,我們通常會怎樣做呢?

            不斷從原序列中取出元素來排成一個新序列,在新序列形成的時候保證它有序,這就是插入排序的辦法。插入排序需要有空間存放和操作新序列,這可以在原序列的空間中滿足。插入排序需要大量的比較和移動,量級是O(n*n)。我們也可以每次從現有元素中取出最小元素,這樣新序列排下來自然是有序的,這就是選擇排序的辦法。選擇排序需要O(n*n)的比較,最壞最好情況下都一樣。這是一個缺點,和與之對稱的插入排序比較,選擇排序對原序列的情況缺乏適應性,冒泡排序是對此的改進。冒泡排序也基本使用原序列的空間,每次在現有元素中進行比較以尋找最小元素,并通過交換逐步把它移到相應位置。冒泡排序在原序列的基礎上逐步調整得到新序列,它可以更加適應原序列的情況,在最好情況下時間為O(n)。這三種排序是最基本的,其它方法都是從各種角度對它們進行改進。

            三種基本排序都著眼于從原序列形成新序列的過程。這是最基本的,但還可以把整個過程用分治或漸進的思想來處理。具體的改進方法有多種,希爾排序,快速排序,歸并排序等等,我們現在可以欣賞它們的思想,但是當初每種方法的發現都是重要的成果,需要對排序問題有扎實的認識,也需要創造的靈感。

            希爾排序把原序列分組,每一組進行插入排序,從較小的分組開始逐漸擴大,直到整個序列作為一組。分組元素是間隔選取的,較小的分組排序完成后,整個序列就在一個較大的尺度上有序了,隨著分組的擴大,序列在越來越小的尺度上有序,直到完全有序。希爾排序利用插入排序對樂觀情況的適應性,自頂向下漸進處理,避免了直接在小尺度上進行處理的盲目性。

            歸并排序反映出一種自底向上的分治思想,其時間為O(n*log(n))。

            快速排序采用了自頂向下的分治思想,其做法和歸并排序有某種對稱性。

            基數排序也是自頂向下的分治思想,它從關鍵字本身衡量問題的解決程度。


            posted @ 2006-05-03 17:40 依舊的博客 閱讀(435) | 評論 (0)編輯 收藏

            2NF以上的范式都是對關系上的依賴進行限制,其中最重要的是3NF和BCNF。

            • ?BCNF:所有非平凡依賴都以超鍵為決定子。一個屬性集只有包含了整個的鍵,才能決定集外的屬性。
            • 3NF:其非平凡依賴X->A必須滿足:X是超鍵,或者A是主屬性。3NF比BCNF有所放松,允許含鍵不完全的屬性集決定集外的屬性,但必須是主屬性。
            • 不符合3NF的情況有兩種:
              1. 鍵的真子集決定非主屬性,即非主屬性對鍵的部分依賴;
              2. 既非超鍵也非鍵的真子集決定非主屬性,由此將可證明,存在非主屬性對鍵的傳遞依賴。
              如果一個關系不滿足2但滿足1,稱此關系符合2NF。

            2NF和3NF的涵義是:鍵是關系的標識信息,非主屬性是附屬信息。如果附屬信息對標識信息的依賴不夠緊密,關系的語義單純性就差,從而容易出現各種更新異常。

            如果違反2NF,既存在非主屬性對鍵的部分依賴,會有什么問題?例如關系模式SCGT(S#,C#,G,TN),S#是學生號,C#是課程號,G是成績,TN是任課教師姓名,假設每門課只有一個教師。(S#,C#)是鍵,C#->TN是非主屬性對鍵的部分依賴,因為它的存在會產生三種更新異常:1). 不開課的教師姓名無法插入;2). 一門課的所有學生都退選,則任課教師姓名無法保留;3). 一門課更換教師時,必須對選該課的所有學生進行修改。非主屬性對鍵的部分依賴反映了附屬信息和標識信息的缺乏整體一致性,所以會產生以上問題。

            如果符合2NF,但違反3NF,即存在非主屬性對鍵的傳遞依賴,會有什么問題?例如關系模式SDL(S#,DEPT,LOC),S#是學生號,DEPT是所在系,LOC是系的辦公地,這里S#是鍵,S#->DEPT,DEPT-/>S#,DEPT->LOC,LOC傳遞依賴于S#,因為它的存在會產生三種更新異常:1). 如果一個系新成立尚未招生,則無法插入;2). 如果一個系不再招生,但仍為其他系開課,則現有學生畢業后,系的信息無法保留;3). 一個系更換辦公地時,必須對該系的所有學生進行修改。非主屬性對鍵的傳遞依賴反映了附屬信息和標識信息缺乏直接一致性,所以會產生以上問題。缺乏直接一致不如缺乏整體一致那樣嚴重,所以到了3NF才排除。

            那么BCNF的涵義在哪里呢?

            2NF和3NF對一個關系模式中的非主屬性加以限制,而忽略鍵之間的關系。如果一個主屬性依賴含鍵不完全的屬性組意味著什么呢?可以證明,該依賴涉及不止一個鍵,其決定子有兩種情況,一種是部分鍵,一種是含部分鍵和鍵外的屬性。第一種情況下存在一個鍵之外的屬性對該鍵的部分依賴;第二種情況下,取一個不含前述主屬性的鍵,易知存在該屬性對該鍵的傳遞依賴,即一個鍵外的屬性對該鍵的傳遞依賴,排除這兩種情況就得到BCNF。為什么要這樣做呢?因為有多個鍵的情況下,必須照顧每一個鍵,如果鍵之外的屬性和該鍵不能保持整體和直接的一致,也可能產生更新異常。例如SCZ(S,C,Z),S,C,Z分別表示街道,城市,郵編,關系模式上的依賴集為{SC->Z,Z->C},SC和SZ都是鍵。如果插入一個城市的總郵編,必須借助一個街道,刪除這個街道,城市的總郵編也被刪除,出現這種情況是因為C與SZ鍵缺乏整體一致性。


            參考:

            王能斌《數據庫系統教程》/電子工業出版社

            posted @ 2006-05-02 17:15 依舊的博客 閱讀(312) | 評論 (0)編輯 收藏

            1. 客戶-服務器通信中的基本問題

            客戶和服務器通信是為了使用服務,為此在傳輸機制的基礎上設計協議,通過對通信行為的規范,實現通信的目的,并解決傳輸中的問題。

            傳輸機制通常由下層協議提供,根據不同的通信需要選擇不同的下層協議,這是一個基本的問題。對應用協議來說,可用的傳輸機制有可靠連接的字節流類型和不可靠無連接的數據報類型。

            服務器處理大量客戶的請求,從而并發是服務器的一個基本問題,如何處理這個問題也取決于通信需要。處理方式上,服務器可以是循環的或并發的,并發服務器有多種實現方式(異步I/O,多線程和多進程)。

            一件事情能無重復地連續進行,通常會獲得更好的效率,這要求主體始終知道當前的狀態。一次通信過程的連續性取決于通信雙方,它們都要知道通信進行的狀態。這對客戶一般不成問題,但服務器要和大量客戶通信,不一定能為每個客戶的每次通信保存狀態。如果服務器是有狀態的,那么就更快地計算響應,減少通信的數據量。但是傳輸和客戶的故障使有狀態服務器面臨很大問題,當傳輸不可靠(報文重復,丟失,亂序)時,服務器維護的狀態會和客戶失去一致,一個不斷崩潰重啟的客戶會造成狀態信息不能發揮作用,而維護開銷卻極大增加。

            這就提出了客戶-服務器通信中的三個基本問題,它們的解決方案都取決于實際需要,客戶-服務器通信中有哪些情況的需要呢?

            • 是否要求可靠傳輸;
            • 是否需要服務器進行大量處理。對循環服務器進行分析可以知道,需要大量處理的通信用循環方案可能會丟失請求,用并發方案還可以提高服務器資源利用率,改善性能,只要少量處理的通信則無法忍受開銷大的解決方案。
            • 在局域網還是互聯網環境下,局域網中傳輸很少出錯,互聯網環境則不然;


            通常根據前兩個基本問題把服務器實現分為四種類型,它們的適用范圍如下:

            • 循環無連接服務器,少量處理的通信時,并且在局域網中或不要求可靠傳輸。這種做法主要是為了避免開銷。
            • 循環連接服務器,較少用,主要是循環的方式不夠高效,因為連接有一定開銷,響應時間可能不低。在少量處理并要求可靠性的情況下使用。
            • 并發無連接服務器,很少用,因為要給每個請求開線程,開銷太大。在不要求可靠性的情況下,如果線程開銷遠小于計算響應開銷,或者并發可以讓各請求的I/O并行,或者循環方案會丟失請求時可以考慮。
            • 并發連接服務器,常用。


            2. winsock基本函數的使用

            winsock的基本函數有WSAStartup(),WSACleanup(),socket(),closesocket(),bind(),listen(),accept(), connect(),send()和recv()。

            使用這些函數,客戶端的大概算法是,

            1.  調用WSAStartup()初始化winsock庫。
            2.  調用socket()創建套接字,返回套接字描述符s。
            3.  指定遠程套接字地址sa,對s調用connect(),向sa標識的服務進程請求連接。
            4.  連接成功后,對s調用send()發送請求,調用recv()接收響應,如此反復直到完成任務。
            5.  對s調用closesocket()關閉連接。
            6.  不再發起連接處理新的任務時,調用WSACleanup()釋放winsock庫。

            服務器端的大概算法是,

            1. 調用WSAStartup()初始化winsock庫。
            2. 調用socket()創建套接字,返回套接字描述符s。
            3. 對s調用bind(),將其綁定到本地的套接字sa。
            4. 調用listen(),將s置為被動模式。此時開始偵聽客戶端的連接請求,將其放入一個隊列。
            5. 對s調用accept(),即從請求隊列中取出一項,接受該連接后返回一個新的套接字描述符s',以及對應客戶端的套接字地址sa'。
            6. 對s'調用recv()接收請求,調用send()發送響應,如此反復直到完成任務。
            7. 對s'調用closesocket()關閉該連接。
            8. 重復5到7的過程。
            9. 從8退出后,調用WSACleanup()釋放winsock庫。

            有以下幾點需要進一步說明,

            1). 客戶端調用connect()和服務器端調用accept()成功后將在客戶進程和服務器進程之間建立一個TCP連接。連接兩端的每個套接字描述符都包含一個本地端點地址和一個遠程端點地址。所以在使用連接套接字發送數據時不用指示目的地址。

            2). 多宿主主機的IP地址選擇問題。從上面的算法容易提出這樣的問題,為什么客戶端在使用套接字時不綁定端點地址?通常的主機只有一個IP,但是多宿主主機有多個IP地址,在這種情況下,客戶端為套接字指定的IP可能與實際發送時經過的IP不符,所以允許客戶端不指定套接字地址,而由TCP/IP軟件在實際發送時指定IP,同時選擇一個未用過的端口號,這正是在connect()調用中完成的。那么服務器端就不存在同樣的情況嗎?不是,在它調用bind()時指定一個套接字地址,其端口部分采用應用協議的熟知端口,而IP地址部分有著同樣的問題。為此定義了一個代表統配地址的常量INADDR_ANY,用它指示IP地址部分。實際使用的IP仍然是由TCP/IP軟件分配。

            3). TCP為一個連接的發送端和接收端各維護一個緩沖區。當發送端緩沖區滿的時候,send()調用會阻塞,在接收端緩沖區為空的時候,recv()調用會阻塞。為什么要在通信進程和TCP連接之間維護一個間接層呢?可能是為了在一端有多個進程要使用信道的情況下,在多個進程之間進行信道分配的協調。比如在發送端,信道傳輸數據時send()調用可以繼續執行,多個進程的send()調用同緩沖區打交道,彼此影響不大,因為讀寫緩沖區速度很快,而信道同緩沖區打交道,這時可以對各進程的發送數據進行協調,實現公平的信道分配。另外,在TCP中有滑動窗口概念,是用于流量控制的,前述緩沖區和滑動窗口有什么關系?我現在不太清楚。

            4). 套接字的關閉問題。在客戶機和服務器通過TCP連接完成數據交換后,有一個安全關閉的問題。一方面,服務器不能關閉連接,因為客戶機可能還有請求,另一方面,客戶機雖然知道何時不再請求,但是它不知道服務器的響應何時發送完,因為有些應用協議的響應數據量不確定。為此采用部分關閉的辦法,雖然連接是雙向的,但是允許在一個方向上關閉它,當客戶端不再請求時,可以部分關閉連接,使服務器收到一個信號,如果響應發送完了,服務器就可以關閉連接,此時連接被完全關閉

            3. 套接字接口中的端點地址

            端點地址用來表示通信的進程,是傳輸層協議及其套接字接口中的重要概念。不同的協議族可以用不同方式表示端點地址,一個協議族還可以有多個地址族,每個地址族的地址格式不同。TCP/IP只有一個地址族,它的端點地址包括一個32位IP地址和一個16位端口號。在協議族和地址族的基礎上,套接字接口用更為具體的結構來表示端點地址。

            套接字是一種適用于多個協議族的接口,并允許一個協議族使用多個地址族。TCP/IP協議族及其唯一地址族的標識分別是PF_INET和AF_INET。由于套接字接口的通用性,它提供一個通用的地址結構,其格式為(地址族,該族中的套接字地址)。套接字作為一個接口標準,可以有不同實現,以下我們只討論windows套接字。

            如下定義的sockaddr實現了前述通用地址結構,

            // winsock2.h

            struct sockaddr {
                    u_short sa_family;              /* address family */
                    char    sa_data[14];            /* up to 14 bytes of direct address */
            };

            sockaddr的通用性是相對的,某些地址族不適合這個結構。

            盡管sockaddr適合于TCP/IP協議族,但是winsock還定義了TCP/IP專用的地址格式,

            // winsock2.h

            struct sockaddr_in {
                    short   sin_family;
                    u_short sin_port;
                    struct  in_addr sin_addr;
                    char    sin_zero[8];
            };

            sin_family域取值恒為AF_INET。其中的in_addr結構表示IP地址,定義如下,

            //winsock2.h

            struct in_addr {
                    union {
                            struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
                            struct { u_short s_w1,s_w2; } S_un_w;
                            u_long S_addr;
                    } S_un;
            #define s_addr  S_un.S_addr
                                            /* can be used for most TCP & IP code */
            #define s_host  S_un.S_un_b.s_b2
                                            /* host on imp */
            #define s_net   S_un.S_un_b.s_b1
                                            /* network */
            #define s_imp   S_un.S_un_w.s_w2
                                            /* imp */
            #define s_impno S_un.S_un_b.s_b4
                                            /* imp # */
            #define s_lh    S_un.S_un_b.s_b3
                                            /* logical host */
            };

            為了保證軟件的可移植性與可維護性,訪問TCP/IP的代碼不應使用sockaddr。只使用TCP/IP的應用程序可以只使用sockaddr_in,而永遠不用sockaddr。

            4. winsock程序實例

            《vc6技術內幕》的例程ex34a包括一個web服務器和三個客戶,服務器用winsock實現,一個客戶用winsock,另兩個用wininet。我們以winsock實現的服務器和客戶為例。

            CBlockingSocket對各接口函數進行封裝,使它們的調用可以統一報錯。把錯誤檢查和函數調用一起封裝可以避免每次調用這些函數時都檢錯。為統一報錯,采用了異常機制,在檢出錯誤后拋出異常,然后統一進行異常處理。異常機制使我們可以把錯誤檢查和錯誤處理分開,檢查必須是分散的,但是處理可以適當集中,使代碼簡化。

            CHttpBlockingSocket根據接收http報文的特點對CBlockingSocket進行了擴展。成員函數ReadHttpHeaderLine()可以從TCP連接中按行接收字符(它引入了一個緩沖區,緩沖區中收到的字符構成一行后再輸出。該緩沖區的長度需要能夠容納每一行字符,溢出時報錯。接收輸出行的緩沖區也可能長度不足,這時接收的數據不足一行,在調用ReadHttpHeaderLine()時要注意這一點),ReadHttpResponse()用于接收首部行之后所有的響應,當調用者提供的緩沖區不足時會報錯。緩沖區不足的情況都是在CBlockingSocket::Receive()函數中檢測到的,該函數調用以上層次中的代碼按照正常情況編寫。

            CSockAddr是一個與sockaddr_in同樣用途的類,但是用法更方便。winsock函數使用的端點地址結構是sockaddr,sockaddr_in本身用來代替它,所以CSockAddr需要能夠替代sockaddr。sockaddr可能用在傳值或傳址參數中,CSockAddr必須在邏輯上和存儲上都和sockaddr有等價性,并實現有關強制類型轉換。CSockAddr還實現了和sockaddr, sockaddr_in互相轉換的成員函數,因為一種結構很難在所有情況下都好用,新結構也需要和舊結構保持兼容。

             

            本例中采用服務器關閉套接字的辦法,因為每次連接只處理一個請求。

            參考:

            《用TCP/IP進行網際互聯第三卷(windows套接字版)》/清華出版社

            《vc6技術內幕 5th ed》/希望電子出版社

            posted @ 2006-05-02 12:46 依舊的博客 閱讀(2273) | 評論 (0)編輯 收藏

            軟件中的對象同領域中的概念有著密切的關系。

            我們知道概念是人們在某個領域中實踐經驗的總結,并可能發展為理論。概念是以客觀事物為基礎,但不是對客觀事物的刻板反映。它來自于實踐,所以包含主體因素,這是很重要的。實踐是概念的根本來源,理論上的需要對概念形成也有一些作用。

            軟件中使用的對象類似于領域中使用的概念。《UML與模式應用》中說,面向對象就是按照概念而不是功能進行分解。為什么軟件要使用概念性元素呢?因為人的認識是概念性的,而軟件由人來使用,人來開發。為了提供有良好概念性的用戶接口,軟件本身適宜采用概念性元素。由人來開發的軟件則更需要采用概念性元素,軟件本身和領域實踐都有巨大的復雜性,人不是按照功能性元素來思考的,面向對象可以讓開發人員用概念性元素思考,從而增加對復雜性的適應和控制能力。

            領域概念是對象的重要來源,但是對象也形成于軟件開發的過程。一方面,軟件的使用沒有改變領域實踐的本質,至少沒有完全改變,而概念反映了領域中已經成熟的認識,所以是非常重要的指導和參考。另一方面,領域實踐由軟件進行和由人進行確實非常不同,要求進行新的認識,軟件本身的需要也會影響到對象的形成。

            面向對象的基本特征反映著概念的基本特征。

            1. 封裝:概念不是靜態的,它的屬性和操作不可分。
            2. 通信:概念不是孤立的,概念的屬性可以其他概念構成,也可以在屬性中引用其他概念,概念的操作過程中會用到其它的概念。
            3. 抽象:從具體概念可以進一步產生抽象概念。
            4. 多態:抽象概念有著多樣的具體表現。

            Stroustrup的《C++程序設計語言》中說:

            “類應該用于模擬程序員的和應用的世界里的那些概念。...一個概念不會孤立地存在,它總與一些相關的概念共存,并在與相關概念的相互關系中表現出它的大部分力量。...因為我們要用類表示概念,問題就變成了如何去表示概念之間的關系。然而,我們無法在程序語言里表述任意的關系。即使能這樣做,我們也未必想去做它。我們的類應該定義得比日常概念更窄一些——而且也更精確。”

            我們可以體會這段話的深刻性。

            1. 類用來定義概念。首先,概念不是靜態的,它的屬性和操作不可分,封裝是面向對象的第一個特征。然后,概念不是孤立的,一個概念總是與相關概念共存,最基本的關系有兩對:封裝和通信,抽象和多態。前者反映了分工與合作關系,后者反映了抽象與具體關系。類是面向對象的核心,從探討類的作用出發,就引出了面向對象的四個基本特征。
            2. 類所定義的概念不但來自應用領域,也來自程序員引入的東西。
            3. “我們無法在程序語言里表述任意的關系。即使能這樣做,我們也未必想去做它。”
            posted @ 2006-05-02 12:44 依舊的博客 閱讀(258) | 評論 (0)編輯 收藏

            僅列出標題
            共2頁: 1 2 
            久久久亚洲欧洲日产国码aⅴ | 99精品久久精品一区二区| 欧美日韩精品久久久久| 精品久久8x国产免费观看| 午夜视频久久久久一区| 精品精品国产自在久久高清| 久久九色综合九色99伊人| AV无码久久久久不卡蜜桃| 国产—久久香蕉国产线看观看| 久久男人Av资源网站无码软件| 狠狠狠色丁香婷婷综合久久俺| 久久久久国色AV免费看图片| 久久久久久久亚洲Av无码| 亚洲婷婷国产精品电影人久久| 国产精品99久久不卡| 亚洲成色www久久网站夜月| 久久亚洲AV无码精品色午夜麻豆 | 2021国内久久精品| 国产A级毛片久久久精品毛片| 久久久久久久精品妇女99| 久久综合久久综合亚洲| 久久99这里只有精品国产| 99久久成人国产精品免费| 99久久精品费精品国产| 国产精品日韩深夜福利久久| 久久精品国产亚洲αv忘忧草| 久久久久国色AV免费看图片| 91麻精品国产91久久久久| 99久久人妻无码精品系列| 亚洲一级Av无码毛片久久精品| 久久本道久久综合伊人| 色婷婷综合久久久久中文字幕| av无码久久久久久不卡网站| 久久亚洲AV成人无码电影| 99久久国产综合精品网成人影院 | 伊人久久大香线蕉成人| 中文字幕无码av激情不卡久久| 久久午夜综合久久| 熟妇人妻久久中文字幕| 偷偷做久久久久网站| 亚洲国产精品久久久天堂|