[ 原創(chuàng)文檔 本文適合高級讀者 已閱讀19451次 ] |
大型作業(yè)答辯:C/S考試系統(tǒng)程序制作詳解 下載源代碼
用Win32 API函數(shù)構(gòu)造主窗體和界面元素,如圖一所示: ![]() 圖一 程序組織結(jié)構(gòu) ![]() 一、完全端口模型(I/O completion ports)是迄今為止最為復(fù)雜的一種I/O模型,假如一個程序需要管理為數(shù)眾多的套接字,那么采用這種模型往往可以達到最佳的系統(tǒng)性能,不幸的是該模型只適用與WIN2000和WINNT操作系統(tǒng),因其設(shè)計的復(fù)雜性,只有在你的應(yīng)用程序需要同時管理數(shù)百乃至上千個套接字的時候,而且希望隨著系統(tǒng)內(nèi)安裝的CPU的數(shù)量增多,應(yīng)用程序的性能也可以線性的提升,才考慮采用“完成端口模型”(WEB服務(wù)器便是這方面的典型例子)。I/O completion ports是唯一適用于高負載服務(wù)器的一個技術(shù),它利用一些線程幫助平衡“I/O請求”所引起的負載,這樣的構(gòu)架特別適合應(yīng)用在SMP系統(tǒng)中產(chǎn)生所謂的“Scalable”服務(wù)器,(Scalable是指能夠籍著增加RAM或磁盤空間,CPU個數(shù)而提升應(yīng)用程序效能的一種系統(tǒng))。 二、完全端口模型的具體實現(xiàn) 為了使用“完成端口模型”,我產(chǎn)生了一堆線程在端口上等待,線程數(shù)量=CPU個數(shù)x2+2,我將每個客戶端產(chǎn)生的文件句柄與I/O completion ports端口相關(guān)聯(lián),建立了這種關(guān)系之后,任何客戶端發(fā)出操作請求,便會導(dǎo)致I/O completion packet被送到“完成端口”去,這個步驟是操作系統(tǒng)完成的,為了回應(yīng)I/O completion packet,我讓I/O completion釋放一個等待中的線程,如果目前沒有線程正在等待,它不會為這個客戶端N產(chǎn)生新的線程, 當(dāng)作用中的線程處理完相應(yīng)客戶端的“overlapped I/O”后,將返回I/O completion端口進行等待,客戶端N這時才能夠被處理,這樣就保證了我的Workers線程總是保持一個穩(wěn)定的數(shù)量(CPU個數(shù)x2+2)。如圖二所示: ![]() 圖二 完全端口線程模型示意圖 三、數(shù)據(jù)庫的操作實現(xiàn) 這部分功能主要是通過WinSocket32 API和ODBC API結(jié)合使用來實現(xiàn)的,服務(wù)端進入監(jiān)聽狀態(tài)后,為每個客戶端提供相應(yīng)線程處理發(fā)過來的指令,通過分析指令,作出以下相應(yīng)的操作: 客戶端發(fā)送的指令(自定義的): login: 登陸校驗 參數(shù):用戶名,科目,密碼 Srecv:ScanTm: 檢查服務(wù)器時間校對試卷修改試卷狀態(tài),拋出計數(shù)值 Srecv:GetSta: 獲取試卷狀態(tài) Srecv:GetRlt: 獲取上次做答 Srecv:GetNum: 獲得試卷相關(guān)信息(總題數(shù),開考時間,結(jié)束時間) Srecv:GetQue: 獲取試卷題目內(nèi)容. Srecv:SaveDt: 保存試卷 Srecv:ChanST: 修改試卷狀態(tài).四、服務(wù)器程序總結(jié) 數(shù)據(jù)庫被單獨存放在一個服務(wù)器中可以保證數(shù)據(jù)安全性,程序會將客戶端的一切操作顯示在窗口中,用戶可以通過觀察窗口,知道所有客戶端的動作。這個程序采用“完成端口”模型,可以滿足大規(guī)模的考試需求。 ![]() 一、窗口完全采用Win32API函數(shù)生成 主要包含一下標(biāo)準(zhǔn)控件: static控件 Edit控件 Button控件 Scroll控件 窗口元素全部采用計算后的相對坐標(biāo)定位,所以800X600和1024X768下均能正常顯示,二、試卷的初始化 考慮到每張試卷的題目數(shù)量都不同,為了節(jié)約內(nèi)存空間,所以我在堆中動態(tài)生成了一個試卷結(jié)構(gòu)體,通過向服務(wù)器程序發(fā)送GetNum:指令來獲得試卷總題數(shù)QuestionNum,然后使用TestPaper=new TestRubric [QuestionNum] /*結(jié)構(gòu)體定義*/ //試卷每道題的結(jié)構(gòu) struct Questions{ BOOL state; char Text [512]; }; struct SelectObject{ BOOL state; char Text[256]; }; struct TestRubric{ struct Questions Tile; struct SelectObject choose [4]; };由于TextOut函數(shù)不支持自動換行,所以換行操作必須由我自己完成.因此我用同樣的方法在堆中創(chuàng)建了一個Screen用作屏幕顯示的結(jié)構(gòu)體 Screen=new Lines[LINES] struct Lines{ int earmark; //用來存儲Button的ID BOOL color1;//置顏色標(biāo)志 BOOL color2;//置顏色標(biāo)志 char Line [512]; };LINES=掃描TestPaper中超過屏幕寬度的行數(shù)+ QuestionNum*5+QuestionNum*3 屏幕寬度=客戶區(qū)的寬/每個文字的寬度/2*2 屏幕高度=客戶區(qū)的高/每個文字的高度 為每一體產(chǎn)生4個互斥的按鈕 按鈕總數(shù)= QuestionNum*4 在堆中生成hWndList數(shù)組保存按鈕handle hWndList=new hWnd [QuestionNum*4] 按鈕ID=題號*10+選項號 Screen.earmark=按鈕ID ![]() 圖三 將TestPaper中的內(nèi)容經(jīng)過換行處理之后Copy到Screen結(jié)構(gòu)中,并設(shè)置好Screen.earmark,Screen.Color1, Screen.Color2。在主窗口消息循環(huán)的WM_PAINT消息中將Screen.Line顯示在窗口中: TextOut(hdc,x,cyhar*i,Screen.Line,strlen(Screen.Line));并檢查Screen.earmark中是否為零,不為零就: ShowWindow(hWndList[Sreen.earmark/10-1] [Screen.earmark%10-1],1); Screen.Color1, Screen.Color2是否為1,如果為1,則改變顏色顯示。
|