?? c++builder網絡編程系列講座第一講:tserversocket和tclientsocket的使用.txt
字號:
作者:hxfwsk
email: hxfwsk@hotmail.com
日期:6/15/2001 7:28:35 PM
TServerSocket和TClientSocket的使用 [H5]C++Builder網絡編程系列之一[/H5][H4]TServerSocket和TClientSocket的使用[/H4]
在網絡編程中,WinSocket API編程是最基本,也是最麻煩的地方(說句不怕影響形象的話,我對此就是一知半解)。但是,如果你是使用C++Builder作為編程平臺,你就偷著樂吧,有了BCB,菜鳥變高手!:-)
在BCB中,TServerSocket和TClientSocket涵蓋了基本的WinSocket編程,其中TServerSocket作為服務器方使用,TClientSocket作為客戶端使用,這兩個組件本身并不提供Socket連接,但是他們都有一個Socket屬性,這個屬性才提供了Socket連接。下面就先向大家介紹一下這兩個組件常用的方法屬性,然后在通過一個例子來看看這兩個組件的使用。[H5]1)TServerSocket[/H5][TABLE][TR][TD]名稱[/TD][TD]類型[/TD][TD]說明[/TD][/TR][TR][TD]Socket[/TD][TD]TServerWinSocket[/TD][TD]最重要的屬性,提供Socket連接,事實上發送/接收數據都要靠這個屬性,下面還有詳細的介紹。[/TD][/TR][TR][TD]Port[/TD][TD]int[/TD][TD]要監聽的端口,如果在Service屬性中指定了服務類型,此屬性將被忽略。[/TD][/TR][TR][TD]Service[/TD][TD]AnsiString[/TD][TD]提供的服務,如HTTP、FTP等,如果在這里指定了服務類型,Port將被忽略,因為各種服務都有特定的端口,如FTP:21、HTTP:80[/TD][/TR][TR][TD]ServerType[/TD][TD]TServerType[/TD][TD]設置與客戶連接的方式,取值為兩個枚舉常量stNonBlocking和 stThreadBlocking,stNonBlocking表示用非阻塞方式連接每一個客戶,每個連接都在一個單獨的線程中處理。并用OnClientRead()和OnClientWrite()通知服務器端的Socker進行讀寫。 stThreadBlocking表示以阻塞方式連接客戶,即以主動查詢的方式可客戶連接。[/TD][/TR][TR][TD]Active[/TD][TD]bool[/TD][TD]激活服務,相當于調用Open()方法。[/TD][/TR][TR][TD]OnAccept[/TD][TD]事件[/TD][TD]當有客戶請求連接時觸發[/TD][/TR][TR][TD]OnClientRead[/TD][TD]事件[/TD][TD]通知服務器去讀取有關信息。OnClientWrite與此類似。[/TD][/TR][/TABLE][H5]2)TClientSocket[/H5][TABLE][TR][TD]名稱[/TD][TD]類型[/TD][TD]說明[/TD][/TR][TR][TD]Socket[/TD][TD]TClientWinSocket[/TD][TD]同TServerSocket[/TD][/TR][TR][TD]Active[/TD][TD]bool[/TD][TD]同TServerSocket[/TD][/TR][TR][TD]Address[/TD][TD]AnsiString[/TD][TD]服務器的IP地址,如202.98.35.14[/TD][/TR][TR][TD]ClientType[/TD][TD]TClientType[/TD][TD]與服務器連接方式,取值為兩個枚舉常量ctNonBlocking,tBlocking。ctNonBlocking表示非阻塞方式,ctBlocking表示阻塞方式,詳見上例。[/TD][/TR][TR][TD]Host[/TD][TD]AnsiString[/TD][TD]要連接的主機名,如www.cpcw.com[/TD][/TR][TR][TD]Port[/TD][TD]int[/TD][TD]同TServerSocket[/TD][/TR][TR][TD]Service[/TD][TD]AnsiString[/TD][TD]同TServerSocket[/TD][/TR][TR][TD]OnConnect[/TD][TD]事件[/TD][TD]當連接時發生,OnConnecting、OnDisConnect與此類似[/TD][/TR][TR][TD]OnRead[/TD][TD]事件[/TD][TD]通知客戶機去讀取有關信息。OnWrite與此類似。[/TD][/TR][/TABLE]
TServerSocket和TClientSocket只提供基本的服務器/客戶機的連接,真正提供數據傳輸的是它們都有的屬性Socket,它的類型分別是TServerWinSocket和TClientWinSocket,而TServerWinSocket和TClientWinSocket的父類都是TCustomWinSocket,下面我們就來看看TServerWinSocket和TClientWinSocket常用的屬性和方法。[H5]●共同的屬性方法(來源于TCustomWinSocket)[/H5][TABLE][TR][TD]名稱[/TD][TD]類型[/TD][TD]說明[/TD][/TR][TR][TD]Connected[/TD][TD]bool[/TD][TD]檢查是否連接成功[/TD][/TR][TR][TD]LocalAddress[/TD][TD]AnsiString[/TD][TD]本地IP地址,與此類似LocalHost:本機域名,LocalPort:本機端口[/TD][/TR][TR][TD]RemoteAddress[/TD][TD]AnsiString[/TD][TD]另一端的IP地址,與此類似RemoteHost:另一端域名,RemotePort:另一端端口[/TD][/TR][TR][TD]SocketHandle[/TD][TD]int[/TD][TD]只讀,返回Socket對象的Windows句柄,如果你要調用WinSocket API函數,將用到此句柄。[/TD][/TR][TR][TD]Handle[/TD][TD]HWND[/TD][TD]Socket能夠接受到的異步事件都是以Windows消息的形式發送給此句柄的。[/TD][/TR][TR][TD]Close()[/TD][TD]方法[/TD][TD]作為服務器,關閉所有連接;作為客戶機,關閉自己與服務器的連接[/TD][/TR][TR][TD]SendText(AnsiString)[/TD][TD]方法[/TD][TD]發送一個字符串,[/TD][/TR][TR][TD]SendBuf(void* buff,int count)[/TD][TD]發送緩沖區buff中的count個字節,返回實際發送的字節數[/TD][/TR][TR][TD]SendStream(TStream* AStream)[/TD][TD]發送一個流到Socket中。[/TD][/TR][TR][TD]ReceiveText()[/TD][TD]從Socket中讀取并返回一個字符串。[/TD][/TR][TR][TD]ReceiveLength()[/TD][TD]從Socket讀取數據需多少字節的緩沖區。[/TD][/TR][TR][TD]ReceiveBuf(void* buff,int count)[/TD][TD]從Socket中讀取count字節的數據到buff。[/TD][/TR][/TABLE][H5]●TClientWinSocket[/H5]
TClientWinSocket只增加了一個ClientType屬性,用于決定與服務器的連接類型(參見
TClientSocket->ClientType)。[H5]●TServerWinSocket[/H5][TABLE][TR][TD]名稱[/TD][TD]類型[/TD][TD]說明[/TD][/TR][TR][TD]ServerType[/TD][TD]?/td> [TD]服務類型,參見TServerSocket->ServerType。[/TD][/TR][TR][TD]ActiveConnection[/TD][TD]int[/TD][TD]只讀,返回當前活動的連接數。[/TD][/TR][TR][TD]Connection[/TD][TD]TCustomWinSocket[/TD][TD]數組,索引n表示第n+1個連接,如Connection[0]表示第一個連接。[/TD][/TR][/TABLE]
有了這些知識,我們就可以完成一些基本的WinSocket編程了,下面就結合一個簡單的閑聊程序來看看具體的應用。
首先在窗體上放置以下VCL組件,并修改相應屬性:[TABLE][TR][TD]類型[/TD][TD]Name屬性[/TD][TD]Caption/Text[/TD][TD]說明[/TD][/TR][TR][TD]TCheckBox[/TD][TD]ckListen[/TD][TD]監聽[/TD][TD]當選取時,本程序作為服務器[/TD][/TR][TR][TD]TCheckBox[/TD][TD]ckConnect[/TD][TD]連接[/TD][TD]當選取時,本程序作為客戶機[/TD][/TR][TR][TD]TEdit[/TD][TD]edName[/TD][TD]無名氏[/TD][TD]閑聊時所用的名字。[/TD][/TR][TR][TD]TBitBtn[/TD][TD]bbtSave[/TD][TD]&S保存[/TD][TD]單擊時保存談話內容[/TD][/TR][TR][TD]TBitBtn[/TD][TD]bbtClose[/TD][TD]&C關閉[/TD][TD]單擊時關閉此窗口(設置Kind=bkClose)[/TD][/TR][TR][TD]TEdit[/TD][TD]edTalk[/TD][TD]?/td> [TD]在此輸入談話內容[/TD][/TR][TR][TD]TMemo[/TD][TD]mmTalk[/TD][TD]?/td> [TD]在此顯示談話內容[/TD][/TR][TR][TD]TServerSocket[/TD][TD]ServerSocket1[/TD][TD]?/td> [TD]作服務器時使用(設置Port=2222)[/TD][/TR][TR][TD]TClientSocket[/TD][TD]ClientSocket1[/TD][TD]?/td> [TD]作為客戶時使用(設置Port=2222)[/TD][/TR][TR][TD]TSaveDialog[/TD][TD]sdTalk[/TD][TD]?/td> [TD]保存文件時的選項(設置DefaultExt="*.txt",Filter=文本文件(*.TXT)|*.txt|所有文件(*.*)|*.*)。[/TD][/TR][TR][TD]TStatusBar[/TD][TD]StatusBar1[/TD][TD]?/td> [TD]用于顯示一些提示信息,只要在屬性"Pannels"中加一欄即可[/TD][/TR][/TABLE]
然后調整布局如下圖所示:
http://go.163.com/~cbuilder/use/27_0.gif[H5]程序作為服務器的設置:[/H5]
當單擊"監聽"時,如果沒有監聽則開始監聽,在提示欄中顯示"監聽",并把"連接"這個復選框無效。如果已經監聽,則取消監聽,并使"連接"這個復選框有效。所以,在ckListen的on_click事件中加入以下代碼:[PRE] if(ServerSocket1->Active) { ServerSocket1->Active=false; ckListen->Checked=false; StatusBar1->Panels->Items[0]->Text=""; } else { ServerSocket1->Active=true; ckListen->Checked=true; ClientSocket1->Active=false; StatusBar1->Panels->Items[0]->Text="監聽..." ; } ckConnect->Enabled=!(ckListen->Checked);
當有客戶加入時,向所有的客戶發出通知:并在自已的mmTalk加入此消息:所以在ServerSocket1的OnAccept事件中加入如下代碼:[/PRE][PRE] int i; AnsiString str1="服務器消息:"+Socket->RemoteHost+"加入"; for(i=0;iSocket->ActiveConnections;i++) ServerSocket1->Socket->Connections[i]->SendText("服務器消息:"+Socket->RemoteHost+"加入"); StatusBar1->Panels->Items[0]->Text=str1; mmTalk->Lines->Add(str1);
當客戶機通知服務器讀信息時,首先讀出字符串,然后把讀到的字符串發送到每一臺連接的客戶,并在自已的mmTalk中加入客戶發送來的字符串。所以,在TServerSocket的OnClientRead事件中加入以下代碼:[/PRE][PRE] AnsiString str1=Socket->ReceiveText(); mmTalk->Lines->Add(str1); int i; for(i=0;iSocket->ActiveConnections;i++) ServerSocket1->Socket->Connections[i]->SendText(str1); [/PRE][H5]程序作為客戶機的設置:[/H5][PRE]
當單擊"連接"時,如果還未連接,則詢問要連接的主機,然后連接之,屏蔽"監聽";如果已經連接,則斷開連接。"監聽"使能。所以,在ckConnect的on_click事件中加入以下代碼: [/PRE][PRE] if(ClientSocket1->Active) { ClientSocket1->Active=false; ckConnect->Checked =false; } else { AnsiString Server="localhost"; if(InputQuery("連接","請輸入要連接的主機地址:",Server)) { ClientSocket1->Host=Server; ClientSocket1->Active=true; ckConnect->Checked =true; } } ckListen->Enabled= !(ckConnect->Checked);
當連接服務器成功時,在狀態欄中顯示此信息,所以,在ClientSocket1的ClientSocket1Connect中加入:[/PRE][PRE] StatusBar1->Panels->Items[0]->Text ="連接到主機:"+Socket->RemoteHost;
當服務器發送字符串來時,把它加入mmTalk中,但如果本字符串就是自已發送的(因為服務器會把收到的消息發給每一客戶),為條信息就是重復的,所以,要比較mmTalk中最后兩條信息是否相同,如果相同,則刪除重復信息。代碼如下:[/PRE][PRE] mmTalk->Lines->Add(Socket->ReceiveText()); int i=mmTalk->Lines->Count-1; if(mmTalk->Lines->Strings[i]==mmTalk->Lines->Strings[i-1]) mmTalk->Lines->Delete(i); [/PRE][H5]公用部分[/H5][PRE]
當在edTalk輸入交談內容,按回車鍵表示輸入完成,此時把交談內容發送出去并清除edTalk的內容。在發送信息時,要看本程序是作為服務器還是客戶機,如果是服務器則把信息發送到每一個客戶;如果是作為客戶則把信息發送到服務器。代碼如下:[/PRE][PRE] if(Key==13) { mmTalk->Lines->Add(edName->Text+":"+edTalk->Text); if(ckListen->Enabled&&ckConnect->Enabled==false) //"監聽"有效,"連接"無效。表示是服務器 { int i; for(i=0;iSocket->ActiveConnections;i++) ServerSocket1->Socket->Connections[i]->SendText(edName->Text+":"+edTalk->Text); } else { ClientSocket1->Socket->SendText(edName->Text+":"+edTalk->Text); } edTalk->Text=""; }
mmTalk的內容不可能永遠增加,所以當它有100行時就清空它,在mmTalk的OnChange事件中檢查:[/PRE][PRE] if(mmTalk->Lines->Count>=100)mmTalk->Lines->Clear();
當然你也可以雙擊mmTalk來清空它,在mmTalk的OnDblClick事件中加入:[/PRE][PRE] mmTalk->Lines->Clear();
當你覺得談話的內容很有意思,你可以單擊bbtSave打開保存對話框設置保存特性,所以在bbtSave的on_click中加入代碼:[/PRE][PRE] if(sdTalk->Execute()) mmTalk->Lines->SaveToFile(sdTalk->FileName);
OK,我們的閑聊程序就完成了[/PRE]
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -