在網絡編程中,WinSocket API編程是最基本,也是最麻煩的地方(說句不怕影響形象的話,我對此就是隻知其一;不知其二)。可是,若是你是使用C++Builder做爲編程平臺,你就偷着樂吧,有了BCB,菜鳥變高手!:-)
在BCB中,TServerSocket和TClientSocket涵蓋了基本的WinSocket編程,其中TServerSocket做爲服務器方使用,TClientSocket做爲客戶端使用,這兩個組件自己並不提供Socket鏈接,可是他們都有一個Socket屬性,這個屬性才提供了Socket鏈接。下面就先向你們介紹一下這兩個組件經常使用的方法屬性,而後在經過一個例子來看看這兩個組件的使用。
1)TServerSocket
名稱 類型 說明
Socket TServerWinSocket 最重要的屬性,提供Socket鏈接,事實上發送/接收數據都要靠這個屬性.
Port int 要監聽的端口,若是在Service屬性中指定了服務類型,此屬性將被忽略.
Service AnsiString 提供的服務,如HTTP、FTP等,若是在這裏指定了服務類型,
Port將被忽略,由於各類服務都有特定的端口,如FTP:2一、HTTP:80
ServerType TServerType 設置與客戶鏈接的方式,取值爲兩個枚舉常量stNonBlocking和
stThreadBlocking,stNonBlocking表示用非阻塞方式鏈接每個客戶
每一個鏈接都在一個單獨的線程中處理。並用OnClientRead()和
OnClientWrite()通知服務器端的Socker進行讀寫。stThreadBlocking
表示以阻塞方式鏈接客戶,即以主動查詢的方式可客戶鏈接。
Active bool 激活服務,至關於調用Open()方法。
OnAccept事件當有客戶請求鏈接時觸發
OnClientRead事件通知服務器去讀取有關信息。OnClientWrite與此相似。
2)TClientSocket
名稱 類型 說明
Socket TClientWinSocket 最重要的屬性,提供Socket鏈接,事實上發送/接收數據都要靠這個屬性.
Active bool 激活服務,至關於調用Open()方法。
Address AnsiString 服務器的IP地址,如202.98.35.14
ClientType TClientType 與服務器鏈接方式,取值爲兩個枚舉常量ctNonBlocking,tBlocking。
ctNonBlocking表示非阻塞方式,ctBlocking表示阻塞方式,詳見上例。
Host AnsiString 要鏈接的主機名,如www.cpcw.com
Port int 要監聽的端口,TClientSocket,此屬性將被忽略.
Service AnsiString 提供的服務,如HTTP、FTP等,若是在這裏指定了服務類型,
OnConnect事件當鏈接時發生,OnConnecting、OnDisConnect與此相似
OnRead事件通知客戶機去讀取有關信息。OnWrite與此相似。
TServerSocket和TClientSocket只提供基本的服務器/客戶機的鏈接,真正提供數據傳輸的是它們都有的屬性Socket,它的類型分別是TServerWinSocket和TClientWinSocket,而TServerWinSocket和TClientWinSocket的父類都是TCustomWinSocket,下面咱們就來看看TServerWinSocket和TClientWinSocket經常使用的屬性和方法。
共同的屬性方法(來源於TCustomWinSocket)
名稱 類型 說明
Connected bool 檢查是否鏈接成功
LocalAddress AnsiString 本地IP地址,與此相似LocalHost:本機域名,LocalPort:本機端口
RemoteAddress AnsiString 另外一端的IP地址,與此相似RemoteHost:另外一端域名, RemotePort:另外一端端口
SocketHandle int 只讀,返回Socket對象的Windows句柄,調用WinSocket API函數會用到它。
Handle HWND Socket可以接受到的異步事件都是以Windows消息的形式發送給此句柄的。
Close()方法做爲服務器,關閉全部鏈接;做爲客戶機,關閉本身與服務器的鏈接
SendText(AnsiString)方法發送一個字符串,
SendBuf(void* buff,int count)發送緩衝區buff中的count個字節,返回實際發送的字節數
SendStream(TStream* AStream)發送一個流到Socket中。
ReceiveText()從Socket中讀取並返回一個字符串。
ReceiveLength()從Socket讀取數據需多少字節的緩衝區。
ReceiveBuf(void* buff,int count)從Socket中讀取count字節的數據到buff。
TClientWinSocket
TClientWinSocket只增長了一個ClientType屬性,
用於決定與服務器的鏈接類型(參見TClientSocket-> ClientType)。
TServerWinSocket
名稱 類型說明
ServerType 服務類型,參見TServerSocket-> ServerType。
ActiveConnection int只讀,返回當前活動的鏈接數。
Connection TCustomWinSocket數組,索引n表示第n+1個鏈接,如Connection[0]表示第一個鏈接。
有了這些知識,咱們就能夠完成一些基本的WinSocket編程了,下面就結合一個簡單的閒聊程序來看看具體的應用。
首先在窗體上放置如下VCL組件,並修改相應屬性:
類型 Name 屬性 Caption/Text
TCheckBox ckListen 監聽當選取時,本程序做爲服務器
TCheckBox ckConnect 鏈接當選取時,本程序做爲客戶機
TEdit edName 無名氏閒聊時所用的名字。
TBitBtn bbtSave&S 保存單擊時保存談話內容
TBitBtn bbtClose&C 關閉單擊時關閉此窗口(設置Kind=bkClose)
TEdit edTalk 在此輸入談話內容
TMemo mmTalk 在此顯示談話內容
TServerSocket ServerSocket1 做服務器時使用(設置Port=2222)
TClientSocket ClientSocket1 做爲客戶時使用(設置Port=2222)
TSaveDialog sdTalk 保存文件時的選項(設置DefaultExt= "*.txt ",Filter=文本文件(*.TXT) |*.txt|全部文件(*.*)|*.*)。
TStatusBar StatusBar1 用於顯示一些提示信息,只要在屬性 "Pannels "中加一欄便可
程序做爲服務器的設置:
當單擊 "監聽 "時,若是沒有監聽則開始監聽,在提示欄中顯示 "監聽 ",並把 "鏈接 "這個複選框無效。若是已經監聽,則取消監聽,並使 "鏈接 "這個複選框有效。因此,在ckListen的OnClick事件中加入如下代碼:
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事件中加入以下代碼:
int i;
AnsiString str1= "服務器消息: "+Socket-> RemoteHost+ "加入 ";
for(i=0;i <ServerSocket1-> Socket-> ActiveConnections;i++)
ServerSocket1-> Socket-> Connections[i]-> SendText( "服務器消息: "+Socket-> RemoteHost+ "加入 ");
StatusBar1-> Panels-> Items[0]-> Text=str1;
mmTalk-> Lines-> Add(str1);
當客戶機通知服務器讀信息時,首先讀出字符串,而後把讀到的字符串發送到每一臺鏈接的客戶,並在自已的mmTalk中加入客戶發送來的字符串。因此,在TServerSocket的OnClientRead事件中加入如下代碼:
AnsiString str1=Socket-> ReceiveText();
mmTalk-> Lines-> Add(str1);
int i;
for(i=0;i <ServerSocket1-> Socket-> ActiveConnections;i++)
ServerSocket1-> Socket-> Connections[i]-> SendText(str1);
程序做爲客戶機的設置:
當單擊 "鏈接 "時,若是還未鏈接,則詢問要鏈接的主機,而後鏈接之,屏蔽 "監聽 ";若是已經鏈接,則斷開鏈接。 "監聽 "使能。因此,在ckConnect的OnClick事件中加入如下代碼:
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中加入:
StatusBar1-> Panels-> Items[0]-> Text = "鏈接到主機: "+Socket-> RemoteHost;
當服務器發送字符串來時,把它加入mmTalk中,但若是本字符串就是自已發送的(由於服務器會把收到的消息發給每一客戶),爲條信息就是重複的,因此,要比較mmTalk中最後兩條信息是否相同,若是相同,則刪除重複信息。代碼以下:
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);
公用部分
當在edTalk輸入交談內容,按回車鍵表示輸入完成,此時把交談內容發送出去並清除edTalk的內容。在發送信息時,要看本程序是做爲服務器仍是客戶機,若是是服務器則把信息發送到每個客戶;若是是做爲客戶則把信息發送到服務器。代碼以下:
if(Key==13)
{
mmTalk-> Lines-> Add(edName-> Text+ ": "+edTalk-> Text);
if(ckListen-> Enabled&&ckConnect-> Enabled==false)
// "監聽 "有效, "鏈接 "無效。表示是服務器
{
int i;
for(i=0;i <ServerSocket1-> Socket-> 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事件中檢查:
if(mmTalk-> Lines-> Count> =100)mmTalk-> Lines-> Clear();
固然你也能夠雙擊mmTalk來清空它,在mmTalk的OnDblClick事件中加入:
mmTalk-> Lines-> Clear();
當你以爲談話的內容頗有意思,你能夠單擊bbtSave打開保存對話框設置保存特性,因此在bbtSave的onClick中加入代碼:
if(sdTalk-> Execute())
mmTalk-> Lines-> SaveToFile(sdTalk-> FileName);
OK,咱們的閒聊程序就完成了,在局域網中試試吧?若是你只有一臺機器,客戶程序在鏈接時服務器時輸入localhost或你機器的名字就能夠了