《第二十三章InterNet网路式设计》由会员分享,可在线阅读,更多相关《第二十三章InterNet网路式设计(89页珍藏版)》请在金锄头文库上搜索。
1、第二十三章 InterNet網路程式設計有沒有想過自己寫個Service呢?想不想自己也寫一套發信程式呢?在這一章節裡,我們將介紹socket的觀念以及BCB在Internet上開發應用程式的方法。 1大綱23-1. Socket觀念 23-2. 哪些VCL元件可以使用23-3. 寄信程式 23-4. HTTP 抓網頁程式 23-5. 一些Client端的小程式 23-6. 聊天室 23-7. CGI程式設計 本章習題223-1. Socket觀念 Socket是一種API,他介於網路應用程式跟通訊協定之間,他就像是一般的程式中提供的函式一樣,只不過Socket是專用於網路程式之中。所以說,S
2、ocket也屬於System call,讓使用者可以在程式中直接呼叫,就好像在C/C+語言中你要輸入就需要使用scanf、cin或是輸出時會用到printf、cout等等。 雖然說使用Socket就像使用一般的函式一樣簡單,但是還是必須要具備一些基本的通訊協定概念,不然什麼時候要呼叫socket()、bind()等函式就一頭霧水了,只是我們只需要了解,不需要知道怎麼寫出這些API。 323-1. Socket觀念 Socket最早是出現在BSD系列的Unix主機中,所以現在大家在學習Socket的過程中,最常聽到的就是Berkeley Socket。當然現在我們是在Windows上寫Socke
3、t的程式,所以我們使用的Socket就被稱做Win Socket。會叫做Berkeley的原因是BSD中的B就是Berkeley。Win Socket跟Berkeley Socket有什麼不同? 除了作業系統(開發環境)的不同外,其餘皆大同小異,因為大家通常都會遵循著Berkeley Socket的標準來開發整個Socket的API。 423-1. Socket觀念 Socket的定義網路連線的兩端之間,為了要互相聯繫以及溝通所用的介面就稱做Socket。這兩端無論是不是在LAN內都可以。 在Unix下要寫網路程式真的需要對整個Socket以及通訊協定有一定的熟悉度,但是現在BCB打破了如此的
4、迷失。BCB內部提供了Server/Client Socket供Server端以及Client端使用,我們不需要自己慢慢寫程式碼來建立連線,你只要設定好Server/Client Socket的一些屬性,BCB就會幫你完成一堆瑣碎的事情。我們所要考慮的只剩下該如何設計整個網路程式的主體。523-2. 哪些VCL元件可以使用在BCB6中撰寫有關網路的程式,會有許多的VCL元件可供我們使用。例如Webservices、InternetExpress、Internet、WebSnap、FastNet、Indy Clients、以及Indy Servers等等,都是發展網路相關程式的元件。在這一章裡,
5、我們主要要介紹的元件都分布在Internet和FastNet這兩個Page中,包括Client/Server Socket或是其他已經包裝好的VCL元件都可以找的到。 623-2. 哪些VCL元件可以使用InterNet中比較常用到的元件ClientSocketClient端的TCP Socket API。可以利用這個元件讓我們使用TCP通訊協定開發Client端的網路應用程式ServerSocketServer端的TCP Socket API。可以利用這個元件讓我們使用TCP通訊協定開發Server端的網路應用程式FastNet中比較常用到的元件NMDayTime可以傳回Server端的日期
6、以及時間NMMsg 傳送一般的文字訊息 723-2. 哪些VCL元件可以使用FastNet中比較常用到的元件NMMSGServ接收一般的文字訊息 NMEcho 傳送或是接收訊息,和NMMsg以及NMMSGServ有部分相似 NMFTP 設計FTP Client端的VCL元件,主要用來檔案傳輸 NMHTTP 利用HTTP通訊協定取得HTML的文件 NMNNTP 從新聞伺服器接收文章,或是傳送文章到新聞伺服器 823-2. 哪些VCL元件可以使用FastNet中比較常用到的元件NMStrm 傳送資料流 NMStrmServ 接收NMStrm送過來的資料流 NMPOP3 使用POP3通訊協定來接收或
7、是傳送信件 NMSMTP使用SMTP通訊協定來傳送郵件 NMTime 可以傳回Server端的時間 923-2. 哪些VCL元件可以使用FastNet中比較常用到的元件NMUDP 傳輸的通訊協定改用UDP Protocol NMURL 將URL編碼/解碼成一般的文字字串 NMUUProcess 使用MIME或是UU En/Decode來編碼/解碼 NMFinger 獲得Server端使用者的訊息 1023-3. 寄信程式在這一小節我們將寫一個小小的寄信程式,讓使用者可以直接在電腦上寄出信件。這個程式可以說是相當的簡單,看起來似乎不太像是網路程式,其實最主要是因為這些底層的程式碼BCB都幫我們包
8、裝好了,所以才不需要寫太多程式碼。這個程式我們將會放在範例23-1中。 1123-3. 寄信程式範例23-1:寄信程式的開發範例說明範例23-1主要是希望可以利用BCB強大又好用的VCL元件快速的開發出一個寄信程式出來,在這個範例中,我們主要使用了FastNet中的NMSMTP這個VCL元件來寄信。整個開發過程我們完全不需要知道任何跟網路相關的知識,我們只需要知道NMSMTP中的各個屬性該填入什麼樣的值即可! 範例23-1的設計畫面 用到LabeledEdit、Memo、ListBox、BitBtn、StatusBar、NMSMTP、以及OpenDialog這七種VCL元件1223-3. 寄信
9、程式範例23-1:寄信程式的開發設定OpenDialog 將OpenDialog中的ofAllowMultiSelect這個屬性的值改成true,讓使用者在選擇附加檔案的時候可以多重選擇。也將Filter的值設定成All File|*,也就是預設讀取所有的檔案。 1323-3. 寄信程式範例23-1:寄信程式的開發功能的需求選擇附加檔案預計要做到選了幾個檔案就必須要全部填入ListBox中,若是要取消則是選取ListBox的內容後按下Delete按鈕,這樣子可以方便使用這套Mail Sender來寄送附帶檔案。 有關寄送信件的這段程式碼中,會先判斷是不是有些必要的值沒有被輸入,如果有缺少某些值
10、,程式會自動判斷出缺少哪些值,並要求重新輸入如果輸入的值都沒有缺少,就會馬上進入寄信的程式。在寄信的一開始我們要先Connect上Mail Server,連上了之後,我們才繼續將一些資訊寫入NMSMTP中,等這些事情都做完了,我們就可以執行Send這個函式將這封Mail利用所指定的Server寄出。 1423-3. 寄信程式範例23-1:主要程式碼(main.cpp)傳送郵件部分程式碼/-/ 傳送郵件/-void _fastcall TForm1:BitBtn1Click(TObject *Sender) /宣告一個AnsiString用來放置要給使用者看的錯誤訊息 AnsiString ms
11、g; /判斷是不是有必要的欄位沒有填寫 if (LabeledEdit1-Text != “”) & (LabeledEdit2-Text != ) & (LabeledEdit3-Text != ) & (LabeledEdit4-Text != ) & (LabeledEdit5-Text != ) /將各個欄位的資料全部寫入變數中,這樣比較方便在程式中使用 AnsiString Server = LabeledEdit1-Text; int Port = LabeledEdit2-Text.ToInt(); AnsiString User = LabeledEdit3-Text; Ans
12、iString Subject = LabeledEdit4-Text; AnsiString To = LabeledEdit5-Text; 1523-3. 寄信程式範例23-1:主要程式碼(main.cpp)傳送郵件部分程式碼 /將Server Side相關屬性設定好 NMSMTP1-Host = Server; /填入 E-mail Server 的位址 NMSMTP1-Port = Port; /填入E-mail Server 的 Port, 一般都是使用25 NMSMTP1-UserID = User; /填入User Name NMSMTP1-Connect(); /與Server
13、連線 /將各個資料填入NMSMTP的PostMessage中 NMSMTP1-PostMessage-FromAddress = User + + Server; /填入寄信者的e-mail address NMSMTP1-PostMessage-FromName = User; /填入寄件者的姓名 NMSMTP1-PostMessage-Subject = Subject; /填入信件標題 NMSMTP1-PostMessage-ToAddress-Add(To); /填入收信人的E-mail Address /填入副本收件者的E-mail Address NMSMTP1-PostMessa
14、ge-ToCarbonCopy-Add(LabeledEdit6-Text); /填入密件副本收件者的E-mail Address NMSMTP1-PostMessage-ToBlindCarbonCopy-Add(LabeledEdit7-Text); NMSMTP1-PostMessage-Body-Assign(Memo1-Lines); /填入信件內容 NMSMTP1-SendMail(); /將信送出1623-3. 寄信程式範例23-1:主要程式碼(main.cpp)傳送郵件部分程式碼 /將信送出後.把輸入框內的資料清空 LabeledEdit1-Text = ; LabeledEd
15、it2-Text = 25; LabeledEdit3-Text = ; LabeledEdit4-Text = ; LabeledEdit5-Text = ; LabeledEdit6-Text = ; LabeledEdit7-Text = ; ListBox1-Clear(); Memo1-Clear(); else /如果在檢查的時後發現有重要的資訊沒有填寫,就會依照沒有填寫的項目一一警告使用者要填入 /在這邊會警告的是第一個沒填入的,並不是只要沒填就出現警告 /如果要做到只要填寫錯誤就出現錯誤訊息,只要把所有的else if拿掉改用if即可 if (LabeledEdit1-Text
16、 = ) msg = Please inpit the server host!n; MessageBox(GetActiveWindow(), msg.c_str(), NULL, MB_OK|MB_ICONERROR); LabeledEdit1-SetFocus(); else if (LabeledEdit2-Text = ) msg = Please inpit the server port!n; MessageBox(GetActiveWindow(), msg.c_str(), NULL, MB_OK|MB_ICONERROR); LabeledEdit2-SetFocus()
17、; 1723-3. 寄信程式範例23-1:主要程式碼(main.cpp)傳送郵件部分程式碼 else if (LabeledEdit3-Text = ) msg = Please input the user id!n; MessageBox(GetActiveWindow(), msg.c_str(), NULL, MB_OK|MB_ICONERROR); LabeledEdit3-SetFocus(); else if (LabeledEdit4-Text = ) msg = Please input the mail subject!n; MessageBox(GetActiveWind
18、ow(), msg.c_str(), NULL, MB_OK|MB_ICONERROR); LabeledEdit4-SetFocus(); else if (LabeledEdit5-Text = ) msg = Please input the mail receiver!n; MessageBox(GetActiveWindow(), msg.c_str(), NULL, MB_OK|MB_ICONERROR); LabeledEdit5-SetFocus(); 1823-3. 寄信程式範例23-1:主要程式碼(main.cpp)新增郵件附件檔部分程式碼/-/ 新增郵件附件檔/-void
19、 _fastcall TForm1:BitBtn2Click(TObject *Sender) if (OpenDialog1-Execute() /將我們選取的郵件附件檔的完整檔名加到PostMessage中 NMSMTP1-PostMessage-Attachments-AddStrings(OpenDialog1-Files); /將我們選取的檔案的檔名加到List中 for (int i = 0 ; i Files-Count ; i+) ListBox1-Items-Add(ExtractFileName(OpenDialog1-Files-Stringsi); 1923-3. 寄信
20、程式範例23-1:主要程式碼(main.cpp)刪除郵件附件檔部分程式碼/-/刪除郵件附件檔/-void _fastcall TForm1:BitBtn3Click(TObject *Sender) if (ListBox1-SelectedListBox1-ItemIndex) /將選取的郵件附件檔移除 /在這邊除了要從PostMessage中移除外.也要從ListBox中移除 /移除在這邊都是使用Index的方式. /因為我們在新增附件檔的時候.都是採用append的方式 /所以不管是在PostMessage或是在ListBox中的Index都是一樣的 /並不會有順序亂掉的問題.所以才可以
21、使用這樣的code. /如果要將程式寫的嚴謹一點.加入check file name的判斷即可 NMSMTP1-PostMessage-Attachments-Delete(ListBox1-ItemIndex); ListBox1-Items-Delete(ListBox1-ItemIndex); 2023-3. 寄信程式範例23-1:執行結果一個簡單的寄信程式就經完成了,簡單吧!不過,不知道大家有沒有發現一些事情?如果Server突然連不上去怎麼辦?如果Server主機的位址被亂填怎麼辦?如果附加檔名不存在怎麼辦?如果。有著太多不確定的問題在這個程式中了,那我們該如何解決呢?我們將在範例2
22、3-2中告訴各位這些不確定性的問題該如何解決。範例23-2主要是由範例23-1改過來的,根據範例23-1中的一些小缺點作一些改進。 2123-3. 寄信程式範例23-2:寄信程式加強版的開發範例說明範例23-2主要是修正了範例23-1的一些缺點,包括了連線上的問題,附帶檔的問題,以及一些我們必須要自己handle的問題我們都在範例23-2中一併解決。 在這個範例中最大的差別就是在多了Connect和Disconnect這兩個按鈕。為什麼要多這兩個呢?最主要是因為如果你還沒有確定可以跟Server連結上就將mail送出去,這時就會發生例外事件,如果我們在還沒Connect之前不讓使用者可以按下S
23、end鈕就可以保證不會發生Connect Fail的例外事件發生。 2223-3. 寄信程式範例23-2:主要程式碼(main.cpp)可以用來檢查輸入資料的方法很多,包括用例外事件的寫法或是利用一堆If來做判斷都可以,在這個範例中,我們使用最基本的If來檢查資料的正確性。在這個範例中除了用If來判斷資料的正確性外,我們還使用了BCB所提供的一些函式來判斷連線的狀況。在這份投影片中,我們只列出使用BCB函式來判斷連線的程式碼。想要了解範例完整的程式碼,請參考書上的程式碼。2323-3. 寄信程式範例23-2:主要程式碼(main.cpp)與Server連線部分程式碼/-/ 與Server連線/
24、-void _fastcall TForm1:BitBtn4Click(TObject *Sender) /宣告一個AnsiString用來放置要給使用者看的錯誤訊息 AnsiString msg; /判斷是不是有必要的欄位沒有填寫 if (LabeledEdit1-Text != ) & (LabeledEdit2-Text != ) & (LabeledEdit3-Text != ) /將各個欄位的資料全部寫入變數中,這樣比較方便在程式中使用 AnsiString Server = LabeledEdit1-Text; int Port = LabeledEdit2-Text.ToInt(
25、); AnsiString User = LabeledEdit3-Text; /將Server Side相關屬性設定好 NMSMTP1-Host = Server; /填入 E-mail Server 的位址 NMSMTP1-Port = Port; /填入E-mail Server 的 Port, 一般都是使用25 NMSMTP1-UserID = User; /填入User Name2423-3. 寄信程式範例23-2:主要程式碼(main.cpp)與Server連線部分程式碼 /一切設定都確認有輸入之後.改變以下這些屬性 BitBtn1-Enabled = true; BitBtn2-
26、Enabled = true; BitBtn3-Enabled = true; BitBtn4-Enabled = false; BitBtn5-Enabled = true; LabeledEdit1-ReadOnly = true; LabeledEdit2-ReadOnly = true; LabeledEdit3-ReadOnly = true; /與Server連線 NMSMTP1-Connect(); else /如果在檢查的時後發現有重要的資訊沒有填寫,就會依照沒有填寫的項目一一警告使用者要填入 /在這邊會警告的是第一個沒填入的並不是只要沒填就出現警告 /如果要做到只要填寫錯誤就
27、出現錯誤訊息,只要把所有的else if拿掉改用if即可 if (LabeledEdit1-Text = ) msg = Please inpit the server host!n; MessageBox(GetActiveWindow(), msg.c_str(), NULL, MB_OK|MB_ICONERROR); LabeledEdit1-SetFocus(); 2523-3. 寄信程式範例23-2:主要程式碼(main.cpp)與Server連線部分程式碼 else if (LabeledEdit2-Text = ) msg = Please inpit the server po
28、rt!n; MessageBox(GetActiveWindow(), msg.c_str(), NULL, MB_OK|MB_ICONERROR); LabeledEdit2-SetFocus(); else if (LabeledEdit3-Text = ) msg = Please input the user id!n; MessageBox(GetActiveWindow(), msg.c_str(), NULL, MB_OK|MB_ICONERROR); LabeledEdit3-SetFocus(); 2623-3. 寄信程式範例23-2:主要程式碼(main.cpp)與Serv
29、er斷線部分程式碼/-/ 與Server斷線/-void _fastcall TForm1:BitBtn5Click(TObject *Sender) NMSMTP1-Disconnect(); /斷線之後將所有的屬性恢復預設值,將該清乾淨的輸入框清乾淨 LabeledEdit1-Text = ; LabeledEdit2-Text = 25; LabeledEdit3-Text = ; LabeledEdit4-Text = ; LabeledEdit5-Text = ; LabeledEdit6-Text = ; LabeledEdit7-Text = ; Memo1-Clear(); L
30、istBox1-Clear(); /將一些改成唯讀的屬性改回來 LabeledEdit1-ReadOnly = false; LabeledEdit2-ReadOnly = false; LabeledEdit3-ReadOnly = false; /重新設定按鈕的Enable屬性 BitBtn1-Enabled = false; BitBtn2-Enabled = false; BitBtn3-Enabled = false; BitBtn4-Enabled = true; BitBtn5-Enabled = false;2723-3. 寄信程式範例23-2:主要程式碼(main.cpp)C
31、onnection Failed 時所呼叫的Function /-/ Connection Failed 時所呼叫的Function/-void _fastcall TForm1:NMSMTP1ConnectionFailed(TObject *Sender) /宣告一個AnsiString用來放置要給使用者看的錯誤訊息 AnsiString msg; /跳出一個訊息告訴使用者連線失敗 msg = Connection Failed!n; MessageBox(GetActiveWindow(), msg.c_str(), NULL, MB_OK|MB_ICONERROR); /連線錯誤之後將
32、所有的屬性恢復預設值,將該清乾淨的輸入框清乾淨 LabeledEdit1-Text = ; LabeledEdit2-Text = 25; LabeledEdit3-Text = ; LabeledEdit4-Text = ; LabeledEdit5-Text = “; LabeledEdit6-Text = ; LabeledEdit7-Text = ; Memo1-Clear(); ListBox1-Clear(); /將一些改成唯讀的屬性改回來 LabeledEdit1-ReadOnly = false; LabeledEdit2-ReadOnly = false; LabeledEd
33、it3-ReadOnly = false; /重新設定按鈕的Enable屬性 BitBtn1-Enabled = false; BitBtn2-Enabled = false; BitBtn3-Enabled = false; BitBtn4-Enabled = true; BitBtn5-Enabled = false;2823-3. 寄信程式範例23-2:寄信程式加強版的開發執行結果2923-3. 寄信程式如果各位有發現的話,在範例23-2應該會看到多了NMSMTP1ConnectionFailed這個函式,這個就是當ConnectionFailed的時候會觸發的事件程序在NMSMTP中有
34、關各種Failed所引發的事件還有OnAttatchmentNotFound、OnAuthenticationFailed、OnConnectionFailed、OnFailure、OnHeaderIncomplete、OnInvalidHost、OnRecipienNotFound等事件這些事件的用法跟OnConnectionFailed的用法都差不多,大家可以嘗試著使用看看,一套完善的發信軟體必須要能handle住上面所列的那些Exception才不至於一天到晚出問題,也希望讀者可以透過範例29-2繼續完成還沒Implement的這些Failed相關的Event,讓這個寄信程式可以更完美。
35、 3023-4. HTTP抓網頁程式上一節我們舉了Mail Client當作一個範例,這邊我們再舉一個利用HTTP Protocol來抓取網頁資料的小程式。這個程式我們將放在範例23-3中。這個程式最主要是抓取我們所指定的網頁的資料,傳回值為這個網頁的Header以及Body。這個程式設計上相當的簡單,只用了一個NMHTTP以及兩個SaveDialog為隱藏的VCL元件,其餘的就是PageControl、Panel、Edit、Memo、以及BitBtn等基本的元件。 3123-4. HTTP抓網頁程式範例23-3:網頁抓取範例範例說明在該範例中,我們將利用NMHTTP這個元件抓取網路上任何一個
36、網頁的Header以及Body這兩個重要資料。底下我們將一步一步告訴讀者我們該如何完成這個範例。 Step 1: 開新專案,並且將該使用到的VCL元件放到Form上面 Step 2: 設定相關屬性 SaveDialogNMHTTPStep 3: 程式碼的撰寫 3223-4. HTTP抓網頁程式範例23-3:主要程式碼(main.cpp)void _fastcall TForm1:BitBtn1Click(TObject *Sender) if (LabeledEdit1-Text != ) NMHTTP1-Get(LabeledEdit1-Text); Memo1-Lines-Add(NMHT
37、TP1-Header); Memo2-Lines-Add(NMHTTP1-Body); /-void _fastcall TForm1:BitBtn2Click(TObject *Sender) if (SaveDialog1-Execute() if (!FileExists(SaveDialog1-FileName) Memo1-Lines-SaveToFile(SaveDialog1-FileName); else ShowMessage(檔案已存在!n); /-void _fastcall TForm1:BitBtn3Click(TObject *Sender) if (SaveDia
38、log2-Execute() if (!FileExists(SaveDialog2-FileName) Memo2-Lines-SaveToFile(SaveDialog2-FileName); else ShowMessage(檔案已存在!n); 3323-4. HTTP抓網頁程式範例23-3:網頁抓取範例執行結果3423-5. 一些Client端的小程式在這一小節我們將寫一些簡單的Client/Server程式,全部是利用FastNet內的元件所完成的。DateTime、Time、Message、Echo、NNTP、Stream、POP3、URL、MIME、UUDecode、Finger
39、等。現在我們就利用範例23-4、23-5、23-6、23-7、23-8、23-9、23-10、23-11、以及23-12解釋各個元件的使用方法。 3523-5. 一些Client端的小程式範例23-4:DayTime / Time範例範例說明DayTime、以及Time這兩種服務主要就是傳回目前Server上的時間以及日期。我們寫的這個程式就是去Server上要回這些資料並且顯示出來。在這個介面中,我們將DayTime以及Time兩種不同的服務分開寫,這樣可以讓使用者更容易分辨這兩種VCL元件的用法。 3623-5. 一些Client端的小程式範例23-4:主要程式碼(main.cpp)voi
40、d _fastcall TForm1:BitBtn1Click(TObject *Sender) /使用NMDayTime抓取Server的Date and Time,如果Server Host和Port皆有輸入才連線去抓取資料 if (LabeledEdit1-Text != ) & (LabeledEdit2-Text != ) /將相關的值設定到變數.方便使用 AnsiString Server = LabeledEdit1-Text; int Port = LabeledEdit2-Text.ToInt(); /指定Server Host以及Server Port NMDayTime1
41、-Host = Server; NMDayTime1-Port = Port; /抓取Server端的DateTime LabeledEdit3-Text = NMDayTime1-DayTimeStr; /-void _fastcall TForm1:NMDayTime1ConnectionFailed(TObject *Sender) /當NMDayTime連線錯誤時發生的事件 MessageBox(GetActiveWindow(), 連線失敗!n, NULL, MB_OK|MB_ICONERROR);/-void _fastcall TForm1:NMDayTime1InvalidHo
42、st(bool &Handled) /當NMDayTime發現輸入的Server Host不合法所發生的事件 MessageBox(GetActiveWindow(), 不合法的位址!n, NULL, MB_OK|MB_ICONERROR);3723-5. 一些Client端的小程式範例23-4:主要程式碼(main.cpp)void _fastcall TForm1:BitBtn2Click(TObject *Sender) /使用NMTime抓取Server的Time,如果Server Host和Port皆有輸入才連線去抓取資料 if (LabeledEdit4-Text != ) & (
43、LabeledEdit6-Text != ) /將相關的值設定到變數.方便使用 AnsiString Server = LabeledEdit4-Text; int Port = LabeledEdit6-Text.ToInt(); /指定Server Host以及Server Port NMTime1-Host = Server; NMTime1-Port = Port; /抓取Server端的Time LabeledEdit5-Text = NMTime1-TimeStr; /-void _fastcall TForm1:NMTime1ConnectionFailed(TObject *S
44、ender) /當NMTime連線錯誤時發生的事件 MessageBox(GetActiveWindow(), 連線失敗!n, NULL, MB_OK|MB_ICONERROR);/-void _fastcall TForm1:NMTime1InvalidHost(bool &Handled) /當NMTime發現輸入的Server Host不合法所發生的事件 MessageBox(GetActiveWindow(), 不合法的位址!n, NULL, MB_OK|MB_ICONERROR);3823-5. 一些Client端的小程式範例23-4:DayTime / Time範例執行結果3923
45、-5. 一些Client端的小程式範例23-5:Message Server / Client範例範例說明Message這部分的程式分成Server端以及Client端,使用6711這個Port來傳輸Message。這個程式最主要的功能就是Server端可以接收來自Client端的資料。 4023-5. 一些Client端的小程式範例23-5:主要程式碼(main.cpp)Server Side Codevoid _fastcall TForm1:BitBtn1Click(TObject *Sender) /清除Message Log Memo1-Clear();/-void _fastcal
46、l TForm1:NMMSGServ1MSG(TComponent *Sender, const AnsiString sFrom, const AnsiString sMsg) /將收到的訊息放在msg這個AnsiString中 AnsiString msg = sFrom + = + sMsg; /停頓一些時間.讓程式處理Socket Sleep(100); /將收到的Log Append到Memo上 Memo1-Lines-Add(msg);4123-5. 一些Client端的小程式範例23-5:主要程式碼(main.cpp)Client Side Codevoid _fastcall
47、TForm1:BitBtn2Click(TObject *Sender) /檢查是否三個Edit全部都填入資料了 if (LabeledEdit1-Text != ) & (LabeledEdit2-Text != ) & (LabeledEdit3-Text != ) NMMsg1-Host = LabeledEdit1-Text; /設定Server Host NMMsg1-FromName = LabeledEdit2-Text; /設定Senders Name NMMsg1-PostIt(LabeledEdit3-Text); /將要送出的Message Post出去給Server L
48、abeledEdit3-Text = ; /將送出的內容從LabeledEdit中清除 /-void _fastcall TForm1:LabeledEdit3KeyPress(TObject *Sender, char &Key) /功能跟BitBtn2一樣,不過這個事件是用來抓取Enter這個鍵 /如果我們在LabeledEdit中按下Enter,程式就會自動幫我們把訊息送出去 if (Key = 13) if (LabeledEdit1-Text != ) & (LabeledEdit2-Text != ) & (LabeledEdit3-Text != ) NMMsg1-Host =
49、LabeledEdit1-Text; NMMsg1-FromName = LabeledEdit2-Text; NMMsg1-PostIt(LabeledEdit3-Text); LabeledEdit3-Text = ; 4223-5. 一些Client端的小程式範例23-5:Message Server / Client範例執行結果4323-5. 一些Client端的小程式範例23-6:Echo Client範例範例說明Echo Server主要的用途就是將Client發給Server的訊息傳送回來。在這個程式中,我們除了將訊息傳送回來外,我們也加入了時間的計算,看看整個傳送過程中需要多少
50、時間。 4423-5. 一些Client端的小程式範例23-6:主要程式碼(main.cpp)void _fastcall TForm1:BitBtn1Click(TObject *Sender) /該輸入的資訊都有輸入就與Server建立連線 if (LabeledEdit1-Text != ) & (LabeledEdit2-Text != ) /設定屬性 NMEcho1-Host = LabeledEdit1-Text; NMEcho1-Port = LabeledEdit2-Text.ToInt(); NMEcho1-Connect(); /與Server離線 /-void _fast
51、call TForm1:BitBtn2Click(TObject *Sender) NMEcho1-Disconnect(); /與Server切斷連線/-void _fastcall TForm1:BitBtn3Click(TObject *Sender) /傳送LabeledEdit3的Text至Echo Server,將Echo送回的字串放到LabeledEdit4的Text中 /並且將整個Echo所花的時間紀錄到LabeledEdit5的Text中 if (LabeledEdit3-Text != ) LabeledEdit4-Text = NMEcho1-Echo(LabeledEd
52、it3-Text); LabeledEdit5-Text = FloatToStr(NMEcho1-ElapsedTime) + milliseconds; 4523-5. 一些Client端的小程式範例23-6:主要程式碼(main.cpp)void _fastcall TForm1:LabeledEdit3KeyPress(TObject *Sender, char &Key) /與BitBtn3功能一樣.只是在這邊不需要按下按鈕 /只要在LabeledEdit3中輸入Enter就馬上送出 if (Key = 13) if (LabeledEdit3-Text != ) LabeledEd
53、it4-Text = NMEcho1-Echo(LabeledEdit3-Text); LabeledEdit5-Text = FloatToStr(NMEcho1-ElapsedTime) + milliseconds; /-void _fastcall TForm1:NMEcho1Connect(TObject *Sender) /當連線的時候要做的屬性設定 BitBtn1-Enabled = false; BitBtn2-Enabled = true; BitBtn3-Enabled = true;/-void _fastcall TForm1:NMEcho1Disconnect(TOb
54、ject *Sender) /當斷線的時候要做的屬性設定 BitBtn1-Enabled = true; BitBtn2-Enabled = false; BitBtn3-Enabled = false;4623-5. 一些Client端的小程式範例23-6:Echo Client範例執行結果4723-5. 一些Client端的小程式範例23-7:NNTP Client範例範例說明NNTP就是我們一般常說的新聞群組或是News Server。在這個程式中我們只先寫出與主機連線以及取得Group名稱,並且抓取Group文章的數量資訊。至於列出Group內的文章的標題,還有閱讀文章、發表文章等等的
55、功能,只需要稍微看一下NMNNTP中的各種Event所代表的意義,以及我們要執行的動作有哪些屬性,我們就可以很容易的寫出一個完整的NNTP Client端軟體(也常被稱做NNTP Reader)。至於News Server還有一些Control Message也只是一封比較特別的文章而已,所以要發表所謂的Control Message我們只需要稍微改一下發表文章這一部份就可以達到所要的功能。4823-5. 一些Client端的小程式範例23-7:主要程式碼(main.cpp)void _fastcall TForm1:FormShow(TObject *Sender) /當Form顯示的時候抓
56、取Temp Directory,把Temp Directory設定成Cache Dir.以及Attach File Dir. LPITEMIDLIST pidl; LPMALLOC pShellMalloc; char szDir512; int CSIDL_STR = CSIDL_TEMPLATES; if (SUCCEEDED(SHGetMalloc(&pShellMalloc) if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_STR, &pidl) if (SHGetPathFromIDList(pidl, szDir) Labe
57、ledEdit5-Text = szDir; LabeledEdit6-Text = szDir; pShellMalloc-Free(pidl); pShellMalloc-Release(); /-void _fastcall TForm1:BitBtn1Click(TObject *Sender) /與NNTP Server連線 if (LabeledEdit1-Text != ) & (LabeledEdit2-Text != ) & (LabeledEdit5-Text != ) & (LabeledEdit6-Text != ) /設定NNTP Client的一些基本資訊 NMNN
58、TP1-ReportLevel = Status_Basic; NMNNTP1-TimeOut = 20000; NMNNTP1-NewsDir = LabeledEdit5-Text; NMNNTP1-AttachFilePath = LabeledEdit6-Text; NMNNTP1-Host = LabeledEdit1-Text; NMNNTP1-Port = StrToInt(LabeledEdit2-Text); /如果NNTP Server需要使用者名稱及密碼,只要有輸入就可以在這邊進行認證 if (LabeledEdit3-Text != “”) NMNNTP1-UserId
59、 = LabeledEdit3-Text; NMNNTP1-Password = LabeledEdit4-Text; NMNNTP1-Connect(); /與NNTP Server連線 4923-5. 一些Client端的小程式範例23-7:主要程式碼(main.cpp)void _fastcall TForm1:BitBtn2Click(TObject *Sender) NMNNTP1-Disconnect(); /與NNTP Server斷線/-void _fastcall TForm1:NMNNTP1Connect(TObject *Sender) /與Server連線時要做的工作
60、BitBtn1-Enabled = false; BitBtn2-Enabled = true; StatusBar1-SimpleText = Connected; /當Client和Server連線之後,要直接呼叫GetGroupList.將Server上的Group抓下來 NMNNTP1-GetGroupList();/-void _fastcall TForm1:NMNNTP1Disconnect(TObject *Sender) /與Server斷線時要做的工作 BitBtn1-Enabled = true; BitBtn2-Enabled = false; StatusBar1-S
61、impleText = Disconnected;/-void _fastcall TForm1:NMNNTP1GroupListUpdate(AnsiString name, int FirstArticle, int LastArticle, bool Posting) ListBox1-Items-Add(name); /將GetGroupList抓到的資料放到Memo中5023-5. 一些Client端的小程式範例23-7:主要程式碼(main.cpp)void _fastcall TForm1:NMNNTP1GroupListCacheUpdate(bool &Handled, An
62、siString name, int FirstArticle, int LastArticle, bool Posting) /將GetGroupList抓到的資料放到Memo中 ListBox1-Items-Add(name);/-void _fastcall TForm1:NMNNTP1Status(TComponent *Sender, AnsiString Status) /將NNTP Client的狀態顯示在StatusBar1上 if (StatusBar1 != 0) StatusBar1-SimpleText = Status; /-void _fastcall TForm1
63、:ListBox1DblClick(TObject *Sender) /設定當我們在Memo上的Item點兩下 /Client會自動幫我們去Server查詢該Group的資訊 NMNNTP1-SetGroup(ListBox1-Items-StringsListBox1-ItemIndex);5123-5. 一些Client端的小程式範例23-7:NNTP Client範例執行結果5223-5. 一些Client端的小程式範例23-8:Stream Server / Client範例範例說明Stream Server / Client到底要做什麼?照字面上來看,Stream就是資料流的意思。如
64、果要跟Message Client / Server比較的話,Message Client / Server是傳送純文字的資料給Server端而Stream Client / Server則是傳送binary資料給Server端。在這個程式中我們使用6712這個Port來傳送接收資料,傳送的資料為影像檔(Bitmap File)。主要介面分成Client以及Server兩個部分,Client端只有單純的選擇檔案(BMP檔案)並且送出Server端則是將接受到的資料存成buf.bmp,如果我們按下Server端的Load,程式則會將Server都到資料存下來的這個檔案放到Timage上讓大家觀看
65、。不管在Client端或是Server端,我們都必須得開一個FileStream這個物件出來當作資料流的暫存區,這跟我們一般開檔的觀念很像,只是在這邊我們將整個二元檔看成是一個Stream。 5323-5. 一些Client端的小程式範例23-8:主要程式碼(main.cpp)void _fastcall TForm1:BitBtn1Click(TObject *Sender) /選擇一張用來傳送的圖片 if (OpenPictureDialog1-Execute() LabeledEdit2-Text = OpenPictureDialog1-FileName;/-void _fastcal
66、l TForm1:BitBtn2Click(TObject *Sender) /這個函式主要功能就是傳送我們選擇的檔案到Server端 if (LabeledEdit1-Text != ) & (LabeledEdit2-Text != ) /開一個File Stream出來,並把我們要傳送的檔案經由該File Stream傳送 TFileStream *FStream; FStream = new TFileStream(OpenPictureDialog1-FileName, fmOpenRead); NMStrm1-Host = LabeledEdit1-Text; NMStrm1-Fr
67、omName = LabeledEdit2-Text; NMStrm1-PostIt(FStream); /傳送完畢後將Stream關閉 FStream-Free(); 5423-5. 一些Client端的小程式範例23-8:主要程式碼(main.cpp)void _fastcall TForm1:BitBtn3Click(TObject *Sender) Image1-Picture-LoadFromFile(buf.bmp); /在Server端將接收到的圖片load出來/-void _fastcall TForm1:NMStrm1Connect(TObject *Sender) Stat
68、usBar1-SimpleText = Connected; /在狀態列顯示Client已經與Server端連線/-void _fastcall TForm1:NMStrm1Disconnect(TObject *Sender) StatusBar1-SimpleText = Disconnected; /在狀態列顯示Client已經與Server端斷線/-void _fastcall TForm1:NMStrm1MessageSent(TObject *Sender) /當我們在傳送Stream的時候,會跳出一個MessageBox告訴我們將要傳送一個Stream到Server端 ShowM
69、essage(A Stream Sent to + LabeledEdit1-Text);/-void _fastcall TForm1:NMStrm1Status(TComponent *Sender, AnsiString Status) if (StatusBar1 != 0) StatusBar1-SimpleText = Status;5523-5. 一些Client端的小程式範例23-8:主要程式碼(main.cpp)void _fastcall TForm1:NMStrmServ1MSG(TComponent *Sender, const AnsiString sFrom, TS
70、tream *strm) /這個函式是Server端用來接收Client送過來的Stream /在這邊我們是將收到的Stream存到buf.bmp這個檔案裡 TFileStream *FStream; /開一個File Stream /如果buf.bmp已經存在就砍掉 if (FileExists(buf.bmp) DeleteFile(buf.bmp); /將收到的Stream放到檔案中 FStream = new TFileStream(buf.bmp, fmCreate); FStream-CopyFrom(strm, strm-Size); FStream-Free(); /釋放Str
71、eam的資源 BitBtn3-Enabled = true;/-void _fastcall TForm1:NMStrmServ1Status(TComponent *Sender, AnsiString Status) if (StatusBar2 != 0) StatusBar2-SimpleText = Status;5623-5. 一些Client端的小程式範例23-8:Stream Server / Client範例執行結果5723-5. 一些Client端的小程式範例23-9:POP3 Client範例範例說明目前在看信方便比較常用的通訊協定大概就是POP3以及IMAP。在範例23
72、-9我們利用BCB提供的NMPOP3這個VCL元件實做POP3 Client。在這個程式中,大部分的功能(會用到的函式)都已經在這個範例程式中用到了。整個介面中,除了輸入Host以及使用者、密碼外,就是顯示出我們有多少郵件在Server上,在程式中我們只將每封信件的標題和送信者列出來。要看信的內容,也只需要去抓取NMPOP3-MailMessage中的一些屬性就可以了。在附夾檔方面,這個VCL元件也提供處理附帶檔的功能,一樣是在MailMessage中就可以把附帶檔的問題處理完。 5823-5. 一些Client端的小程式範例23-9:主要程式碼(main.cpp)與POP3 Server連線
73、並取得Mail Listvoid _fastcall TForm1:BitBtn1Click(TObject *Sender) if (LabeledEdit1-Text != ) & (LabeledEdit2-Text != ) & (LabeledEdit3-Text != ) & (LabeledEdit4-Text != ) AnsiString buf; /設定一些Client的基本屬性 NMPOP31-ReportLevel = Status_Basic; NMPOP31-TimeOut = 20000; NMPOP31-Host = LabeledEdit1-Text; NMP
74、OP31-Port = LabeledEdit2-Text.ToInt(); NMPOP31-UserID = LabeledEdit3-Text; NMPOP31-Password = LabeledEdit4-Text; NMPOP31-Connect(); /與POP3 Server連線 /顯示目前Server上有幾封信.以及將信件列出 StatusBar1-SimpleText = IntToStr(NMPOP31-MailCount) + Mail; NMPOP31-List(); /把每一封mail利用Subject和From弄成我們想要顯示出來的格式,並且把這些資訊放到Memo上
75、 for (int i = 1 ; i MailCount ; i+) NMPOP31-GetMailMessage(i); buf = IntToStr(i) + = Subject : ; buf += NMPOP31-MailMessage-Subject + ttFrom : ; buf += NMPOP31-MailMessage-From; Memo1-Lines-Add(buf); 5923-5. 一些Client端的小程式範例23-9:主要程式碼(main.cpp)void _fastcall TForm1:BitBtn2Click(TObject *Sender) NMPOP
76、31-Disconnect(); /與POP3 Server終止連線/-void _fastcall TForm1:NMPOP31List(int Msg, int Size) if (Msg Clear(); /-void _fastcall TForm1:NMPOP31Connect(TObject *Sender) StatusBar1-SimpleText = Connected; BitBtn1-Enabled = false; BitBtn2-Enabled = true;/-void _fastcall TForm1:NMPOP31Disconnect(TObject *Send
77、er) StatusBar1-SimpleText = Disconnected; BitBtn1-Enabled = true; BitBtn2-Enabled = false; LabeledEdit1-Text = ; LabeledEdit2-Text = 110; LabeledEdit3-Text = ; LabeledEdit4-Text = ;6023-5. 一些Client端的小程式範例23-9:POP3 Client範例執行結果6123-5. 一些Client端的小程式範例23-10:URL Encode / Decode範例範例說明URL這部分最主要就是提供網址的編碼以及
78、解碼,當然也可以對網頁中的一些特殊字元作編碼的動作。在程式中我們分成上下兩個畫面,上面那部分為編碼,下面是解碼,大家可以試試看將一串文字編碼後再次解碼是不是會一樣 6223-5. 一些Client端的小程式範例23-10:主要程式碼(main.cpp)void _fastcall TForm1:BitBtn1Click(TObject *Sender) /URL Encode if (LabeledEdit1-Text != ) NMURL1-InputString = LabeledEdit1-Text; LabeledEdit2-Text = NMURL1-Encode; /-void _
79、fastcall TForm1:BitBtn2Click(TObject *Sender) /URL Decode if (LabeledEdit3-Text != ) NMURL1-InputString = LabeledEdit3-Text; LabeledEdit4-Text = NMURL1-Decode; /-void _fastcall TForm1:NMURL1Error(TObject *Sender, AnsiString Operation, AnsiString ErrMsg) /在Encode或是Decode過程中出現在問題 /就將錯誤訊息顯示在MessageBox上
80、 ShowMessage(ErrMsg);6323-5. 一些Client端的小程式範例23-10:URL Encode / Decode範例執行結果6423-5. 一些Client端的小程式範例23-11:MIME / UUDecode範例範例說明UUEncode / UUDecode也是非常常用的編碼/解碼方式。在BCB的VCL元件中,當然也提供了MIME以及UUCode這兩種常用的編碼解碼函式,使用上非常的簡單方便。在傳送信件的過程中,我們常常使用MIME或是UUCode這類的軟體來做編碼及解碼的動作。MIME和UUCode是兩種不同的編法方法。例如我們現在將一封信件利用MIME編碼後,
81、對方必須也要經過MIME的解碼後才看的懂。一般來說現在的一些Mail Server(如Sendmail、qmail、postfix)已經不再像以前常常會因為MIME編碼的關係產生亂碼的問題。在範例23-11中,因為我們是利用檔案來做MIME以及UUCode的編碼解碼,所以也有使用到File Stream。6523-5. 一些Client端的小程式範例23-11:主要程式碼(main.cpp)void _fastcall TForm1:BitBtn1Click(TObject *Sender) /選擇Input File if (OpenDialog1-Execute() LabeledEdit
82、1-Text = OpenDialog1-FileName;/-void _fastcall TForm1:BitBtn2Click(TObject *Sender) /選擇Output File if (SaveDialog1-Execute() LabeledEdit2-Text = SaveDialog1-FileName;/-void _fastcall TForm1:BitBtn3Click(TObject *Sender) /MIME Encode if (LabeledEdit1-Text != ) & (LabeledEdit2-Text != ) TFileStream *i
83、n = new TFileStream(LabeledEdit1-Text, fmOpenRead); TFileStream *out = new TFileStream(LabeledEdit2-Text, fmCreate); NMUUProcessor1-Method = uuMime; NMUUProcessor1-InputStream = in; NMUUProcessor1-OutputStream = out; NMUUProcessor1-Encode(); LabeledEdit1-Text = ; LabeledEdit2-Text = ; in-Free(); out
84、-Free(); 6623-5. 一些Client端的小程式範例23-11:主要程式碼(main.cpp)void _fastcall TForm1:BitBtn4Click(TObject *Sender) /MIME Decode if (LabeledEdit1-Text != ) & (LabeledEdit2-Text != ) TFileStream *in = new TFileStream(LabeledEdit1-Text, fmOpenRead); TFileStream *out = new TFileStream(LabeledEdit2-Text, fmCreate)
85、; NMUUProcessor1-Method = uuMime; NMUUProcessor1-InputStream = in; NMUUProcessor1-OutputStream = out; NMUUProcessor1-Decode(); LabeledEdit1-Text = ; LabeledEdit2-Text = ; in-Free(); out-Free(); /-void _fastcall TForm1:BitBtn7Click(TObject *Sender) /選擇Input File if (OpenDialog2-Execute() LabeledEdit3
86、-Text = OpenDialog2-FileName;/-void _fastcall TForm1:BitBtn8Click(TObject *Sender) /選擇Output File if (SaveDialog2-Execute() LabeledEdit4-Text = SaveDialog2-FileName;6723-5. 一些Client端的小程式範例23-11:主要程式碼(main.cpp)void _fastcall TForm1:BitBtn5Click(TObject *Sender) /UUCode Encode if (LabeledEdit3-Text !=
87、 ) & (LabeledEdit4-Text != ) TFileStream *in = new TFileStream(LabeledEdit3-Text, fmOpenRead); TFileStream *out = new TFileStream(LabeledEdit4-Text, fmCreate); NMUUProcessor1-Method = uuCode; NMUUProcessor1-InputStream = in; NMUUProcessor1-OutputStream = out; NMUUProcessor1-Encode(); LabeledEdit3-Te
88、xt = ; LabeledEdit4-Text = ; in-Free(); out-Free(); /-void _fastcall TForm1:BitBtn6Click(TObject *Sender) /UUCode Decode if (LabeledEdit3-Text != ) & (LabeledEdit4-Text != ) TFileStream *in = new TFileStream(LabeledEdit3-Text, fmOpenRead); TFileStream *out = new TFileStream(LabeledEdit4-Text, fmCrea
89、te); NMUUProcessor1-Method = uuCode; NMUUProcessor1-InputStream = in; NMUUProcessor1-OutputStream = out; NMUUProcessor1-Decode(); LabeledEdit3-Text = ; LabeledEdit4-Text = ; in-Free(); out-Free(); 6823-5. 一些Client端的小程式範例23-11:主要程式碼(main.cpp)void _fastcall TForm1:NMUUProcessor1BeginDecode(TObject *Se
90、nder) StatusBar1-SimpleText = Decoding.;/-void _fastcall TForm1:NMUUProcessor1BeginEncode(TObject *Sender) StatusBar1-SimpleText = Encoding.;/-void _fastcall TForm1:NMUUProcessor1EndDecode(TObject *Sender) StatusBar1-SimpleText = Decoded;/-void _fastcall TForm1:NMUUProcessor1EndEncode(TObject *Sende
91、r) StatusBar1-SimpleText = Encoded; 6923-5. 一些Client端的小程式範例23-11:MIME / UUDecode範例執行結果7023-5. 一些Client端的小程式範例23-12:Finger Client範例範例說明Finger最主要的用途是用來查詢Server端的使用者的一些資本資料以及訊息,不過這幾年來因為Finger的Daemon不斷的傳出安全性上的漏洞,所以現在大部分的Server都已經將Finger這個Service關閉了。不過在這邊我們還是可以找到不少Server仍然有提供這項服務。7123-5. 一些Client端的小程式範例2
92、3-12:主要程式碼(main.cpp)void _fastcall TForm1:BitBtn1Click(TObject *Sender) if (LabeledEdit1-Text != ) & (LabeledEdit2-Text != ) & (LabeledEdit3-Text != ) NMFinger1-ReportLevel = Status_Basic; NMFinger1-Host = LabeledEdit1-Text; NMFinger1-Port = LabeledEdit2-Text.ToInt(); NMFinger1-User = LabeledEdit3-T
93、ext; Memo1-Text = NMFinger1-FingerStr; /-void _fastcall TForm1:NMFinger1Status(TComponent *Sender, AnsiString Status) if (StatusBar1 != 0) StatusBar1-SimpleText = Status;/-void _fastcall TForm1:NMFinger1Connect(TObject *Sender) StatusBar1-SimpleText = Connected;/-void _fastcall TForm1:NMFinger1Disco
94、nnect(TObject *Sender) StatusBar1-SimpleText = Disconnected; 7223-5. 一些Client端的小程式範例23-12:Finger Client範例執行結果7323-6. 聊天室聊天室,這是一個普遍大家都用過的網路軟體。現在我們將利用BCB所提供的元件,將聊天室寫出來。在範例23-13以及範例23-14中,我們分別實作聊天室的Server端以及Client端。Server端負責接受Client端的連線,並且將Client端傳過來的Message傳給每個正在線上的Client端,達到聊天室的功能。在這兩個範例中,我們主要是利用Serv
95、er Socket以及Client Socket為網路連線的溝通管道,使用的Port是6666這個Port,所以在網路連線以及傳送的部分皆由Server / Client Socket這兩個VCL元件幫忙handle。 7423-6. 聊天室範例23-13:聊天室Server實作範例說明Server端主要的工作就是要做到Concurrent Server,還好在這方面BCB已經幫我們處理好了。接著在Server端習慣上都會將連線紀錄給Log下來,所以我們就利用一些小技巧將Client連線的時間以及位址紀錄下來,也順便記錄了離線時間。除了Log的紀錄外,也可以將聊天紀錄紀錄下來,畢竟所有的Cli
96、ent端要收到訊息都必須要透過Server,所以在Server端想要做到監控或是紀錄都非常的容易。在Server端我們總共開啟了ServerSocket的這幾種事件:OnClientConnect、OnClientDisconnect、以及OnClientRead在Form上面啟動了OnCreate事件,再加入清除Memo的按鈕,如此我們就完成了一個聊天室的Server了。在傳送資料到Client端我們用了一點小小的技巧,那就是用一個變數用來記錄目前連線的Client端數量,也藉此來判斷我們在connections這個物件中那些需要接收我們的訊息,藉此達到將資料傳給每個Client的功能。 7
97、523-6. 聊天室範例23-13:主要程式碼(main.cpp)int Num = 0; /紀錄目前Server上的連線個數/-_fastcall TForm1:TForm1(TComponent* Owner) : TForm(Owner) /-void _fastcall TForm1:BitBtn1Click(TObject *Sender) Memo1-Clear(); /清除Memo內容/-void _fastcall TForm1:FormShow(TObject *Sender) /當MainForm出現的時候.將ServerClient Active,並且在StatusBar
98、上顯示Listening. ServerSocket1-Active = true; StatusBar1-Panels-Items0-Text = Listening.; Num = 0;/-void _fastcall TForm1:ServerSocket1ClientConnect(TObject *Sender, TCustomWinSocket *Socket) AnsiString buf; /Client與Server連線上所要做的事情,將Server收到連線需求的Log Post一份在Memo上 buf = DateTimeToStr(Now(); buf += + Sock
99、et-RemoteAddress + tConnected.; Memo1-Lines-Add(buf); Num+; /在StatusBar上顯示目前有多人人在站上 StatusBar1-Panels-Items0-Text = IntToStr(Num) + Client Connected.;7623-6. 聊天室範例23-13:主要程式碼(main.cpp)void _fastcall TForm1:ServerSocket1ClientDisconnect(TObject *Sender, TCustomWinSocket *Socket) AnsiString buf; /Clie
100、nt與Server斷線時所要做的動作 buf = DateTimeToStr(Now(); buf += + Socket-RemoteAddress + tDisconnected.; Memo1-Lines-Add(buf); Num-; if (Num 0) StatusBar1-Panels-Items0-Text = IntToStr(Num) + Client Connected.; else StatusBar1-Panels-Items0-Text = Listening.;/-void _fastcall TForm1:ServerSocket1ClientRead(TObj
101、ect *Sender, TCustomWinSocket *Socket) /將Server端收到的Message傳送給所有的Client,這些收到Message的Client包括自己 AnsiString buf = Socket-ReceiveText(); int i = 0; for (i = 0 ; i Socket-Connectionsi-SendText(buf);7723-6. 聊天室範例23-13:聊天室Server實作執行結果7823-6. 聊天室範例23-14:聊天室Client實作範例說明Client端在設計上更是容易,我們只需要管送出訊息以及接收訊息就好,其他的幾
102、乎可以不需要考慮。當然在送訊息之前的Connect部分,我們在程式中有做一些檢查的動作,例如還沒有Connect的時候就無法按下Send的按鈕,而且也要求一定要輸入User Name才可以,這樣才能讓收到訊息的人知道是誰說了這句話。如果希望再嚴謹一點,可以在Server端也一併記錄這個訊息的來源IP。在輸入Message的部分,我們跟前面一樣都利用Edit中的OnKeyPress這個Event來偵測是不是有按下Enter鍵,當按下Enter鍵的時候就自動傳送訊息出去。在ClientSocket中我們共使用了OnConnect、OnDisconnect、以及OnRead這三個Event,再加上其
103、他的一些檢查程式或是傳送的程式碼,這樣就即將完成這個範例程式。 7923-6. 聊天室範例23-14:主要程式碼(main.cpp)void _fastcall TForm1:FormShow(TObject *Sender) StatusBar1-Panels-Items0-Text = Disconnect;/ 與Server端建立連線void _fastcall TForm1:BitBtn1Click(TObject *Sender) AnsiString Server; /如果已經連線又按下連線的按鈕.將目前的連線切斷 if (ClientSocket1-Active) ClientS
104、ocket1-Active = false; LabeledEdit1-Text = ; LabeledEdit2-Text = ; LabeledEdit3-Text = ; Memo1-Clear(); LabeledEdit2-Enabled = false; if (LabeledEdit1-Text != ) /與Serve端建立連線 ClientSocket1-Host = LabeledEdit1-Text; ClientSocket1-Active = true; LabeledEdit2-Enabled = true; else /如果沒有輸入Server位址或是重新連線就跳
105、出一個Input Box讓我們輸入Server Address /輸入之後會自動幫我們利用該Address連線 if (InputQuery(Computer to connect to, Address Name:, Server) if (Server.Length() 0) ClientSocket1-Host = Server; ClientSocket1-Active = true; LabeledEdit2-Enabled = true; 8023-6. 聊天室範例23-14:主要程式碼(main.cpp)void _fastcall TForm1:BitBtn2Click(TOb
106、ject *Sender) AnsiString Buf; /將我們想要傳送的訊息加上User Name後送出 Buf = LabeledEdit2-Text + : + LabeledEdit3-Text; ClientSocket1-Socket-SendText(Buf); LabeledEdit3-Text = ;/-void _fastcall TForm1:ClientSocket1Connect(TObject *Sender, TCustomWinSocket *Socket) /與Server連線上將在StatusBar上顯示相關訊息 StatusBar1-Panels-It
107、ems0-Text = Connected to + Socket-RemoteAddress; LabeledEdit1-Text = Socket-RemoteAddress;/-void _fastcall TForm1:ClientSocket1Disconnect(TObject *Sender, TCustomWinSocket *Socket) /與Server切斷連線也會在StatusBar上顯示訊息 StatusBar1-Panels-Items0-Text = Disconnected;8123-6. 聊天室範例23-14:主要程式碼(main.cpp)void _fast
108、call TForm1:ClientSocket1Read(TObject *Sender, TCustomWinSocket *Socket) /將Client接收到的Message顯示在Memo上 Memo1-Lines-Add(Socket-ReceiveText();/-void _fastcall TForm1:LabeledEdit3KeyPress(TObject *Sender, char &Key) /當我們在LabeledEdit3上按下Enter會去呼叫BitBtn2的Click事件 if (Key = 13) BitBtn2Click(Sender);/-void _f
109、astcall TForm1:LabeledEdit2Change(TObject *Sender) /判斷什麼時候才可以送出Message.什麼時候不能送出 if (LabeledEdit2-Text != ) & (ClientSocket1-Active) LabeledEdit3-Enabled = true; BitBtn2-Enabled = true; else LabeledEdit3-Enabled = false; BitBtn2-Enabled = false; 8223-6. 聊天室範例23-14:聊天室Client實作執行結果8323-7. CGI程式設計CGI程式自
110、從Web開始大行其道時就慢慢的受到大家的注意,那什麼是CGI呢? 簡單的說,就是Web Server端的程式 Web Server可以將這些程式執行之後的結果傳回給使用者,這樣的程式就叫做CGI程式。 8423-7. CGI程式設計CGI程式是放在Server端,當Client端要求某些需要CGI執行後才會有的資料時,Server端就會執行CGI程式(例如去抓取資料庫的資料),然後再把資料傳回給Client端。 一般如果我們利用單純的C/C+語言來發展CGI程式的話,我們一開始會遇到的就是我們一定要先送個Header過去,沒有先送header就會產生Internal Server Error這
111、樣的錯誤。但是在BCB的環境中,我們不需要考慮這些東西,BCB自己會幫我們把該弄的、該送的自動處理完,程式開發者只需要單純的考慮要輸出哪些資料就好。 8523-7. CGI程式設計範例23-15:CGI程式實作範例說明Step 1: 首先開啟一個新專案,並且選擇Web Server Application這個專案項目。接著會出現一個對話窗讓我們選擇CGI的格式,在這個程式中我們選擇CGI Stand-alone這種CGI的格式 Step 2: CGI開發環境介紹Step 3: CGI程式撰寫Step 4: 編譯程式8623-7. CGI程式設計範例23-15:主要程式碼(main.cpp)/-
112、#include main.h/-#pragma package(smart_init)#pragma resource *.dfmTWebModule1 *WebModule1;/-_fastcall TWebModule1:TWebModule1(TComponent* Owner): TWebModule(Owner)/-void _fastcall TWebModule1:WebModule1WebActionItem1Action( TObject *Sender, TWebRequest *Request, TWebResponse *Response, bool &Handled
113、) Response-Content = Hello World!; 8723-7. CGI程式設計範例23-15:CGI程式實作執行結果上面就是一個最基本的CGI程式的開發過程。但是以最近幾年的發展,用C/C+寫CGI似乎漸漸的沒落,目前市場上比較常見的CGI語言有php、jsp、asp、以及用perl、shell等script語言寫的CGI,有興趣的讀者可以研究相關的書籍。 88本章習題請將NNTP Client完成。 請將POP3 Client完成。以聊天室為藍本,改寫成五子棋對戰。 將聊天室改用UDP Protocol實作。 請說明這一章所說的CGI跟php、jsp、asp等有什麼異同。 89