當你預期的那一天,也許是你害怕的那一天,終于來到了:從工程師的隊伍里,你被提拔到了軟件項目領導或者團隊領導即
項目經理的位置。
這也許就是你選擇的職業道路,或許你不太情愿,將就嘗試一下。無論在哪種情況下,你都可能缺少工程學科、人員管理以及領導能力的相關教育。這需要更多的領導能力和管理(它們不是一回事),而不能象Dilbert(譯注:著名IT漫畫主角)那樣簡單地和老板對抗了。
當你考慮新的目標時,請考慮下面的活動計劃列表。一次就抓住了每個亮點,這是不可能的。但是這份建議說明可以幫助你將注意力放在可以提高你和你的團隊績效的活動上。
一、建立優先級
作為項目經理,首先要做的、最重要的事是你需要有意識地建立優先級。當你仍陷于繁重的
軟件開發活動中時,你需要一套新的職責。過多的經理新手不能抗拒技術的吸引而陷于此類活動,這將導致項目組的其他人員想要獲得經理的幫助時,卻得不到幫助。
有成效的領導知道他們首要的任務是為其他組員提供服務。這些服務包括訓練和指導、解決問題和沖突、提供資源、建立項目目標和優先級、提供適當的技術指引。
要使每個組員都能清楚的知道,你總是可以幫助他們。我發現將自己定位于為被我監督的人
工作是非常有意義的,而不是相反的。在你所作的事情中,對于組員要求你幫助他們這件事,應該具有非屏蔽中斷的優先級。
第二重要的,是使你的客戶滿意。作為一名經理,沒有直接的能力使客戶滿意,因為你已不再是作為個人提供產品和服務完成這點。相反,你必須建立一種環境,準許你的組員最大程度上滿足客戶的需求。經理提供了強有力的方法,有效地提高客戶的滿意度。
第三重要的,是為你的項目工作。因為也許還有其他許多技術上的項目,或者其他經理的請求幫助,諸如為指導委員會工作。當這些和二個高級別的發生沖突時,都要準備推辭掉。很明顯,使其他經理滿意的事情是你最不重要的事情。
在一個有秩序的組織里,如果你在三個以上的重大環節上獲得了成功,其他的經理都會很激動的。我們并不都能很幸運地工作在一個良好的環境里,但一定要對你任務單上排在最前面的工作任務努力盡到最大的責任。集中精力有效地、快樂地、盡可能地幫助你的組員,不要將精力放在使你上司滿意的上面。
二、分析你的技能差距
除非你已經為新位置做好了準備,否則相對于你當前的領導能力和管理技能,你會感到一些差距。出色的技術背景或許是你被選為領導角色的一個因素,但是你要想干得出色,你需要更多的技能。針對別人的評論和項目,真實地列出你的長處和短處,然后減少差距。軟件人員并不以令人滿意的人際關系技能出名。你會希望增強處理人際關系的經驗:解決沖突、說服以及灌輸想法。你也不得不處理包括招聘、解雇、商談計劃表,以及在你的辦公室里評論某人業績使其傷心落淚等一些事務。
我發現從一堂傾聽技能課開始我的管理職業是非常好的。當作為個體提議人,積極地將我們自己的技術議程提交小組時,我們經常對此感到非常愜意。有效的管理要求更多的合作和善于接受的人際關系方式。要花點時間
學習如何(何時)巧妙地引導自己的自然判斷。傾聽技能課提供了一種交流機制,我已經發現在許多場合下都很有用。
接著,到講臺的另一側,提高你的演講能力。如果你真的不適應公開場合的講話,學習戴爾.卡內基的課會有幫助的。你會發覺,通過這樣的培訓獲得的經驗,以及獲得提高的交流能力,都可以幫助你更好地適應將來的工作。
作為項目領導,為了計劃和跟蹤項目, 以及當需要項目回退而采取修正措施時,你有責任調整其他人的工作。參加
項目管理的培訓課,閱讀一些有關項目和風險管理的書籍和
文章。參加項目管理學會,閱讀其月刊--PM Network。SEI的軟件能力成熟度模型對于軟件項目計劃和項目跟蹤提供了很多有用的建議。建立優先級的能力、控制有效果的會議、清晰的交流,對于你,作為一名經理的績效將會有實質上的影響。
定義“質量”
幾乎每個人都會認真地對待質量問題而且都希望生產出高質量的產品。然而,對于軟件的質量含義,沒有一個統一的定義。傳統上的軟件質量觀點和“足夠好”的軟件觀點有著激烈的爭論。為了幫助小組走向成功,需要花一些時間和你的組員、客戶共同探討質量的含義。 這兩種陣營在思想上經常不會有相同的定義,可以很容易的就不同目的開展工作。關注交付計劃的經理對于想正常地檢查每行代碼的工程師會不耐煩的;認為可靠性非常重要的客戶對一個帶有很少使用但帶有很多bugs的特性的產品是不會滿意的;一個很好的GUI也許會讓用戶厭煩,因為用戶已經熟記了如何有效地使用前一個版本的產品。
為了更好的理解客戶對軟件質量的看法,在Kodak,我的小組曾經邀請了我們的客戶和他們的經理就這個議題在一個開放的論壇展開討論。這個論壇是很有意義的,那些使用我們產品的人有著自己的理解,通過討論,我們可以知道我們制定質量的思路有哪些和他們是不相符的。明白了不同,就可以使你集中精力,照顧客戶的最大利益,而不是使開發人員獲得最大滿意。
軟件質量的傳統描述包括要與說明書一致,滿足客戶的需求,代碼和文檔沒有缺陷。“六個∑質量” (six-sigma quality)這個流行詞,建立了一個非常高的尺度,用于監測失敗的頻率和密度。但它不適用于如快速產品交付,可用性,充足的特性集,已支付價錢的交付意義這樣的質量尺度,。對于我們生產和購買的產品,我們總是熱衷于盡可能涵蓋所有的這些質量特性,然而,妥協總是必須的。
在一個項目的需求階段,我們制定了包括十項質量屬性的一個列表,如效率,協同性,正確性以及宜于學習,我們認為這對于用戶來說是最重要的。我們請客戶關鍵人物代表小組以1到5的尺度評估每項屬性。一旦我們決定了哪些屬性是最重要的,我們就可以設計并實現這些目標。如果你在了解了對于客戶的質量含義并在設計實現質量屬性的過程中沒有麻煩的話,而且客戶對質量屬性表示滿意,那你是很幸運的。在眾多關注的質量說明中,我曾聽到過一個:“客戶回來了,但產品沒有” 。和你的客戶、開發人員一起對每一個產品都確定適當的質量目標。一旦決定了,就給出達到質量目標的明確的最高優先級。以身作則,按很高的質量標準要求你自己的工作。采用這個座右銘:“力求盡善盡美,滿足于優秀。”
三、表彰成績
對你組員成績的表彰和獎勵,是激勵他們的一種很重要的手段。除非你的小組中已經有了一種表彰程序,否則這應是你最重要的事情之一。表彰包括象征性的東西(證書,旅游獎勵)以及實際的東西(電影票,餐館禮品券,兌現獎)。在送贈品時要說一些親切的話語:“感謝你所給予的幫助”或者“祝賀取得了成績”。在表彰和獎勵上花費很少的心思和錢,就可以獲得很多的友好和將來的合作。包括客戶代表,以及為項目成功做出過貢獻的支持人員等等開發組外的人員也可以獲得表彰。
和你的組員討論,了解他們
感興趣的表彰和獎勵的方式。使得無論大小成就的表彰活動成為小組文化的一個標準組成部分。對每位組員對其所作的工作表現出發自內心的興趣也要給與含蓄的表揚,為消除所有影響他們戰斗力的障礙盡你的力量。表彰是展示組員以及小組外的其他人的一種方式――你要知道并感謝他們為小組成功所作的貢獻。
四、學習過去
你的小組在過去承擔的一些項目有可能沒有取得完全的成功。甚至在成功的項目上,我們也能經常認為一些事情我們下次會作得更好。當你進入了新的領導角色,需要花點時間了解早期的項目為什么失敗,并要計劃避免犯同樣的錯誤。對于軟件開發,每位經理花時間處理每種可能要發生的錯誤是非常困難的,學習過去的成功和失敗就是個成功的開始。
可以從過去你們小組承擔的一個沒有經過檢查評估的項目著手,不要管其成功還是失敗,實施項目后的回顧(有時稱作事后調查分析)。你的目標不是判定責任,而是為了在將來項目中作得更好。借此,可以了解什么已經作得很好,什么應該作得更好。在當前每個項目的主要里程碑時,通過集體討論或公平的組織者,用同樣的方式,領導小組用頭腦風暴的方式對其展開分析。
另外,要了解領悟已有的軟件工業的最佳準則。一個好的起點是Steve McConnell的Jolt Award獲獎作品:快速開發(Rapid Development,Microsoft Press, 1996)的第三部分,敘述了27個最佳準則。也要避免McConnell敘述的36個常見的軟件開發錯誤。你的組員也許反對新的工作方式,但是你的角色是作為一名領導,要確保團隊一致連續地使用最佳可用的方法、過程和工具。積極促進組員之間的信息共享,這樣局部單個最好的實踐經驗就能成為每個開發人員的工具箱的一部分。
五、建立改進目標
一旦你對過去的項目建立起了回顧,確立了質量對小組的意義,你就要建立短期以及長期改進的一些目標。目標要盡可能量化,所以你要劃分幾個簡單的階段,標明你是否采取了適當的過程朝著目標前進。
例如,如果你認定由于需求的不穩定導致項目經常延期,你可以建立一個改進需求穩定的目標,在6個月內提高50%。這樣一個目標需要你確切知道每周或每月需求的變化數,清楚他們的出處,采取行動控制那些變更。這可能要求你要改變與那些提交需求改變的人的交流方式。
你的目標和階段是軟件過程改進程序的組成部分,你要使之有序。作為缺乏創造力的官僚主義的最后避難所,輕視“過程”很流行。雖然事實上,每個小組都能找到改進其工作的方式。當然,如果你總是用已有的工作方式工作,你也就不要期望你會得到比以前更好的結果。有兩個強烈的原因要求改進過程:校正問題,防止問題。確保你的改進努力要圍繞著已知的或可預知的可能威脅項目成功的問題。領導你的小組找出當前正在使用的方法的長處和短處,以及項目面臨的風險。
我的小組召開了一次“兩段式頭腦風暴”練習,來確定改進軟件生產力和質量過程的絆腳石。在第一次會議中,參會者在便條上寫出他們關于會議主題的想法,一個便條一個想法。組織者將他們寫在便條上的想法收集上來并分組。最后,我們就會得到一打主要的分類,并將其記錄到活動掛圖上。
第二次會議,相同的參會者在便箋上寫出解決這些障礙的思路,并貼在掛圖的合適位置。進一步細化,歸納出一些詳細的活動,就可以成為我們努力的一部分,清除障礙,幫助組員實現軟件的質量和生產力的目標。
建立可度量和可達到的目標,便于你集中精力實現改進。要使目標具有明顯的優先級,并可周期性地監視過程。記住你的目的是,提高你的項目和公司完成的技術和業務上成功,不要滿足于一些過程改進書籍里提到的期望細節。要把改進的工作視為迷你項目,具有可分發、資源、計劃和有責任的小項目。否則,過程改進活動將總處于比誘人的技術工作低的優先級上。
六、緩慢的開始
這篇文章提供了許多建議,幫助你,一位軟件經理新人,帶領你的小組走向偉大的成功。在日復一日新的工作壓力面前,要努力保持你的頭腦清醒。
在長時間的塑造軟件開發小組的文化和習慣上,你還是個非常重要的角色。你不必一次性都作完,可以選擇跟環境最相關的的幾個開始。作為軟件經理,除了項目要按時按照預算完成外,你要擔負的責任還很多。你還要:領導技術人員,將他們形成一個具有凝聚力的團隊;建立協同團隊工作的環境;鼓勵和獎賞高級軟件工程師的實踐應用;平衡來自客戶、公司,組員和你自己的需求。
ava中的邏輯運算符包括邏輯與&,邏輯或|,邏輯非!邏輯異或^,除此之外還有幾個比較相似的&&短路與,||短路或。
那么這幾個邏輯運算符到底有什么意思呢?下面我們來看看
首先看邏輯與&的用法,只有兩個都為真,結果為真,如果看如下代碼
int a = 20;
int b = 5;
if(a>10&b>10){
System.out.println("true");
}else{
System.out.println("false");
}
輸出false
同樣|,只要有一個為真就是真
if(a>10|b>10){
System.out.println("true");
}else{
System.out.println("false");
}
輸出true
!的作用是取反
boolean a = true;
if(!a){
System.out.println("true");
}
接下來再看看短路與&&和短路或||
int a = 10;int b = 20;
if(a>10&&b++>10){
System.out.println(true)
}
System.out.println(b);
看看b的值是多少呢,這個時候很多人會答錯,b的值最終還是20
在這里由于第一個a>10為false,所以這個表達式為false,這樣就不會執行后面的b++操作了,相當于把b++給短路了,所以叫短路與。同樣短路或的作用是如果第一個為真,那么整個表達就為true,就把第二個表達式短路了。所以叫短路或。
把今天早上我在物聯網研究院參與的作業寶產品需求確認的過程大致敘述一下,為將來即將走上
軟件開發工作崗位的同學揭開
項目管理的神秘面紗。
先說說項目背景吧。因為,該軟件產品還沒有上線,涉及到商業機密,所以我只能大致說明一下,這是一款智能
手機上的本地程序,中小學生可以在手機上提交作業,教師在后臺評判作業,學生可以瀏覽作業的評判結果。
項目組采用的項目管理方法論主要是基于scrum敏捷開發,迭代或沖刺的周期為兩周。因為一些客觀原因,項目的某些方面沒有完全嚴格按照scrum來實施,例如:我們這里沒有明確誰是PO和SM,當然這種情況一般老板就是PO和SM,項目畢竟總要有人負責嘛。
需求確認是每個迭代前的重要項目管理活動,今天早上主要做了這件事情,下面就說說這個過程,至于項目管理的其他方面,以后工作中碰到了,再抽時間整理成文。
早上8點半產品組內部進行了v0.4版本的范圍確定,當然功能點不是用story描述的,所以說不是嚴格的scrum,基本上確認的是一個功能清單。產品組內部確認完成后,9點產品組和開發組的負責人及工程師在會議室就v0.4版本中的功能清單逐條評審。評審的過程由產品組逐條講解功能,開發組的軟件工程師確認每個功能技術實現的可行性和開發工期上的可行性。對v0.4功能范圍達成共識后,產品組會后細化v0.4中的每個功能項,給出界面原型圖和功能描述,提交到redmine上。開發組組長根據redmine上的功能描述,在redmine上創建相應的開發任務指派到開發人員身上,一個沖刺就定義完成了,
測試組根據功能描述準備相應的測試案例。
今天被同事問到一個問題,問題描述如下:
一個
測試類,只有一個帶參構造函數。在帶參構造函數上加@Test,同時加@Parameters注解從
testng.xml中傳遞參數。為保證測試函數在帶參構造函數之后執行,所以測試方法前的@Test加了dependsOnMethods屬性,依賴于帶參構造函數。
重現問題的示例代碼如下:
package com.ibm.testng.test; import org.testng.annotations.Parameters; import org.testng.annotations.Test; public class WebTest { private int stayTime; //Constructor with params @Test @Parameters({"stayTime"}) public WebTest(int stayTime) { System.out.println("Constructor with parameter!"); this.stayTime = stayTime; } @Test(dependsOnMethods="WebTest") public void stayOnServer() { System.out.println("The times staying on server: " + stayTime); } } |
輸出結果:
根據輸出結果可知,錯誤原因是沒有找到stayOnServer()依賴的測試函數WebTest()。可能會疑問,不是有名稱為WebTest()的函數嗎,而且還用@Test注解了,為什么會提示找不到呢?
這個錯誤,跟TestNG的執行原理有關。TestNG啟動之后,先調用構造函數創建所有的測試實例,然后才進行測試。因此,構造函數與測試函數的執行時機不一樣,構造函數在所有測試方法之前先執行,沒有必要再通過@Test的dependsOnMethods屬性使測試函數依賴于構造函數。
. 構造函數沒必要用@Test注解(注解了也不會報錯),但是TestNG不會把它當做測試函數,它也不會和其他測試函數一起執行。可能習慣性地認為帶參構造函數前的@Parameters一定要和@Test一起使用,其實不是這樣的,@Parameters可以放的位置有如下兩種情況:
1. 任何已經被@Test,@Factory或者Configuration annotation(@BeforeXXX/@AfterXXX)注解的函數。
2. 測試類中至多一個構造函數前面。TestNG會調用該構造函數創建測試實例,并從testng.xml中獲得該構造函數需要的參數。
可能你希望使用某個構造函數來創建測試實例,但是TestNG會根據自己的規則選擇構造函數。TestNG選擇構造函數的規則:
1. 通常情況下,會選擇默認無參構造函數或者自己添加的無參構造函數。
2. 如果有帶參構造函數,且被@Parameters注解,就會選擇該帶參構造函數。
3. 如果同時有無參構造函數和帶參構造函數,且帶參構造函數沒有被@Parameters注解,選擇無參構造函數。
4. 如果只有帶參構造函數,但是帶參構造函數沒有被@Parameters注解,執行測試函數時拋出org.testng.TestNGException。
對于帶參構造函數的測試類,使用@Factory注解,不僅可以解決帶參構造函數沒有被@Parameters注解而導致的org.testng.TestNGException,而且還可以充分發揮TestNG參數化測試的優勢。以添加如下@Factory注解的代碼為例:
@Factory public static Object[] create() { System.out.println("Create test objects!"); List<WebTest> objectList = new ArrayList<WebTest>(); for(int i=1; i<4; i++) { objectList.add(new WebTest(i*10)); } return objectList.toArray(); } |
上面代碼會創建3個stayTime分別為10,20,30的測試實例。如果使用@Parameters注解,必須創建3個test分別將10,20,30從testng.xml傳入。因此,@Factory為帶參構造函數的類創建一系列有規律的測試實例提供了便利。
首先
QTP要連接MySql 先確保機器上有MySql的數據源 沒有的話去網上下一個裝上把。
我這里用的是Mysql 5.1的數據源 ,裝完后,在桌面新建一個TXT文檔,然后修改后綴改為udl,就變成了數據鏈接這樣子

,打開它,你會發現《提供程序》內就沒有Mysql這個名字的程序 ,但是在windows的管理工具數據源內能找Mysql 5.1的名字也可以添加,后來找了好久才知道原來Mysql在數據鏈接內是叫 Miceosoft OLE DB Provider for ODBC Drivers 這個名字。
現在找到了名字 接下來就是設置《連接》的屬性,數據源名稱可以隨意填寫,下面的用戶名和密碼就需要MySql的管理人員給權限。當都設置好了,點擊
測試連接,顯示連接成功的話就是能正常啟動了。
上面配置好了以后,我們用記事本的方式打開*.udl,里面有這些內容 我們只需要復制紅線那部分,這部分為鏈接地址
好了,接下來就進入編寫階段了
實現功能
1:讀取
數據庫 USER_ 表內的 SCREEN 和 SEREENNAME 這兩列下含有"test_"字符串的數據,
2:循環寫入一個登陸界面,實現正確登陸
3:驗證點,登陸完畢后主界面會有一個對象內顯示 Welcome test01! ,用描述性編程找到該對象獲取其中我所需要的屬性值進行驗證
Dim conn Dim name Dim suu Dim inn '檢查字段 SystemUtil.Run "http://192.168.0.233/web/guest/home" '這一段就是復制當時記事本里的那一段語句 這段語句有個問題就是 自己要手動往里添加用戶密碼噢 所以我再里面加了一個Password=1111 Mysql="Provider=MSDASQL.1;Persist Security Info=False;User ID=root;Password=1111;Data Source=sqltest" 'select screenName,screenName from user_ where screenName like 'test__'; '這段話表示說 搜索數據庫內 USER_ 表內 screenName,screenName 這2個列里面所有包含 test 的數據 sql="select screenName,greeting from user_ where screenName like 'test__';" Set conn=CreateObject("ADODB.CONNECTION") conn.Open Mysql If conn.State<>0 Then Reporter.ReportEvent micPass,"連接成功","連接成功" else Reporter.ReportEvent mimicFail,"連接失敗","連接失敗" End If Set res=CreateObject("ADODB.Recordset") res.Open sql,conn,1,1 '1,1表示只讀;1,3表示插入數據;2,3表示修改數據 res.MoveFirst'使游標指向第一個記錄 sum="" 'res.EOF表示游標到達最后一行 While Not res.EOF '讀取整個表內的所有數據 ' For i=0 to res.Fields.Count-1'str.Fields.Count表示字段個數 ' sum=sum& res(i) & " " '把整個記錄顯示出來 ' Next |
'獲取數據庫內 screenName 列的每一行數據 sum=res("screenName") '截取字符串內 從左到右4位字符 suu=Left(sum,4) If suu = "test" Then '檢查字段變量 inn=res("greeting") '錄制登陸過程 把賬號密碼做參數化 Browser("測試比對樣品 - 登錄").Page("測試比對樣品 - 登錄").WebEdit("_58_login").Set sum @@ hightlight id_;_Browser("測試比對樣品 - 登錄").Page("測試比對樣品 - 登錄").WebEdit(" 58 login")_;_script infofile_;_ZIP::ssf1.xml_;_ Browser("測試比對樣品 - 登錄").Page("測試比對樣品 - 登錄").WebEdit("_58_password").set"1111" @@ hightlight id_;_Browser("測試比對樣品 - 登錄").Page("測試比對樣品 - 登錄").WebEdit(" 58 password")_;_script infofile_;_ZIP::ssf2.xml_;_ Browser("測試比對樣品 - 登錄").Page("測試比對樣品 - 登錄").WebButton("登錄").Click '這里是使用SPY抓取獲取到對象屬性值,對該對象2個屬性進行描述性編程 對象庫內不要放入被描述的對象 ,然后使用 .GetROProperty方法獲取所需要對象屬性值 '這里抓取了2個對象進行描述是應為2個該對象使用1個對象進行描述時,界面上還有其他對象的屬性是一樣的,所以描述對象時 盡量抓取對象獨有的屬性,當一個屬性描述找不到時,就再增加一個屬性描述 nn=Browser("測試比對樣品 - 稿件管理").Page("測試比對樣品 - 稿件管理").WebElement("innertext:=Welcome "&sum&"!","html tag:=SPAN").GetROProperty("innerhtml") '設置檢查點 If nn=inn Then print "nn="&nn&" inn="&inn Reporter.ReportEvent micPass,"檢查點","PASS" else Reporter.ReportEvent micFail,"檢查點","Ng" End If Browser("測試比對樣品 - 稿件管理").Page("測試比對樣品 - 稿件管理").Link("text:=退出").Click End If 'Print sum & vbCRLF'打印所有查詢的記錄 VBCRLF 換行語句 res.MoveNext'使游標進入下一個 Wend @@ hightlight id_;_Browser("測試比對樣品 - 稿件管理").Page("測試比對樣品 - 稿件管理").Link("退出")_;_script infofile_;_ZIP::ssf6.xml_;_ |
報告顯示結果
如果不檢查,虛擬桌面基礎架構(VDI)可能會顯著增加網絡壓力,從而直接影響應用程序的性能及終端用戶的體驗。但是,如何知道VDI是否獲得了足夠的網絡帶寬呢?以下方法可以確定VDI的網絡性能。
無論您的基準測試軟件提供了何種統計信息,總體性能中最重要的一部分是終端用戶體驗。終端用戶不關心您的VDI系統,他們只要求系統比以前更好更快。
如果網絡流量過載,或者后臺VDI服務器過載,那么應用程序性能就會下降,而終端用戶體驗也會隨之變差。在一些極端情況中,用戶敲擊鍵盤到屏幕顯示字母的延遲時間可能會達到1至2秒鐘。
因此,量化終端用戶體驗的最佳方法之一是對比VDI環境與獨立PC的應用程序加載時間。例如,您可以對比兩個環境的MicrosoftWord啟動時間。用戶可能不會注意到幾秒鐘的差別,但是相差5秒鐘以上,那么您就必須采取措施優化基礎架構性能。
您還可以通過執行一些計算密集型任務來測試終端用戶體驗。例如,如果財務部門定期執行一個復雜報表,那么您可以比較它在虛擬桌面和在獨立PC上的任務執行時間。
無論是否對終端用戶體驗進行基準測試,都一定要考慮網絡負載和延遲的影響。在上午9:00上班時間執行基準測試,得到的性能數據肯定與半夜執行的結果不同。在VDI網絡處于最高負載時執行基準測試,得到的測試結果肯定是最準確的。
測定VDI性能的另一種方法是使用免費工具LoginVSI。這個工具不僅能在部署VDI時建立基準,也能夠在其修改之后,測試它的效果。
VDI網絡問題:發現飽和征兆
VDI環境產生的流量會大于普通非VDI環境。但是,VDI相關協議比其他網絡協議的效率更高,所以一定要檢查網絡中是否出現的飽和征兆。有許多方法可以測試網絡飽和狀態,選擇的方法取決于具體的基礎架構。使用Ping測試VDI服務器的網卡是最簡單的。雖然Ping一般是用于檢查丟包狀態,但是這個工具也能反映響應時間。Ping信息用于判斷網絡延遲時間。而Tracert也有相似的作用。
如果VDI服務器運行的是
Windows系統,那么可以使用性能監視器查看每個網卡的進出流量。然而,使用這種方法需要注意兩個問題。首先,由于數據包大小不一,因而發送和接收數據包的數量不重要。您需要關注的是發送和接收的字節數。其次,一定要記住,如果采集數據的頻率過密,性能監視器可能會對系統性能產生負面影響。
SNMP是另一種測試網絡飽和狀態的方法。許多網絡交換機都內置了SNMP監控功能,可用于檢查網絡運行性能。
無論使用哪一種方法檢查網絡的飽和狀態,都有很多網絡路徑需要分析。首先檢查VDI客戶端與VDI前端服務器(一般為負載均衡器)之間的路徑。其次檢查VDI前端和VDI后端之間的鏈路。由于VDI基礎架構的創建方式不同,這可能并不簡單。最后要檢查的是宿主服務器與存儲架構之間的鏈路。
根據遇到的問題,您可能需要對網絡流量進行優先級劃分,或者升級網絡負載均衡。此外,提高VDI服務器與存儲基礎架構之間的網絡速度也可能會有所幫助。
代理模式(Proxy Pattern)
代理模式是常用的Java 設計模式,它的特征是代理類與委托類有同樣的接口,代理類主要負責為委托類預處理消息、過濾消息、把消息轉發給委托類,以及事后處理消息等。代理類與委托類之間通常會存在關聯關系,一個代理類的對象與一個委托類的對象關聯,代理類的對象本身并不真正實現服務,而是通過調用委托類的對象的相關方法,來提供特定的服務。
如下列子:
package proxy; interface Dao{ public void insert(); } class JdbcDao implements Dao{ public void insert(){ System.out.println("in jdbc insert"); } } class HibernateDao implements Dao{ public void insert(){ System.out.println("in hibernate insert"); } } class ProxyDao implements Dao{ private Dao dao; public ProxyDao(Dao dao){ this.dao=dao; } public void insert() { System.out.println("write log before invoke"); dao.insert(); System.out.println("write log after invoke"); } } public static void main(String[] args) { Dao jdbcDao =new JdbcDao(); Dao proxydao = new ProxyDao(jdbcDao); proxydao.insert(); } } |
運行結果:
write log before invoke
in jdbc insert
write log after invoke
隨著Proxy的流行,Sun把它納入到JDK1.3實現了Java的動態代理。動態代理和普通的代理模式的區別,就是動態代理中的代理類是由 java.lang.reflect.Proxy類在運行期時根據接口定義,采用Java反射功能動態生成的。和 java.lang.reflect.InvocationHandler結合,可以加強現有類的方法實現。如圖2,圖中的自定義Handler實現 InvocationHandler接口,自定義Handler實例化時,將實現類傳入自定義Handler對象。自定義Handler需要實現 invoke方法,該方法可以使用Java反射調用實現類的實現的方法,同時當然可以實現其他功能,例如在調用實現類方法前后加入Log。而Proxy類根據Handler和需要代理的接口動態生成一個接口實現類的對象。當用戶調用這個動態生成的實現類時,實際上是調用了自定義Handler的 invoke方法。
Proxy類提供了創建動態代理類及其實例的靜態方法。
(1)getProxyClass()靜態方法負責創建動態代理類,它的完整定義如下:
public static Class<?> getProxyClass(ClassLoader loader, Class<?>[] interfaces) throws IllegalArgumentException
參數loader 指定動態代理類的類加載器,參數interfaces 指定動態代理類需要實現的所有接口。
(2)newProxyInstance()靜態方法負責創建動態代理類的實例,它的完整定義如下:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler) throws
IllegalArgumentException
參數loader 指定動態代理類的類加載器,參數interfaces 指定動態代理類需要實現的所有接口,參數handler 指定與動態代理類關聯的 InvocationHandler 對象。
以下兩種方式都創建了實現Foo接口的動態代理類的實例:
/**** 方式一 ****/
//創建InvocationHandler對象
InvocationHandler handler = new MyInvocationHandler(...);
//創建動態代理類
Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), new Class[] { Foo.class });
//創建動態代理類的實例
Foo foo = (Foo) proxyClass.getConstructor(new Class[] { InvocationHandler.class }).
newInstance(new Object[] { handler });
/**** 方式二 ****/
//創建InvocationHandler對象
InvocationHandler handler = new MyInvocationHandler(...);
//直接創建動態代理類的實例
Foo foo = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),new Class[] { Foo.class }, handler);
由Proxy類的靜態方法創建的動態代理類具有以下特點:
動態代理類是public、final和非抽象類型的;
動態代理類繼承了java.lang.reflect.Proxy類;
動態代理類的名字以“$Proxy”開頭;
動態代理類實現getProxyClass()和newProxyInstance()方法中參數interfaces指定的所有接口;
Proxy 類的isProxyClass(Class<?> cl)靜態方法可用來判斷參數指定的類是否為動態代理類。只有通過Proxy類創建的類才是動態代理類;
動態代理類都具有一個public 類型的構造方法,該構造方法有一個InvocationHandler 類型的參數。
由Proxy類的靜態方法創建的動態代理類的實例具有以下特點:
1. 假定變量foo 是一個動態代理類的實例,并且這個動態代理類實現了Foo 接口,那么“foo instanceof Foo”的值為true。把變量foo強制轉換為Foo類型是合法的:
(Foo) foo //合法
2.每個動態代理類實例都和一個InvocationHandler 實例關聯。Proxy 類的getInvocationHandler(Object proxy)靜態方法返回與參數proxy指定的代理類實例所關聯的InvocationHandler 對象。
3.假定Foo接口有一個amethod()方法,那么當程序調用動態代理類實例foo的amethod()方法時,該方法會調用與它關聯的InvocationHandler 對象的invoke()方法。
InvocationHandler 接口為方法調用接口,它聲明了負責調用任意一個方法的invoke()方法:
Object invoke(Object proxy,Method method,Object[] args) throws Throwable
參數proxy指定動態代理類實例,參數method指定被調用的方法,參數args 指定向被調用方法傳遞的參數,invoke()方法的返回值表示被調用方法的返回值。
最后看一個例子,該例子模仿spring 的AOP原理。
package proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; class Logic{ public void logic(){ Dao dao = Factory.create(); System.out.println("dynamic proxy's name: "+dao.getClass().getName()); dao.insert(); } } class Factory{ static Dao create(){ Dao dao = new JdbcDao(); MyInvocationHandler hand = new MyInvocationHandler(); return (Dao)hand.get(dao); } } interface Dao{ public void update(); public void insert(); } class JdbcDao implements Dao{ public void update(){ System.out.println("in jdbc update"); } public void insert(){ System.out.println("in jdbc insert"); } } class HibernateDao implements Dao{ public void update(){ System.out.println("in hibernate update"); } public void insert(){ System.out.println("in hibernate insert"); } } class MyInvocationHandler implements InvocationHandler { Object o; public Object get(Object o){ System.out.println("in get method of MyInvocationHandler"); this.o = o; return Proxy.newProxyInstance(o.getClass().getClassLoader(),o.getClass().getInterfaces(),this); } public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable { System.out.println("write log before invoke"); Object result = arg1.invoke(o, arg2); System.out.println("write log after invoke"); return result; } } public class Test { public static void main(String[] args) { Logic l = new Logic(); l.logic(); } } |
運行結果:
in get method of MyInvocationHandler
dynamic proxy's name: proxy.$Proxy0
write log before invoke
in jdbc insert
write log after invoke
結論: JDK的動態代理并不能隨心所欲的代理所有的類。Proxy.newProxyInstance方法的第二個參數只能是接口數組, 也就是Proxy只能代理接口。
Glass Box 是
IBM Security
AppScan Standard Edition(以下簡稱 AppScan)8.5 版本以后引進的一個新的組件,是對 AppScan 的一個比較大的改進。Glass Box 引進了運行時分析的技術,通過部署在服務器端的代理,在探索和
測試階段搜集
Web 應用程序信息,并進行分析,進而反饋給 AppScan,使 AppScan 更有針對性的進行探索和掃描,提高了掃描的精確性,并有利于發現更多的漏洞。
Glass Box 并不僅僅是 AppScan 的一個新的特性,而是代表了一種全新的思想,將動態分析技術與傳統的 AppScan
黑盒測試技術結合起來,對 Web 應用程序進行代碼級別的分析,給出更精確的分析結果,從而更有效的幫助客戶保護好自己的網站。
前言
Glass Box 是 IBM Security AppScan Standard Edition(以下簡稱 AppScan)8.5 版本以后引進的一個新的組件,是對 AppScan 的一個比較大的改進。Glass Box 引進了運行時分析的技術,通過部署在服務器端的代理,在探索和測試階段搜集 Web 應用程序信息,并進行分析,進而反饋給 AppScan,使 AppScan 更有針對性的進行探索和掃描,提高了掃描的精確性,并有利于發現更多的漏洞。Glass Box 并不僅僅是 AppScan 的一個新的特性,而是代表了一種全新的思想,將動態分析技術與傳統的 AppScan 黑盒測試技術結合起來,對 web 應用程序進行代碼級別的分析,給出更精確的分析結果,從而更有效的幫助客戶保護好自己的網站。
本文首先簡單介紹了 AppScan Glass Box 的技術架構,然后介紹了在 AppScan 8.5 中 Glass Box 在
Windows XP 下使用效果。Glass Box 支持 WebSphere、Tomcat、JBoss 等多種應用服務器,本文選擇的 Web 服務器為大家常見的 Tomcat 7.0。
技術架構
Glass Box 可分為客戶端和服務端。客戶端又可分為 GlassAPI 和 Glass Box 引擎服務,GlassAPI 用于和服務器端通信,獲取服務端返回的問題信息;Glass Box 引擎服務基于預定義的驗證規則,對獲取到的服務端進行分析,判斷是否存在漏洞。服務端通過代理程序搜集服務器端信息,包括 Web 應用程序運行時信息(根據預定義的特征匹配規則)、源代碼信息、配置文件信息、
操作系統信息、
數據庫信息和 Web 服務器信息等,并將搜集到的信息返回給客戶端。
圖 1. Glass Box 技術架構圖
配置及使用
打開掃描配置面板,可對 Glass Box 進行配置。點擊 Glass Box 面板中的添加按鈕,可添加 Glass Box 代理程序。
圖 2. Glass Box 代理程序定義
代理程序的用戶名和密碼分別為安裝 Glass Box 時設定的用戶名和密碼,如需要修改密碼,可運行 AgentCredentials.bat <username> <password> ,或直接修改位于 GBootStrap\WEB-INF 目錄下的 users.xml 文件,重啟 Tomcat 后生效。用戶可添加多個 Glass Box 代理程序,但 AppScan 同一時間只能使用其中的一個。添加代理程序成功后,可對 Glass Box 進行設置,選中"在探索階段使用 glass box ",可發現更多的隱藏的 URL;選中"在測試階段使用 glass box",可發現更多的漏洞和提供更詳細的漏洞信息。配置成功后,AppScan 右下角狀態欄將顯示"Glass box 掃描:已啟用"。
Glass Box 配置成功后,需要對 Web 應用程序重新掃描。需要注意的是,由于 Glass Box 對 URL 的解析問題,掃描本地網站需要配置虛擬域名,即起始 URL 不能是“http://localhost/myproject”, 而應該是“http://mysite/myproject”。Glass Box 目前僅支持 Java 項目。本文所選用掃描網站是 IBM AppScan 開發人員提供的 AltoroJ 項目。
通過對配置 Glass Box 前后的掃描結果進行分析,我們分析一下使用 Glass Box 的三個優勢。為方便起見,本文采用默認的掃描配置(新建一個常規掃描,采用默認的掃描策略,對掃描配置的各項參數不做任何修改),并且沒有對結果進行分析,排除誤報的漏洞。每次掃描,結果可能會略有不同;若差別太大,則應該檢查掃描配置信息,查看日志,找出問題所在。
1. 在探索階段通過檢測出代碼中不可見的參數和 cookie 信息,探索隱藏的掃描路徑,提高掃描覆蓋率。
在應用程序中,有一些參數并未暴露給用戶,即對用戶是"不可見"的,傳統的 AppScan 運行在客戶端,并不能夠檢測到這些參數,更無法探索到相關的頁面。 Glass Box 運行于探索階段全過程,預定義一些"感興趣"的方法(如 getParameter、Runtime.getRuntime().exec 等),并時刻檢測這些方法是否運行,并進而探索出其中的參數,再根據這個參數構造掃描路徑。
2. 在測試階段,Glass Box 可增強 AppScan 在各種漏洞類型方面的檢測。
Glass Box 通過搜集服務端信息,可減少誤報率,增強 AppScan 對各種漏洞類型的檢測,主要能夠增強 AppScan 對注入攻擊、不安全的直接對象引用、安全配置錯誤和不安全的加密存儲等漏洞的檢測。通過掃描 AltoroJ 項目可以發現,配置 Glass Box 前,共掃描出了 100 個漏洞;而配置 Glass Box 后,共掃描到了 139 個漏洞;Glass Box 增加了了大約 40% 的漏洞掃描發現數量。下表是按照 OWASP Top 10 漏洞分類方法,對使用 Glass Box 前后的掃描漏洞數量進行的對比。
表 1. 使用 Glass Box 前后發現的漏洞數量對比
有時候,Web 開發人員會屏蔽錯誤信息,比如設置一個錯誤頁面,發生異常時直接跳轉到該頁面,AppScan 無法直接從 response 信息中判斷是否存在漏洞。Glass Box 通過預定義的方法,搜集服務器端的信息,從而判斷是否存在漏洞。
通過掃描我們發現,未使用 Glass Box 前,AppScan 并未檢測出用戶登錄頁面的
SQL 注入漏洞(AppScan 可檢測出各種類型的漏洞,未檢測出該頁面的 SQL 注入漏洞屬于個別情況)。我們來分析一下原因。
AppScan 在測試階段,向登陸頁面發送請求,我們假定 AppScan 將 Username 和 Password 均設置為"'",服務器返回錯誤信息"Syntax error: Encountered "\'" at line 1, column 63. "。由于在 Response 中并未包含 SQL 異常信息,所以 AppScan 無法判斷是否存在 SQL 注入漏洞。
圖 3. 頁面顯示的錯誤信息
但是如果我們寫一個測試代碼,可得知當我們輸入單引號時,服務器端確實報了 SQL 語法錯誤信息,如圖 4 所示。Glass Box 安裝在服務器端,當它發現請求值為 g'[number]b,且包含了單引號的信息請求時,如果出現 SQL 異常錯誤信息,那么 Glass Box 便判斷出該頁面存在 SQL 注入漏洞。
圖 4.SQL 異常信息
3. 在生成報告階段,可提供代碼級的調試信息和修復建議
我們以 AltoroJ 項目的登陸頁面為例,對比一下使用黑盒測試和使用 Glass Box 發現的漏洞的修復過程。AppScan 通過分別在 Username 和 Password 輸入框中輸入"4ppSc4n"和"A' OR '7659'='7659",發現了該頁面存在 SQL 注入漏洞(AppScan 中稱之為"SQL 注入的認證旁路")。我們根據"請求 / 響應"信息可以發現,該漏洞存在于 doLogin 這個 servlet 中,如圖 5 所示。
圖 5.AppScan “請求 / 響應”
我們根據 WEB-IBF/web.xml 中的信息進一步判斷出漏洞存在于 LoginServlet.java 文件中。
清單 1.web.xml 文件中的 LoginServlet 配置
<servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/doLogin</url-pattern> </servlet-mapping> <servlet> <description> </description> <display-name> LoginServlet</display-name> <servlet-name>LoginServlet</servlet-name> <servlet-class> com.ibm.rational.appscan.altoromutual.servlet.LoginServlet </servlet-class> </servlet> |
打開 LoginServlet.java 文件,我們依然很難一眼就判斷出漏洞的準確位置,需要對代碼做進一步的分析。經過分析,找到存在漏洞的語句:
清單 2. LoginServlet.java 文件中存在漏洞的語句
if (!DBUtil.isValidUser(username, password))
然后,我們再找到 DBUtil 類中的 isValidUser 函數,最終找到了存在漏洞的 sql 語句:
清單 3. 存在漏洞的 SQL 語句
ResultSet resultSet = statement.executeQuery("SELECT COUNT(*)FROM PEOPLE WHERE USER_ID
= '"+ user +"' AND PASSWORD='" + password + "'");
/* BAD - user input should always be sanitized */
雖然有時候對于經驗豐富的開發者來說,對于邏輯簡單程序能夠直接定位到某個 java 文件,省去一些步驟,但是大部分情況下,我們都需要一步步的去分析。而對于 Glass Box 發現的漏洞,則可以大大節省開發人員定位代碼漏洞的時間。Glass Box 可報告出漏洞的具體位置信息,如方法名、類名、文件名和行號等信息;并可報告運行時信息。如圖 6 所示。
圖 6. Glass Box 問題信息
總結
黑盒測試技術由于無法獲取應用程序的內部信息,導致掃描覆蓋率偏低,且無法提供詳細的調試信息;而白盒測試技術的代價過于高昂,需要大量的人工成本,且誤報率較高。而 Glass Box 是有別于傳統黑盒測試和白盒測試的一種混合測試技術,將有效解決這一難題,為客戶創造更好的價值。
一、概述
Loadrunner擁有極為豐富的工具箱,供予我們制造出各種奇妙魔法的能力。其中就有此次要討論的socket套接字操作。
二、socket概述
socket是
操作系統中I/O系統的網絡延伸部分,它擴展了操作系統的基本I/O到網絡通信,使進程和機器之間的通信成為可能。如果想完全地理解socket在Loadrunner中如何
工作的,熟悉一些關于它的歷史會很有幫助。
當前常用的socket,最早起源于BSD UNIX類的操作系統。在UNIX系統上,比如BSD,把對網絡的支持加入操作系統,以一種擴展現有文件描述符(后注)結構的方法來實現的。Socket 可以被看成一個標準的文件描述符。在 UNIX 類的平臺上,其中包括open()、read()、write()和close()。很多時間,程序并不需要知道它正在把數據寫進一個文件、終端、或是一個TCP連接。
系統調用被加入并和socket一起工作,而很多現有的系統調用同樣能和socket一起工作。因此,一個socket允許您使用標準的操作系統和其他的計算機,以及您自己機器上的不同進程來通信。
然而,socket的確存在一些不同工作方式。最明顯地就是建立socket的方法。很多文件是通過調用open()函數來打開的,但socket是通過調用socket()函數來建立的,并且還需要另外的調用來連接和激活他們。recv()和send()這兩個系統調用和read()和write()極為相似。
Socket是一套建立在TCP/IP協議上的接口不是一個協議,只要底層實現TCP IP協議,都可以用socket進行通信。
應用層: HTTP FTP SMTP Web
傳輸層: 在兩個應用程序之間提供了邏輯而不是物理的通信基于流的TCP和基于數據包的UDP
文件描述符一般是指一個文件或某個類似文件的實體。內核(kernel)利用文件描述符(file descriptor)來訪問文件。文件描述符是非負整數。打開現存文件或新建文件時,內核會返回一個文件描述符。讀寫文件也需要使用文件描述符來指定待讀寫的文件。
文件描述符在形式上是一個非負整數。實際上,它是一個索引值,指向內核為每一個進程所維護的該進程打開文件的記錄表。當程序打開一個現有文件或者創建一個新文件時,內核向進程返回一個文件描述符。在程序設計中,一些涉及底層的程序編寫往往會圍繞著文件描述符展開。但是文件描述符這一概念往往只適用于UNIX、
Linux這樣的操作系統。
三、SOCKET連接過程
根據連接啟動的方式以及本地套接字要連接的目標,套接字之間的連接過程可以分為三個步驟:服務器監聽,客戶端請求,連接確認。
服務器監聽:是服務器端套接字并不定位具體的客戶端套接字,而是處于等待連接的狀態,實時監控網絡狀態。
客戶端請求:是指由客戶端的套接字提出連接請求,要連接的目標是服務器端的套接字。為此,客戶端的套接字必須首先描述它要連接的服務器的套接字,指出服務器端套接字的地址和端口號,然后就向服務器端套接字提出連接請求。
連接確認:是指當服務器端套接字監聽到或者說接收到客戶端套接字的連接請求,它就響應客戶端套接字的請求,建立一個新的線程,把服務器端套接字的描述發給客戶端,一旦客戶端確認了此描述,連接就建立好了。而服務器端套接字繼續處于監聽狀態,繼續接收其他客戶端套接字的連接請求。
四、開發原理
服務器:使用ServerSocket監聽指定的端口,端口可以隨意指定(由于1024以下的端口通常屬于保留端口,在一些操作系統中不可以隨意使用,所以建議使用大于1024的端口),等待客戶連接請求,客戶連接后,會話產生;在完成會話后,關閉連接。
客戶端:使用Socket對網絡上某一個服務器的某一個端口發出連接請求,一旦連接成功,打開會話;會話完成后,關閉Socket。客戶端不需要指定打開的端口,通常臨時的、動態的分配一個1024以上的端口。
Socket接口是TCP/IP網絡的API,Socket接口定義了許多函數或例程,程序員可以用它們來開發TCP/IP網絡上的應用程序。要學Internet上的TCP/IP網絡編程,必須理解Socket接口。Socket接口設計者最先是將接口放在Unix操作系統里面的。如果了解Unix系統的輸入和輸出的話,就很容易了解Socket了。網絡的Socket數據傳輸是一種特殊的I/O,Socket也是一種文件描述符。Socket也具有一個類似于打開文件的函數調用Socket(),該函數返回一個整型的Socket描述符,隨后的連接建立、數據傳輸等操作都是通過該Socket實現的。
五、Loadrunner中socket相關函數淺析
Loadrunner對于腳本函數有一份幫助文檔。利用好此文檔,其實對于
性能測試所需腳本就已足以。
當我們打開Create/Edit Scripts,并打開腳本錄制頁面時,摁下F1便可打開《HP LoadRunner Online Function Reference》。在這幫助文檔中找到“鍵入關鍵字進行查找”輸入框。利用它查找我們所需的socket函數了。
幾乎所有關于socket的函數,都是以lrs開頭的。
基本操作函數:
lrs_startup 初始化 WinSock DLL lrs_create_socket 初始化套接字 lrs_send 在數據報上(UDP)或者向流套接字(TCP)發送數據 lrs_receive 接收來自數據報或流套接字的數據 lrs_disable_socket 禁用套接字操作 lrs_close_socket 關閉打開的套接字 lrs_cleanup 終止 WinSock DLL 的使用,回收相關資源。VuGen 在 Windows 上使用 Windows 套接字協議支持應用程序的錄制和回放;而在UNIX 平臺上僅支持回放 lrs_accept_connection 接受偵聽套接字連接 lrs_close_socket 關閉打開的套接字 lrs_create_socket 初始化套接字 lrs_disable_socket 禁用套接字操作 lrs_exclude_socket 重播期間排除套接字 lrs_get_socket_attrib 獲取套接字屬性 lrs_get_socket_handler 獲取指定套接字的套接字處理程序 lrs_length_receive 接收來自指定長度的緩沖區的數據 lrs_receive 接收來自套接字的數據 lrs_receive_ex 接收來自數據報或流套接字的數據(具有特定長度) lrs_send 將數據發送到數據報上或流套接字中 lrs_set_receive_option 設置套接字接收選項 lrs_set_socket_handler 設置特定套接字的套接字處理程序 lrs_set_socket_options 設置套接字選項 |
緩沖區函數:
lrs_free_buffer 釋放分配給緩沖區的內存 lrs_get_buffer_by_name 從數據文件中獲取緩沖區及其大小 lrs_get_last_received_buffer 獲取套接字上接收到的最后的緩沖區及其大小 lrs_get_last_received_buffer_size 獲取套接字上接收到的最后一個緩沖區的大小 lrs_get_received_buffer 獲取最后接收到的緩沖區或其一部分 lrs_get_static_buffer 獲取靜態緩沖區或其一部分 lrs_get_user_buffer 獲取套接字的用戶數據的內容 lrs_get_user_buffer_size 獲取套接字的用戶數據的大小 lrs_set_send_buffer 指定要在套接字上發送的緩沖區 |
環境函數:
lrs_cleanup 終止Windows套接字 DLL 的使用
lrs_startup 初始化 Windows 套接字 DLL
關聯語句函數:
lrs_save_param 將靜態或接收到的緩沖區(或緩沖區部分)保存到參數中
lrs_save_param_ex 將用戶、靜態或接收到的緩沖區(或緩沖區部分)保存到參數中
lrs_save_searched_string 在靜態或接收到的緩沖區中搜索出現的字符串,將出現字符串的緩沖區部分保存到參數中
轉換函數:
lrs_ascii_to_ebcdic 將緩沖區數據從 ASCII 格式轉換成 EBCDIC 格式
lrs_decimal_to_hex_string 將十進制整數轉換為十六進制字符串
lrs_ebcdic_to_ascii 將緩沖區數據從 EBCDIC 格式轉換成ASCII 格式
lrs_hex_string_to_int 將十六進制字符串轉換為整數
超時函數:(這一堆函數,是可以對同一個socket生效的)
lrs_set_accept_timeout 為接受套接字設置超時
lrs_set_connect_timeout 為連接到套接字設置超時
lrs_set_recv_timeout 執行lrs_receive命令后,等待服務器返回消息的超時時間,即服務器的響應時間。
lrs_set_recv_timeout2 創建連接成功,接收到服務器返回的消息后,獲取匹配消息的超時時間。lrs_receive接收到數據后,會和預期的數據長度進行比較,如果長度不匹配,它將重新從套接字上讀取數據,直到超時為止。
lrs_set_send_timeout 為發送套接字數據設置超時
六、實戰講解
在此只做簡單的知識普及,便于快速上手編寫socket測試腳本。簡述創建連接,收發協議,關閉連接的過程。
初始化
//存放通信返回報文 char * ActualBuffer=""; //存放返回報文長度,切記附初值 int numberOfResponse = -1; //鏈接是否創建成功,判斷值 int rc = 0; //返回報文是否成功,判斷值 int msgOk=-1; //存放返回報文 char * position=""; //返回報文是否成功標識 char * passMsg="succee"; |
服務器監聽
//--------------創建連接----------------- rc= lrs_create_socket("socket0", "TCP", "LocalHost=0", "RemoteHost=<RemoteHost>", LrsLastArg); if (rc==0){ //判斷連接是否創建成功 lr_output_message("Socket was successfully created "); } else{ lr_output_message("An error occurred while creating the socket, Error Code: %d", rc); } //--------------創建連接----------------- |
收發協議
lrs_send("socket0", "buf0", LrsLastArg); //往“socket0”發送"buf0" lrs_set_receive_option(EndMarker, BinaryStringTerminator, "</html>"); //設置接收協議包選項,注"</html>"以實際定義協議為準,如果不設置次項。執行到lrs_receive的時候,log里面打印Waiting for writable socket 10 //secs, 0 usecs,都需要等待10秒鐘。是這樣的,因為你在data.ws中定義了recv buffer的長度,例如你定義為100,但是socket上的返回buffer長度不 //是100,這時候,loadrunner會嘗試再次去讀取,直到讀到長度為100的buffer才算成功。 lrs_receive("socket0", "buf1","Flags=MSG_PEEK ", LrsLastArg); //將“socket0”中返回的數據存放到“buf1”中 |
參數配置
可能細心的同學已經發現了,buf0與buf1是從哪里來的。其實這倆兄弟是在data.ws中被定義的,如下所示:
;WSRData 2 1
send buf0 5120
"<參數化>"
recv buf1 1024
-1
5120:此數值為socket協議傳輸內容長度,切記嚴格輸入正確長度值。
"<參數化>":為buf0所傳輸內容。相對于loadrunner的http協議參數用{}來說,socket協議參數化采用<>作為定義符。
接收參數判斷
在做了接收之后,我們需要提取“buf1”中的某些關鍵字符作為通信成功標識。
//獲取套接字上接收到的最后的緩沖區及其大小 lrs_get_last_received_buffer("socket0",&ActualBuffer,&numberOfResponse); //查詢返回報文是否成功 position = (char *)strstr(ActualBuffer, passMsg); // strstr has returned the address. Now calculate * the offset from the beginning of str msgOk = (int)(position - ActualBuffer + 1); if(msgOk>0){ lr_end_transaction("核心對私維護", LR_PASS); lr_output_message("本次交易:%s",ActualBuffer); } else{ lr_end_transaction("核心對私維護", LR_FAIL); lr_error_message("本次交易:%s",ActualBuffer); } 關閉連接 //--------------斷開socket-------------- lrs_disable_socket("socket0", DISABLE_SEND_RECV); //--------------關閉socket-------------- lrs_close_socket("socket0"); |
六、總結
簡要描述了利用Loadrunner編寫socket性能測試腳本的過程,如有錯漏,請予以指正。
摘要: 安裝完TestLink的下一步就是配置了,Testlink的配置信息包含很多方面,從日志信息、與其它bug管理工具的連接、自定義產生的文檔、Email信息配置、用戶認證配置、GUI定制、測試執行設置、測試規約、附件、需求支持、混合功能配置等等,在此一并列出。希望對你的TestLink的配置有所幫助,在此也聲明一點,TestLink系統配置是很龐大的,部分信息是從網上搜集的。 1、配置文件概覽 ...
閱讀全文