CSocket派生于CAsyncSocket, 所有施諸于上的操作皆為同步操作。比如Connnect,Receive等。
同步操作的優(yōu)點(diǎn)是簡單易用,但缺點(diǎn)也顯而易見,效率低下,因?yàn)槟惚仨毜鹊揭粋€(gè)操作完成之后才能進(jìn)行下一個(gè)操作。
如果你很關(guān)心效率,就應(yīng)該優(yōu)先使用CAsyncSocket。反之就用CSocket。
下面將說明如何用CSocket創(chuàng)建簡單的服務(wù)器和客戶端。
[創(chuàng)建服務(wù)器]
服務(wù)器的運(yùn)作有5個(gè)階段:
1. 創(chuàng)建服務(wù)器Socket并開啟監(jiān)聽。
2. 獲取新的客戶端連接Socket,將之加入客戶端Socket列表以管理之。
3. 客戶端Socket讀取數(shù)據(jù)并發(fā)送數(shù)據(jù)。
4. 客戶端連接被動(dòng)關(guān)閉,從列表刪除。
5. 程序關(guān)閉,進(jìn)而服務(wù)器連接主動(dòng)關(guān)閉。
為了維持5階段的運(yùn)作,需要兩種Socket協(xié)同工作, 第一種用作服務(wù)器監(jiān)聽(負(fù)責(zé)步驟1,2,5),第二種用作客戶端管理(負(fù)責(zé)步驟3,4)。
兩種Socket皆派生自CSocket, 通過重寫不同的CSocket成員函數(shù)以實(shí)現(xiàn)不同的功能。
前者需要在服務(wù)器初始化階段創(chuàng)建出來CSocket::Create()并開啟監(jiān)聽CSocket::Listen()(步驟1)。并在服務(wù)器退出時(shí)主動(dòng)關(guān)閉連接CSocket::Close()(步驟5)。
前者還需要重寫OnAccept以在新的客戶端連接到來時(shí)被通知,同時(shí)產(chǎn)生客戶端管理Socket(步驟2)。
后者需要重寫OnReceive以在有數(shù)據(jù)到來時(shí)被通知,或重寫OnClose以在連接被動(dòng)關(guān)閉(客戶端關(guān)閉)時(shí)被通知(步驟3,4)。
讀寫數(shù)據(jù)需要CSocketFile以及CArchieve的支持。前者將CSocket當(dāng)作一個(gè)文件,后者則完成在此文件上的讀寫操作。
通常你需要添加一個(gè)CSocketFile成員,兩個(gè)CArchieve成員(一個(gè)用于讀,一個(gè)用于寫),然后在Socket創(chuàng)建完成后初始化這些成員
socketFile_ = new CSocketFile( this ); // 在archive創(chuàng)建出來后基本上就不需要操作他了,直到Socket關(guān)閉
archiveIn_ = new CArchive( socketFile_, CArchive::load ); // 用于讀
archiveOut_ = new CArchive( socketFile_, CArchive::store ); // 用于取
并在OnRecevie中用archiveIn_讀取數(shù)據(jù),用archiveOut_寫入數(shù)據(jù)。像這樣:
int value;
archiveIn_ >> value;
archiveOut_ << value * value;
下面是比較完整的Server端的源代碼:
//---------------------------------------------------------------------------------
// CServerDoc.cpp
//---------------------------------------------------------------------------------
BOOL CServerDoc::OnNewDocument()
{
...
serverSocket_ = new CServerSocket( this );
serverSocket_->Create( 5001 );
serverSocket_->Listen( 5 );
return TRUE;
}
void CServerDoc::DeleteContents()
{
// TODO: Add your specialized code here and/or call the base class
delete serverSocket_;
serverSocket_ = NULL;
// 主動(dòng)斷開連接
// release all client sockets
POSITION position = clientSockets_.GetHeadPosition();
while ( position != NULL ) {
delete clientSockets_.GetNext( position );
}
clientSockets_.RemoveAll();
CDocument::DeleteContents();
}
void CServerDoc::OnAccept()
{
CClientSocket* newClientSocket = new CClientSocket( this );
serverSocket_->Accept( *newClientSocket );
newClientSocket->Initialize();
clientSockets_.AddTail( newClientSocket );
}
// 被動(dòng)斷開連接
void CServerDoc::OnClose( CClientSocket* clientSocket )
{
POSITION position = clientSockets_.Find( clientSocket );
clientSockets_.RemoveAt( position );
delete clientSocket;
}
void CServerDoc::OnReceive( CClientSocket* clientSocket )
{
// receive data with clientSocket
}
//---------------------------------------------------------------------------------
// CServerSocket.cpp
//---------------------------------------------------------------------------------
CServerSocket::CServerSocket( CServerDoc* document )
: document_( document )
{
}
CServerSocket::~CServerSocket()
{
}
void CServerSocket::OnAccept(int nErrorCode)
{
// TODO: Add your specialized code here and/or call the base class
document_->OnAccept();
CSocket::OnAccept(nErrorCode);
}
//---------------------------------------------------------------------------------
// CClientSocket.cpp
//---------------------------------------------------------------------------------
CClientSocket::CClientSocket( CServerDoc* document )
: document_( document )
, socketFile_( NULL )
, archiveIn_( NULL )
, archiveOut_( NULL )
{
}
CClientSocket::~CClientSocket()
{
delete archiveIn_;
archiveIn_ = NULL;
delete archiveOut_;
archiveOut_ = NULL;
// 必須在刪除archive以后刪除
delete socketFile_;
socketFile_ = NULL;
}
void CClientSocket::OnClose(int nErrorCode)
{
// TODO: Add your specialized code here and/or call the base class
CSocket::OnClose(nErrorCode);
document_->OnClose( this ); // 一定要在最后一行
}
void CClientSocket::OnReceive(int nErrorCode)
{
// TODO: Add your specialized code here and/or call the base class
document_->OnReceive( this );
CSocket::OnReceive(nErrorCode);
}
BOOL CClientSocket::Initialize()
{
socketFile_ = new CSocketFile( this );
archiveIn_ = new CArchive( socketFile_, CArchive::load );
archiveOut_ = new CArchive( socketFile_, CArchive::store );
return TRUE;
}
[創(chuàng)建客戶端]
客戶端的運(yùn)作比服務(wù)器簡單
1. 創(chuàng)建客戶端Socket并連接到服務(wù)器。 CSocket::Create() -> CSocket::Connect()
2. 客戶端Socket讀取數(shù)據(jù)并發(fā)送數(shù)據(jù)。 CSocket::OnReceive()
3. 客戶端連接被動(dòng)關(guān)閉。 CSocket::OnClose()
4. 程序關(guān)閉,進(jìn)而客戶端連接主動(dòng)關(guān)閉。CSocket::Close()
下面是比較完整的Client端的源代碼:
//---------------------------------------------------------------------------------
// CClientDlg.cpp
//---------------------------------------------------------------------------------
CClientDlg::CClientDlg(CWnd* pParent /*=NULL*/)
: CDialog(CClientDlg::IDD, pParent)
, socket_( NULL )
{
...
}
void CClientDlg::OnDestroy()
{
CDialog::OnDestroy();
// TODO: Add your message handler code here
// 主動(dòng)斷開連接
delete socket_;
socket_ = NULL;
}
void CClientDlg::OnBnClickedConnnect()
{
// TODO: Add your control notification handler code here
UpdateData( TRUE );
socket_ = new CClientSocket( this );
socket_->Create();
if ( !socket_->Connect( "127.0.0.1", 5001 ) ) {
delete socket_;
socket_ = NULL;
MessageBox( _T( "連接失敗" ) );
return;
}
socket_->Initialize();
}
// 主動(dòng)斷開連接
void CClientDlg::OnBnClickedDisconnect()
{
// TODO: Add your control notification handler code here
delete socket_;
socket_ = NULL;
}
// 被動(dòng)斷開連接
void CClientDlg::OnClose()
{
delete socket_;
socket_ = NULL;
MessageBox( _T("服務(wù)器斷開") );
}
void CClientDlg::OnReceive()
{
// receive data with socket_
}
//---------------------------------------------------------------------------------
// CClientSocket.cpp
//---------------------------------------------------------------------------------
CClientSocket::CClientSocket( CClientDlg* dialog )
: dialog_( dialog )
, socketFile_( NULL )
, archiveIn_( NULL )
, archiveOut_( NULL )
{
}
CClientSocket::~CClientSocket()
{
delete archiveIn_;
archiveIn_ = NULL;
delete archiveOut_;
archiveOut_ = NULL;
delete socketFile_;
socketFile_ = NULL;
}
void CClientSocket::OnClose(int nErrorCode)
{
// TODO: Add your specialized code here and/or call the base class
CSocket::OnClose(nErrorCode);
dialog_->OnClose();
}
void CClientSocket::OnReceive(int nErrorCode)
{
// TODO: Add your specialized code here and/or call the base class
dialog_->OnReceive();
CSocket::OnReceive(nErrorCode);
}
BOOL CClientSocket::Initialize()
{
socketFile_ = new CSocketFile( this );
archiveIn_ = new CArchive( socketFile_, CArchive::load );
archiveOut_ = new CArchive( socketFile_, CArchive::store );
return TRUE;
}