3.8 一個(gè)使用CSocket類的網(wǎng)絡(luò)通信實(shí)例
本例采用CSocket編程模型建立一個(gè)聊天程序的簡(jiǎn)單實(shí)例。建立項(xiàng)目時(shí)注意選上“Windows套接字”復(fù)選框。
3.8.1 服務(wù)器端應(yīng)用程序設(shè)計(jì)(ServerDemo)
1)界面

各控件屬性如下
控件類型
|
控件ID
|
Caption屬性
|
控件變量
|
變量類型
|
列表框
|
IDC_Log
|
無(wú)
|
m_LogCtrl
|
CListBox
|
編輯框
|
IDC_Message
|
無(wú)
|
m_MsgCtrl
|
CEdit
|
按鈕
|
IDC_Send
|
發(fā)送
|
M_SendCtrl
|
CButton
|
靜態(tài)控件
|
IDC_STATIC
|
記錄
|
無(wú)
|
無(wú)
|
靜態(tài)控件
|
IDC_STATIC
|
待發(fā)消息
|
無(wú)
|
無(wú)
|
列表框控件IDC_Log屬性“sort”值為false,屬性“Horizontal Scroll”值為true。
按鈕IDC_Send屬性Disable值為true。
2)定義CSocket類的派生類CServSocket和CRecvSocket。
從CSocket編程模型知道,服務(wù)器端需要兩種套接字,一個(gè)用來偵聽連接請(qǐng)求,一個(gè)用來與請(qǐng)求連接的套接字建立連接。因此,為程序添加兩個(gè)CSocket派生類:SServSocket和CRecvSocket,它們與對(duì)話框類密切配合,共同完成程序所要求實(shí)現(xiàn)的功能。
3)建立套接字與對(duì)話框類的關(guān)聯(lián)
在程序中,對(duì)話框類要用到套接字類,而套接字類在響應(yīng)某些消息,如在函數(shù)OnAccept、OnReceive中進(jìn)行處理時(shí),也要改變對(duì)話框的某些控件狀態(tài),以反映給用戶這些事情的發(fā)生。
這里存在著兩個(gè)類相互使用的情況,把套接字類對(duì)象定義成對(duì)話框類的成員變量,同時(shí)在套接字類中也把對(duì)話框類定義為成員變量。如何實(shí)現(xiàn)這樣的用法呢?在對(duì)話框類頭文件中加入套接字頭文件的聲明,然后在套接字類頭文件中加入對(duì)話框類頭文件的聲明,這樣的做法顯然行不通。
具體做法應(yīng)該如下:
首先,在ServerDemoDlg.h中加入套接字類頭文件的聲明,語(yǔ)句#pragma once的后面加入如下語(yǔ)句:
#include "ServSocket.h"
#include "RecvSocket.h"
然后在該文件中為CServerDemoDlg類增加兩個(gè)公有成員變量,語(yǔ)句如下:
CServSocket *ServSock;
CRecvSocket *RecvSock;
這樣在對(duì)話框類中就可以使用套接字類了。
繼續(xù)在套接字類中加入對(duì)話框類信息。
首先,在ServSocket.h文件的開頭,語(yǔ)句#pragma once的后面加入如下語(yǔ)句:
class CServerDemoDlg;
然后,在該文件中為CServSocket類添加一個(gè)公有成員變量和一個(gè)構(gòu)造函數(shù):
CServSocket(CServerDemoDlg *Dlg);
CServerDemoDlg *m_Dlg;
接著在ServSocket.cpp文件中添加新的構(gòu)造函數(shù)的實(shí)現(xiàn),并添加一條關(guān)于ServerDemoDlg.h文件的預(yù)編譯聲明,代碼如下:
#include “ServerDemoDlg.h”
CServSocket::CServSocket(CServerDemoDlg *Dlg)
{
m_Dlg=Dlg;
}
這樣,在套接字類中也可以通過成員變量使用對(duì)話框了。
使用同樣的方法對(duì)CRecvSocket類進(jìn)行設(shè)置,使其也可以通過成員變量使用對(duì)話框。
4)為套接字添加串行化讀寫信息的功能。在服務(wù)器端的兩個(gè)套接字中,只有CRecvSocket套接字是真正與客戶端套接字建立連接,發(fā)送與接收數(shù)據(jù)的,因此,我們只為該類添加串行化讀寫信息功能。在RecvSocket.h文件中為類CRecvSocket添加三個(gè)公有成員變量。
CSocketFile *m_File;
CArchive *m_ArIn;
CArchive *m_ArOut;
5)在對(duì)話框中初始化套接字并偵聽連接請(qǐng)求。在OnInitDialog函數(shù)中添加如下代碼:
// TODO: 在此添加額外的初始化代碼
if(ServSock=new CServSocket(this))
{
if(ServSock->Create (9547))
{
m_LogCtrl.AddString ("等待連接......");
ServSock->Listen ();
}
else
{
m_LogCtrl.AddString ("初始化失敗,請(qǐng)重新啟動(dòng)程序!");
delete ServSock;
}
}
else
{
m_LogCtrl.AddString ("初始化失敗,請(qǐng)重新啟動(dòng)程序!");
}
上述代碼主要是創(chuàng)建并初始化ServSock套接字,并開始偵聽連接請(qǐng)求。
6)接受連接請(qǐng)求。由于是CServSocket類的ServSock對(duì)象在偵聽連接請(qǐng)求,因此由該類來接受連接請(qǐng)求。
首先,在ServSocket.h文件中加入如下語(yǔ)句:
#iinclude “RecvSocket.h”
然后,重載該類的OnAccept函數(shù),在該函數(shù)中添加如下代碼:
CRecvSocket *tempSock;
if(tempSock=new CRecvSocket(this->m_Dlg ))
{
if(Accept(*tempSock))
{
tempSock->m_File =new CSocketFile(tempSock);
tempSock->m_ArIn =new CArchive(tempSock->m_File ,CArchive::load );
tempSock->m_ArOut =new CArchive(tempSock->m_File ,CArchive::store );
m_Dlg->RecvSock =tempSock;
tempSock=NULL;
m_Dlg->m_LogCtrl .AddString ("連接成功,可以開始傳遞消息");
m_Dlg->m_SendCtrl.EnableWindow (true);
}
else
{
m_Dlg->m_LogCtrl .AddString ("客戶端當(dāng)前的連接嘗試失敗");
delete tempSock;
}
}
else
{
m_Dlg->m_LogCtrl .AddString ("連接套接字初始化失敗");
}
上述代碼首先調(diào)用Accept函數(shù)接受連接請(qǐng)求,然后為該連接創(chuàng)建一個(gè)CRecvSocket類型的套接字,并為該套接字關(guān)聯(lián)CArchive對(duì)象,使其能實(shí)現(xiàn)串行化傳輸信息的功能。最后把關(guān)聯(lián)好的套接字傳回給對(duì)話框?qū)ο蠊┢涫褂谩_@樣,對(duì)話框?qū)ο蟮某蓡T變量RecvSock套接字便與客戶端套接字之間建立了一條信息通道,信息將在兩個(gè)套接字之間傳遞。
7)接收信息,連接建立成功后,當(dāng)有信息到達(dá)服務(wù)器端時(shí),就會(huì)引發(fā)RecvSock套接字對(duì)象的OnReceive函數(shù),因此需要重載CRecvSocket類的OnReceive函數(shù)。添加代碼如下:
CString str;
(*m_ArIn)>>str;
m_Dlg->m_LogCtrl .AddString ("對(duì)方發(fā)來的信息如下:");
m_Dlg->m_LogCtrl .AddString (str);
m_Dlg->m_LogCtrl .SetCurSel (m_Dlg->m_LogCtrl .GetCount() - 1);
8)發(fā)送信息。為對(duì)話框“發(fā)送”按鈕添加事件處理函數(shù)OnBnClickedSend(),代碼如下:
void CServerDemoDlg::OnBnClickedSend()
{
// TODO: 在此添加控件通知處理程序代碼
CString str;
m_MsgCtrl.GetWindowText (str);
if(str.GetLength ()==0)
AfxMessageBox("空信息,所以不發(fā)出");
else
{
m_LogCtrl.AddString ("你發(fā)出的信息如下:");
m_LogCtrl.AddString (str);
m_LogCtrl.SetCurSel (m_LogCtrl.GetCount ()-1);
*(RecvSock->m_ArOut )<<str;
RecvSock->m_ArOut ->Flush ();
}
}
3.8.2 客戶端應(yīng)用程序設(shè)計(jì)(項(xiàng)目名稱ClientDemo)
1)界面
各控件屬性如下
控件類型
|
控件ID
|
Caption屬性
|
控件變量
|
變量類型
|
列表框
|
IDC_Log
|
無(wú)
|
m_LogCtrl
|
CListBox
|
編輯框
|
IDC_Message
|
無(wú)
|
m_MsgCtrl
|
CEdit
|
按鈕
|
IDC_Send
|
發(fā)送
|
M_SendCtrl
|
CButton
|
靜態(tài)控件
|
IDC_STATIC
|
記錄
|
無(wú)
|
無(wú)
|
靜態(tài)控件
|
IDC_STATIC
|
待發(fā)消息
|
無(wú)
|
無(wú)
|
列表框控件IDC_Log屬性“sort”值為false,屬性“Horizontal Scroll”值為true。
按鈕IDC_Send屬性Disable值為true。
2)創(chuàng)建套接字類(從CSocket類派生)。客戶端只需要一個(gè)套接字,命名為CClientSocket。
3)建立對(duì)話框類與套接字類的關(guān)聯(lián)。
首先,在ClientDemoDlg.h文件的開頭,語(yǔ)句#pragma once后面加入如下語(yǔ)句:
#include “ClientSocket.h”
然后,在該文件中為CClientDemoDlg類添加一個(gè)公有成員變量,語(yǔ)句如下:
CClientSocket *ClientSock;
接著,在ClientSocket.h文件的開頭,語(yǔ)句#pragma once后面加入如下語(yǔ)句:
class CClientDemoDlg;
然后,在該文件中為CClientSocket類添加一公有成員變量和一個(gè)構(gòu)造函數(shù),語(yǔ)句如下:
CClientSocket(CClientDemoDlg *Dlg);
CClientDemoDlg *m_Dlg;
接著,在ClientSocket.cpp文件中添加新的構(gòu)造函數(shù)的實(shí)現(xiàn)代碼,并添加一條關(guān)于CClientDemoDlg.h文件的預(yù)編譯聲明,代碼如下:
#include "ClientDemoDlg.h"
CClientSocket::CClientSocket(CClientDemoDlg *Dlg)
{
m_Dlg=Dlg;
}
這樣,便完成了對(duì)話框和套接字之間的連接了。
4)為套接字添加串行化讀寫信息的功能。在ClientSocket.h文件中,為類CClientSocket添加三個(gè)公有成員變量,代碼如下:
CSocketFile *m_File;
CArchive *m_ArIn;
CArchive *m_ArOut;
5)在對(duì)話框中初始化套接字并建立連接
在對(duì)話框類的OnInitDialog函數(shù)中添加如下代碼
// TODO: 在此添加額外的初始化代碼
m_LogCtrl.AddString ("正在連接......");
if(ClientSock=new CClientSocket(this))
{
if(ClientSock->Create())
{
if(ClientSock->Connect ("localhost",9547))
{
ClientSock->m_File =new CSocketFile(ClientSock);
ClientSock->m_ArIn =new CArchive(ClientSock->m_File ,CArchive::load );
ClientSock->m_ArOut =new CArchive(ClientSock->m_File,CArchive::store );
m_LogCtrl.AddString ("連接成功,可以開始傳遞消息");
m_SendCtrl.EnableWindow (true);
}
else
{
m_LogCtrl.AddString ("連接不成功");
delete ClientSock;
}
}
else
{
m_LogCtrl.AddString ("初始化失敗,請(qǐng)重新啟動(dòng)程序");
delete ClientSock;
}
}
else
{
m_LogCtrl.AddString ("初始化失敗,請(qǐng)重新啟動(dòng)程序");
}
6)接收消息。消息到來時(shí),會(huì)引發(fā)套接字的OnReceive消息,因此要重載CClientSocket類的OnReceive函數(shù),在其中添加代碼如下
// TODO: 在此添加專用代碼和/或調(diào)用基類
CString str;
m_Dlg->m_LogCtrl .AddString ("對(duì)方發(fā)來消息如下:");
*m_ArIn>>str;
m_Dlg->m_LogCtrl .AddString (str);
m_Dlg->m_LogCtrl .SetCurSel (m_Dlg->m_LogCtrl .GetCount ()-1);
7)發(fā)送信息。為對(duì)話框“發(fā)送”按鈕添加事件處理函數(shù)OnBnClickedSend(),代碼如下:
void CClientDemoDlg::OnBnClickedSend()
{
// TODO: 在此添加控件通知處理程序代碼
CString str;
m_MsgCtrl.GetWindowText (str);
if(str.GetLength ()==0)
AfxMessageBox("空信息,所以不發(fā)出");
else
{
m_LogCtrl.AddString ("你發(fā)的信息如下:");
m_LogCtrl.AddString (str);
m_LogCtrl.SetCurSel (m_LogCtrl.GetCount ()-1);
*(ClientSock->m_ArOut )<<str;
ClientSock->m_ArOut ->Flush ();
}
}