明輝手游網(wǎng)中心:是一個免費提供流行視頻軟件教程、在線學習分享的學習平臺!

用WinSock控件編寫網(wǎng)絡(luò)聊天器

[摘要]一.認識C++Builder中的WinSock控件及其相關(guān)類  WinSock是一組用C語言寫的API,用于通過Internet進行數(shù)據(jù)傳輸。通過WinSock編程可以獲得更大的靈活性。編寫WinSock應用程序本來是很麻煩的,不過,在C++ Builder 5.0中,您并不需要直接與WinSoc...
一.認識C++Builder中的WinSock控件及其相關(guān)類

  WinSock是一組用C語言寫的API,用于通過Internet進行數(shù)據(jù)傳輸。通過WinSock編程可以獲得更大的靈活性。編寫WinSock應用程序本來是很麻煩的,不過,在C++ Builder 5.0中,您并不需要直接與WinSock中的API打交道,因為C++ Builder 5.0新增加了TClientSocket控件和TserverSocket控件,這兩個控件封裝了Windows的有關(guān)API,使得對WinSock的訪問大大簡化。用Socket 建立的連接是建立在TCP/IP協(xié)議基礎(chǔ)上的,同時也支持其它相關(guān)的協(xié)議,如XNS、DECnet以及 IPX/SPX等。Socket的連接必須要建立有一個服務器端(Server)和一個客戶端(Client)。在C++ Builder 5.0中分別用TClientSocket控件和TServerSocket控件來操縱客戶端Socket與服務器端Socket的 連接和通信。這兩個控件用于管理服務器和客戶的連接,它們本身并不是Socket對象,操縱 Socket對象的是TCustomWinSocket及其派生類,如TClientWinSocket、TserverWinSocket . TServerClientWinSocket等。

  Socket之間的連接可以分為三種類型:客戶端連接、監(jiān)聽連接以及 服務器端連接,所謂客戶端連接,是指由客戶端的Socket提出連接請求,要連接的目標是服務 器端的Socket。為此,客戶端的Socket必須首先描述它要連接的服務器端Socket(主要是指服務器 端Socket的地址和端口號),然后再定位所要連接的服務器端Socket,找到以后,就向服務器端 Socket請求連接。當然,服務器端的Socket此時未必正好處于準備好狀態(tài),不過,服務器端的 Socket會自動維護客戶請求連接的隊列,然后在它認為合適的時候向客戶端Socket發(fā)出“允許連接” (Accept)的信號,這時客戶端Socket與服務器端Socket的連接就建立了。所謂監(jiān)聽連接,服務器端 Socket并不定位具體的客戶端Socket,而是處于等待連接的狀態(tài)。當服務器端Socket監(jiān)聽到或者說 接收到客戶端Socket的連接請求,它就響應客戶端Socket的請求建立一個新的Socket句柄并與客戶 端連接,而服務器端Socket繼續(xù)處于監(jiān)聽狀態(tài),還可以接收其它客戶端Socket的連接請求。所謂服 務器端連接,是指當服務器端Socket接收到客戶端Socket的連接請求后,就把服務器端Socket的描述 發(fā)給客戶端,一旦客戶端確認了此描述,連接就建立了。在本文中的聊天程序用的就是監(jiān)聽連接, 即服務器設(shè)置連接個數(shù)后進行監(jiān)聽,客戶端進行對服務器端的連接,這樣就可以進行相互通信了。

二.TServerSocket和TClientSocket控件的屬性

1.ServerSocket的控件屬性

threadcachsize:創(chuàng)建服務器線程的最在數(shù)目。
port:確定服務器的監(jiān)視端口。
service:客戶通過此屬性來識別服務器端口。

2.ClientSocket的控件屬性

Socket:此屬性參數(shù)是應用程序之間通信的端點。
Address:此屬性參數(shù)為字符串類型,客戶端確定服務器端的IP地址。
Host:服務器端的主機名稱。
Post:服務器端的監(jiān)視端口。
Servce:用來識別服務器端口。
Active:確定Socket是否可用(true表示可用)。
ClientType:指定客戶機采用哪一種方式(異步/同步)來通信。

三.ServerSocket和ClientSocket控件的事件

