使用MFC類庫實現(xiàn)回聲程序
Zhangtaolmq 2010/04/16
程序名稱:回聲程序
程序功能:客戶端把數(shù)據(jù)發(fā)送給服務(wù)端,服務(wù)端收到數(shù)據(jù)后,立即將數(shù)據(jù)原樣返回給客戶端。
一. 客戶端的創(chuàng)建
1. 使用[MFC AppWizard (exe)]創(chuàng)建一個[Dialog Based]項目:CSockClient。
2. 設(shè)計對話框
去掉Ok和Cancle兩個按鈕,增加ID_Connect(連接)、ID_Send(發(fā)送)、ID_Exit(關(guān)閉)按鈕,增加ListBox控件IDC_LISTMSG和Edit控件IDC_EDITMSG,并按下表在ClassWizard中為CCSockClientDlg類添加變量。
3. CAsyncSocket類用DoCallBack函數(shù)處理MFC消息,當(dāng)一個網(wǎng)絡(luò)事件發(fā)生時,DoCallBack函數(shù)按網(wǎng)絡(luò)事件類型:FD_READ、FD_WRITE、FD_ACCEPT、FD_CONNECT分別調(diào)用OnReceive、OnSend、OnAccept、OnConnect函數(shù)。由于MFC把這些事件處理函數(shù)定義為虛函數(shù),所以要生成一個新的C++類,以重載這些函數(shù)
網(wǎng)絡(luò)事件
|
FD_READ
|
FD_WRITE
|
FD_ACCEPT
|
FD_CONNECT
|
回調(diào)函數(shù)
|
OnReceive
|
OnSend
|
OnAccept
|
OnConnect
|
做法如下:
l 以Public方式繼承CAsyncSocket類,生成新類MySock;
l 為MySock類添加虛函數(shù)OnReceive、OnConnect、OnSend
4. 在[MySock.ccp]中添加以下代碼
#include "CSockClient.h"
#include "CSockClientDlg.h"
5. 在[MySock.h]中添加以下代碼
public:
BOOL m_bConnected; //是否連接
UINT m_nLength; //消息長度
char m_szBuffer[4096]; //消息緩沖區(qū)
6. 在[MySock.ccp]中重載各函數(shù)
MySock::MySock()
{
m_nLength=0;
memset(m_szBuffer,0,sizeof(m_szBuffer));
m_bConnected=FALSE;
}
MySock::~MySock()
{
//關(guān)閉套接字
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));
//繼續(xù)提請一個“讀”的網(wǎng)絡(luò)事件,接收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);
///提請一個“讀”的網(wǎng)絡(luò)事件,準(zhǔn)備接收
AsyncSelect(FD_READ); /
}
CAsyncSocket::OnConnect(nErrorCode);
}
7. 新建對話框IDD_Addr,用來輸入IP地址和Port;生成新類CAddrDlg。增加兩個Edit控件:IDC_Addr、IDC_Port按下表在ClassWizard中為CAddrDlg類添加變量。
8. 在[CSockClientDlg.ccp]中添加代碼
#include "AddrDlg.h"
protected:
int TryCount;
MySock m_clientSocket;
UINT m_szPort;
public:
char m_szServerAdr[256];
9. 雙擊[IDD_CSOCKCLIENT_DIALOG]對話框中的“連接”按鈕,添加以下代碼
void CCSockClientDlg::OnConnect()
{
m_clientSocket.ShutDown(2);
m_clientSocket.m_hSocket=INVALID_SOCKET;
m_clientSocket.m_bConnected=FALSE;
CAddrDlg m_Dlg;
//默認端口1088
m_Dlg.m_Port=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;
//建立計時器,每1秒嘗試連接一次,直到連上或TryCount>10
SetTimer(1,1000,NULL);
TryCount=0;
}
}
10. 添加Windows消息WM_TIMER響應(yīng)函數(shù)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);
}
11. 雙擊IDD_CSOCKCLIENT_DIALOG對話框中的“發(fā)送”按鈕,添加以下代碼
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("");
}
}
12. 雙擊[IDD_CSOCKCLIENT_DIALOG]對話框中的“[關(guān)閉]”按鈕,添加以下代碼
void CCSockClientDlg::OnExit()
{
//關(guān)閉Socket
m_clientSocket.ShutDown(2);
//關(guān)閉對話框
EndDialog(0);
}
13.運行此項目,連接時輸入主機名或IP均可,CAsyncSocket類會自動處理。
二. 服務(wù)端的創(chuàng)建
Server端的編程與Client端的類似,下面主要介紹他的Listen及Accept函數(shù)
1. 建立一個CNewSocket類,重載CAsyncSocket類的OnReceive、OnSend函數(shù),如何進行信息的顯示和發(fā)送可以參考Client程序。本例中采用將收到信息原封不動發(fā)回的方法來實現(xiàn)Echo功能,代碼如下
CNewSocket::OnReceive(int nErrorCOde)
{
m_nLength=Receive(m_szBuffer,sizeof(m_szBuffer),0);
// 直接轉(zhuǎn)發(fā)消息
AsyncSelect(FD_WRITE);
}
CNewSocket::OnSend(int nErrorCode)
{
Send(m_szBuffer,m_nLength,0);
}
2. 建立一個CMyServerSocket類,重載CAsyncSocket類的OnAccept函數(shù)代碼如下
在MyServerSocket.h中聲明變量
public::
CNewSocket* m_pSocket;
void CMyServerSocket::OnAccept(int nErrorCode)
{
//偵聽到連接請求,調(diào)用Accept函數(shù)
CNewSocket* pSocket = new CNewSocket();
if (Accept(*pSocket))
{
pSocket- >AsyncSelect(FD_READ);
m_pSocket=pSocket;
}
else
delete pSocket;
}
3. 為對話框添加一個“偵聽”按鈕,添加如下代碼
在CsockServerDlg.ccp中聲明變量
public:
CMyServerSocket m_srvrSocket;
void CCSockServerDlg::OnListen()
{
if (m_srvrSocket.m_hSocket==INVALID_SOCKET)
{
BOOL bFlag=m_srvrSocket.Create(UserPor,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;
}
}
}
三 測試
注意:
1. 在InitInstance()中加上
if (!AfxSocketInit())
{
AfxMessageBox("AfxSocketInit() failed");
return FALSE;
}
2. 在[stdafx.h]中添加#include <afxsock.h>
zhangtaolmq