隨著計算機網絡化的深入,計算機網絡編程在程序設計的過程中變得日益重要。許多文章都曾經介紹過用VC++進行Socket編程的方法。但由於都是直接利用動態連接庫wsock32.dll進行操作,實現比較繁瑣。其實,VC++的MFC類庫中提供了CAsyncSocket這樣一個套接字類,用它來實現Socket編程,是非常方便的。
客戶端
創建一個Dialog Based項目:CSockClient。設計一對話框,增加ID_Connect(連接)、ID_Send(發送)、ID_Exit(關閉)按鈕,增加ListBox控件IDC_LISTMSG和Edit控件IDC_EDITMSG,並按下表在ClassWizard中為CCSockClIEntDlg類添加變量。
Control ID Type Member
IDC_EDITMSG CEdit m_MSG
IDC_LISTMSG ClistBox m_MSGS
在MySock.ccp中添加以下代碼:
#include "CSockClIEnt.h"
#include "CSockClIEntDlg.h"
在MySock.h中添加以下代碼:
public:
BOOL m_bConnected;
UINT m_nLength;
char m_szBuffer[4096];
在MySock.ccp中重載各函數
MySock::MySock()
{ m_nLength=0;
memset(m_szBuffer,0,sizeof(m_szBuffer));
m_bConnected=FALSE;}
MySock::~MySock()
{ //關閉套接字
if(m_hSocket!=INVALID_SOCKET)
Close(); }
void MySock::OnReceive(int nErrorCode)
{ m_nLength=Receive(m_szBuffer,sizeof(m_szBuffer),0);
//下面兩行代碼用來獲取對話框指針
CCSockClientApp* pApp=(CCSockClIEntApp*)AfxGetApp();
CCSockClientDlg* pDlg=(CCSockClIEntDlg*)pApp- >m_pMainWnd;
pDlg- >m_MSGS.InsertString(0,m_szBuffer);
memset(m_szBuffer,0,sizeof(m_szBuffer));
CAsyncSocket::OnReceive(nErrorCode);}
void MySock::OnSend(int nErrorCode)
{ Send(m_szBuffer,m_nLength,0);
m_nLength=0;
memset(m_szBuffer,0,sizeof(m_szBuffer));
//繼續提請一個“讀”的網絡事件,接收Server消息
AsyncSelect(FD_READ);
CAsyncSocket::OnSend(nErrorCode);}
void MySock::OnConnect(int nErrorCode)
{ if (nErrorCode==0)
{ m_bConnected=TRUE;
CCSockClientApp* pApp=(CCSockClIEntApp*)AfxGetApp();
CCSockClientDlg* pDlg=(CCSockClIEntDlg*)pApp- >m_pMainWnd;
memcpy(m_szBuffer,"Connected to ",13);
strncat(m_szBuffer,pDlg- >m_szServerAdr,
sizeof(pDlg- >m_szServerAdr));
pDlg- >m_MSGS.InsertString(0,m_szBuffer);
AsyncSelect(FD_READ);}//提請一個“讀”的網絡事件,准備接收
CAsyncSocket::OnConnect(nErrorCode); }
建一對話框IDD_Addr,用來輸入IP地址和Port;增加兩個Edit控件:IDC_Addr、IDC_Port按下表在ClassWizard中為CAddrDlg類添加變量。
Control ID Type Member
IDC_Addr CString m_Addr
IDC_Port Int m_Port
在CSockClIEntDlg.ccp中添加代碼:
#include "AddrDlg.h"
protected:
int TryCount;
MySock m_clIEntSocket;
UINT m_szPort;
public:
char m_szServerAdr[256];
雙擊IDD_CSOCKCLIENT_DIALOG對話框中的“連接”按鈕,添加以下代碼:
m_clIEntSocket.ShutDown(2);
m_clIEntSocket.m_hSocket=INVALID_SOCKET;
m_clIEntSocket.m_bConnected=FALSE;
CAddrDlg m_Dlg;
m_Dlg.m_Port=1088;//默認端口1088
if (m_Dlg.DoModal()==IDOK && !m_Dlg.m_Addr.IsEmpty())
{ memcpy(m_szServerAdr,m_Dlg.m_Addr,sizeof(m_szServerAdr));
m_szPort=m_Dlg.m_Port;
SetTimer(1,1000,NULL);//建立計時器,每1秒嘗試連接一次
TryCount=0;}
添加Windows消息WM_TIMER響應函數OnTimer
void CCSockClIEntDlg::OnTimer(UINT nIDEvent)
{ if (m_clIEntSocket.m_hSocket==INVALID_SOCKET)
{ BOOL bFlag=m_clIEntSocket.Create(0,SOCK_STREAM,FD_CONNECT);
if(!bFlag)
{ AfxMessageBox("Socket Error!");
m_clIEntSocket.Close();
PostQuitMessage(0);
return;}}
m_clIEntSocket.Connect(m_szServerAdr,m_szPort);
TryCount++;
if (TryCount >=10 || m_clIEntSocket.m_bConnected)
{ KillTimer(1);
if (TryCount >=10)
AfxMessageBox("Connect Failed!");
return;}
CDialog::OnTimer(nIDEvent);}
雙擊IDD_CSOCKCLIENT_DIALOG對話框中的“發送”按鈕,添加以下代碼:
void CCSockClIEntDlg::OnSend()
{if (m_clIEntSocket.m_bConnected)
{ m_clIEntSocket.m_nLength=m_MSG.GetWindowText
(m_clientSocket.m_szBuffer, sizeof(m_clIEntSocket.m_szBuffer));
m_clIEntSocket.AsyncSelect(FD_WRITE);
m_MSG.SetWindowText("");}}
雙擊IDD_CSOCKCLIENT_DIALOG對話框中的“關閉”按鈕,添加以下代碼:
void CCSockClIEntDlg::OnExit()
{ m_clIEntSocket.ShutDown(2); //關閉Socket
EndDialog(0);}
運行此項目,連接時輸入主機名或IP均可,CAsyncSocket類會自動處理。
服務端
Server端的編程與Client端的類似,下面主要介紹他的Listen及Accept函數。建立一個CNewSocket類,重載CAsyncSocket類的OnReceive、OnSend函數,如何進行信息的顯示和發送可以參考ClIEnt程序。本例中采用將收到信息原封不動發回的方法來實現Echo功能,代碼如下:
CNewSocket::OnReceive(int nErrorCOde)
{ m_nLength=Receive(m_szBuffer,sizeof(m_szBuffer),0);
AsyncSelect(FD_WRITE);}// 直接轉發消息
CNewSocket::OnSend(int nErrorCode)
{ Send(m_szBuffer,m_nLength,0);}
建立一個CMyServerSocket類,重載CAsyncSocket類的OnAccept函數代碼如下:
在MyServerSocket.h中聲明變量
public::
CNewSocket* m_pSocket;
void CMyServerSocket::OnAccept(int nErrorCode)
{ CNewSocket* pSocket = new CNewSocket();
if (Accept(*pSocket))
{ pSocket- >AsyncSelect(FD_READ);
m_pSocket=pSocket;}else
delete pSocket;}
為對話框添加一個“偵聽”按鈕,添加如下代碼:
在CsockServerDlg.ccp中聲明變量
public:
CMyServerSocket m_srvrSocket;
void CCSockServerDlg::OnListen()
{ if (m_srvrSocket.m_hSocket==INVALID_SOCKET)
{ BOOL bFlag=m_srvrSocket.Create
(UserPort,SOCK_STREAM,FD_ACCEPT);
if (!bFlag)
{ AfxMessageBox(“Socket Error!”);
M_srvrSocket.Close();
PostQuitMessage(0);
Return;}}
if (!m_srvrSocket。Listen(1))
{ int nErrorCode = m_srvrSocket.GetLastError();
if (nError!=WSAEWOULDBLOCK)
{ AfxMessageBox(“Socket Error!”);
M_srvrSocket.Close();
PostQuitMessage(0);
Return;}}}
CAsyncSocket類為我們使用Socket提供了極大方便。建立Socket的WSAStartup過程和bind過程被簡化成為Create過程,IP地址類型轉換、主機名和IP地址轉換的過程中許多復雜的變量類型都被簡化成字符串和整數操作,特別是CAsyncSocket類的異步特點,完全可以替代繁瑣的線程操作。MFC提供了大量的類庫,我們若能靈活的使用他們,便會大大提高編程的效率。