1.ServerSocket的事件
onclientconnect:客戶與服務器連接且服務器接收申請后,產(chǎn)生此事件。
onclientdisconnect:當和服務器連接的某一個客戶機關(guān)閉連接后產(chǎn)生此事件。
onGetSocket:一個服務器可以接收多個客戶Socket的連接申請。
onGetThread:當ClientType屬性值設(shè)為StrThreadBlocking時,服務器會產(chǎn)生一個單獨的線程來與客戶的連接。
onAccept:服務器接收客戶的連接申請后,產(chǎn)生此事件。
onClientRead:客戶機發(fā)送數(shù)據(jù)到服務器時產(chǎn)生的事件。

2.ClentSocket事件
onConnect:當客戶端與服務器端連接上后,產(chǎn)生此事件。
onConnecing:當客戶端與服務器端進行連接操作時,產(chǎn)生此事件。
onDisconnect:當客戶端關(guān)閉操作后產(chǎn)生此事件。
onError:在客戶與服務器在建立和通信過程中,如果產(chǎn)生錯誤時,產(chǎn)生此事件。
onLookup:當客戶在計算機網(wǎng)絡(luò)中尋找服務器時,產(chǎn)生此事件。
onRead:數(shù)據(jù)到達時產(chǎn)生此事件。

四.ServerSocket和ClientSocket的方法

1.Open方法
  此方法適用于ServerSocket和CilentSocket進行建立連接,原型如下: void-Fastcall open(void);

2.Close方法
  此方法適用于ServerSocket和CilentSocket進行關(guān)閉連接,原型如下: void-Fastcall close(void);

五.編寫聊天程序

  打開C++Builder 5.0新建一個工程,新建一個Form1窗體,在Form1窗體中添加以下控件:

ClientSocket控件:
1個 ServerSocket控件:
1 個 Button控件:
4個 Label控件:
2個 Memo控件:
1個 Edit控件:
2個 TreeView控件:
1個 StatusBar控件:
1個 添加控件

各控件屬設(shè)置如下:
Form1窗體:Caption="網(wǎng)絡(luò)聊天器".
ServerSocket: Name=ServerSocket1;port=10000;ServerType=stNonBlocking;ThreadCacheSize=10. ClientSocket: Name=ClientSocket1;port=10000;ClientType=stNonBlocking. Mome:Name=Mome1;ScrollBars=ssVertical. TreeView:Name=TreeView1; Button:
第一個:Name=Btnlisten;Caption="監(jiān)聽";
第二個:Name=Btnconnect;Caption="連接";
第三個:Name=Btndisconnect;Caption="斷開";
第一個:Name=BtnExit;Caption="退出"; Label:
第一個:Name=Label1;Caption="發(fā)送";
第二個: Name=Label2;Caption="在線客戶". Edit:
第一個:Name=Edit1;
第二個: Name=Edit2; StatusBar:Name=StatusBar1;

設(shè)置好以上屬性值,就可以進行代碼的編寫了,源程序代碼如下:

//-------------------------- //"Unit1.h"的源程序 //-------------------
#ifndef Unit1H
#define Unit1H
//--------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ScktComp.hpp>
#include <ExtCtrls.hpp>
#include <ComCtrls.hpp>
#include <Menus.hpp>
#include <ToolWin.hpp>
//---------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TClientSocket *ClientSocket1;
TServerSocket *ServerSocket1;
TMemo *Memo1;
TStatusBar *StatusBar1;
TEdit *Edit1;
TLabel *Label1;
TTreeView *TreeView1;
TLabel *Label2;
TEdit *Edit2;
TButton *Btnlisten;
TButton *Btnconnect;
TButton *Btndisconnect;
TButton *BtnExit;
void __fastcall FormCreate(TObject *Sender);
void __fastcall ClientSocket1Connect(TObject *Sender,
TCustomWinSocket *Socket);
void __fastcall ServerSocket1Accept(TObject *Sender,
TCustomWinSocket *Socket);
void __fastcall ServerSocket1ClientDisconnect(TObject *Sender,
TCustomWinSocket *Socket);
void __fastcall ClientSocket1Disconnect(TObject *Sender,
TCustomWinSocket *Socket);
void __fastcall ClientSocket1Error(TObject *Sender,
TCustomWinSocket *Socket, TErrorEvent ErrorEvent,
int &ErrorCode);
void __fastcall ClientSocket1Read(TObject *Sender,
TCustomWinSocket *Socket);
void __fastcall ServerSocket1ClientRead(TObject *Sender,
TCustomWinSocket *Socket);
void __fastcall Edit1KeyDown(TObject *Sender, WORD &Key,
TShiftState Shift);
void __fastcall ClientSocket1Lookup(TObject *Sender,
TCustomWinSocket *Socket);
void __fastcall TreeView1Change(TObject *Sender, TTreeNode *Node);
void __fastcall BtnlistenClick(TObject *Sender);
void __fastcall BtnconnectClick(TObject *Sender);
void __fastcall BtndisconnectClick(TObject *Sender);
void __fastcall BtnExitClick(TObject *Sender);
private:
bool IsServer;
String Server;
// User declarations
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//-------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//-------------------------------------------------------------------------
#endif //"Unit1.cpp"源程序 //-----------------------------------------------
#include <vcl.h>
#include <stdio.h>
#pragma hdrstop #include "Unit1.h"
//--------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
IsServer=false;
Server="";
}
//--------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
Btndisconnect->Enabled=false;
} //------------------------------------------------------------------------

//當用戶提出連接請求后,客戶端會觸發(fā)OnCreate事件
void __fastcall TForm1::ClientSocket1Connect(TObject *Sender,
TCustomWinSocket *Socket)
{
StatusBar1->SimpleText="連接到:"+Server;
TreeView1->Items->Add(TreeView1->Selected ,Server);
Memo1->Lines->Clear();
//定義mouse的類型
Form1->Cursor=crDefault ;
Edit1->Cursor=crDefault;
Memo1->Cursor=crDefault;
//產(chǎn)生一個新的監(jiān)聽 }
//-------------------------------------------------------------------------
//在服務器接受了客戶的請求后會觸發(fā)OnAccept事件
void __fastcall TForm1::ServerSocket1Accept(TObject *Sender,
TCustomWinSocket *Socket)
{ Memo1->Lines->Clear();
IsServer=true;
StatusBar1->SimpleText="連接到:"+Socket->LocalHost;
TreeView1->Items->Add(TreeView1->Selected ,Socket->LocalHost);
}
//-------------------------------------------------------------------------
//斷開后繼續(xù)監(jiān)聽
void __fastcall TForm1::ServerSocket1ClientDisconnect(TObject *Sender,
TCustomWinSocket *Socket)
{
StatusBar1->SimpleText="正在監(jiān)聽...";
}
//-------------------------------------------------------------------------
//客戶端關(guān)閉連接后產(chǎn)生的事件
void __fastcall TForm1::ClientSocket1Disconnect(TObject *Sender,
TCustomWinSocket *Socket)
{
Btnlisten->Enabled=true;
Btnconnect->Enabled=true;
Btndisconnect->Enabled=false;
StatusBar1->SimpleText="";
}
//-------------------------------------------------------------------------
// 通信過程中產(chǎn)生錯誤時產(chǎn)生的事件
void __fastcall TForm1::ClientSocket1Error(TObject *Sender,
TCustomWinSocket *Socket, TErrorEvent ErrorEvent, int &ErrorCode)
{
ShowMessage("錯誤!!! 無法連接到服務器");
ErrorCode=0;
Btnlisten->Enabled=true;
Btnconnect->Enabled=true;
Btndisconnect->Enabled=false;
StatusBar1->SimpleText="";
//定義mouse的類型
Form1->Cursor=crDefault ;
Edit1->Cursor=crDefault;
Memo1->Cursor=crDefault;
Form1->Caption ="網(wǎng)絡(luò)聊天器";
}
//-------------------------------------------------------------------------
//客戶端接收數(shù)據(jù)
void __fastcall TForm1::ClientSocket1Read(TObject *Sender,
TCustomWinSocket *Socket)
{
Memo1->Lines->Add(Socket->ReceiveText());
}
//---------------------------------------------------------------------------
//服務器端接收數(shù)據(jù)
void __fastcall TForm1::ServerSocket1ClientRead(TObject *Sender,
TCustomWinSocket *Socket)
{
Memo1->Lines->Add(Socket->ReceiveText());
}
//-------------------------------------------------------------------------

//在建立連接后,雙方就可以在Edit1中輸入談話內(nèi)容開始進
//行交談了,按下Enter鍵后,將所在行的文本發(fā)送出去
void __fastcall TForm1::Edit1KeyDown(TObject *Sender, WORD &Key,
TShiftState Shift)
{
AnsiString Data;
if(Key==VK_RETURN)
{if (Edit2->Text!="") //在沒有選擇發(fā)送的主機名時不能進行發(fā)送操作
{if(IsServer) //服務器端發(fā)出的數(shù)據(jù)
{Data= "["+TimeToStr(Now())+"]:("+ServerSocket1->Socket->LocalHost+")對"+"("+Edit2->Text+")"+"說:"+Edit1->Text;
ServerSocket1->Socket->Connections[TreeView1->Selected->Index]->SendText(Data);
Memo1->Lines->Add(Data);
Edit1->Text="";}
else //客戶端發(fā)出的數(shù)據(jù)
{ Data="["+TimeToStr(Now())+"]:("+ClientSocket1->Socket->LocalHost+")對"+"("+Edit2->Text
+")"+"說:"+Edit1->Text;
ClientSocket1->Socket->SendText(Data);
Memo1->Lines->Add(Data);
Edit1->Text="";}
}
else
ShowMessage("錯誤!!! 沒有選擇發(fā)送的主機名");
}
}
//---------------------------------------------------------------------------
//在網(wǎng)絡(luò)中搜索服務器端時產(chǎn)生的事件
void __fastcall TForm1::ClientSocket1Lookup(TObject *Sender,
TCustomWinSocket *Socket)
{ //定義mouse的類型
Form1->Cursor=crHourGlass ;
Edit1->Cursor=crHourGlass;
Memo1->Cursor=crHourGlass;
}
//選擇發(fā)向數(shù)據(jù)的主機名
void __fastcall TForm1::TreeView1Change(TObject *Sender, TTreeNode *Node)
{
Edit2->Text=TreeView1->Selected->TreeView->Selected->Text;
StatusBar1->SimpleText="連接到:"+TreeView1->Selected->TreeView->Selected->Text;
} //監(jiān)聽
void __fastcall TForm1::BtnlistenClick(TObject *Sender)
{
ClientSocket1->Active=false;
ServerSocket1->Active=true;
StatusBar1->SimpleText="正在監(jiān)聽...";
Form1->Caption =Form1->Caption+"--服務器端";
Btnlisten->Enabled=false;
Btnconnect->Enabled=false;
}
//連接
void __fastcall TForm1::BtnconnectClick(TObject *Sender)
{
if(InputQuery("連接到服務器","輸入服務器地址:",Server))
{
if(Server.Length() >0){
ClientSocket1->Host=Server; //確定服務器的主機名
ClientSocket1->Active=true;
Btnlisten->Enabled=false;
Btnconnect->Enabled=false;
Btndisconnect->Enabled=true;
Form1->Caption =Form1->Caption+"--客戶端";}
}
}
//斷開
void __fastcall TForm1::BtndisconnectClick(TObject *Sender)
{
//按下斷開
ClientSocket1->Close();
}
//---------------------------------------------------------------------------
//退出
void __fastcall TForm1::BtnExitClick(TObject *Sender)
{
ClientSocket1->Close();
ServerSocket1->Close();
Form1->Close();
}
//編寫完程序代碼后,就要對源程序進行編譯了,編譯方法如下:

1.在菜單 project \ options 下,選擇Packages 頁,去掉Build with runtime packages 項的勾, 然后選擇Linker 頁,去掉 Use dynamic RTL 的勾,然后按“確定”按鈕。
2.在菜單 project \ options 下,選擇 Compiler 頁,按下 Release 按鈕,然后按“確定”按鈕。
3.在菜單 Run \ Run (或F9) 進行編譯就行。 用這種方法編譯的可執(zhí)行文件容量比較小, 而且可以在沒有安裝C++的系統(tǒng)中運行。在運行時 可單機也可多機操作,但必須要有一個主機打開程序的“監(jiān)聽”,其它客戶機進行連接就行了。 快快來下載,編寫自己的聊天程序吧!