使用MFC類庫實現回聲程序

Zhangtaolmq  2010/04/16

 

程序名稱:回聲程序

程序功能:客戶端把數據發送給服務端,服務端收到數據后,立即將數據原樣返回給客戶端。

 

一. 客戶端的創建

 

1 使用[MFC AppWizard (exe)]創建一個[Dialog Based]項目:CSockClient

2 設計對話框

 去掉OkCancle兩個按鈕,增加ID_Connect(連接)、ID_Send(發送)、ID_Exit(關閉)按鈕,增加ListBox控件IDC_LISTMSGEdit控件IDC_EDITMSG,并按下表在ClassWizard中為CCSockClientDlg類添加變量。

 

 

3 CAsyncSocket類用DoCallBack函數處理MFC消息,當一個網絡事件發生時,DoCallBack函數按網絡事件類型:FD_READFD_WRITEFD_ACCEPTFD_CONNECT分別調用OnReceiveOnSendOnAcceptOnConnect函數。由于MFC把這些事件處理函數定義為虛函數,所以要生成一個新的C++類,以重載這些函數

網絡事件

FD_READ

FD_WRITE

FD_ACCEPT

FD_CONNECT

回調函數

OnReceive

OnSend

OnAccept

OnConnect

 

做法如下:

l  Public方式繼承CAsyncSocket類,生成新類MySock

l  MySock類添加虛函數OnReceiveOnConnectOnSend

 

4 [MySock.ccp]中添加以下代碼

#include "CSockClient.h"

#include "CSockClientDlg.h"

 

5 [MySock.h]中添加以下代碼

public:

      BOOL m_bConnected;    //是否連接

      UINT m_nLength;        //消息長度

      char m_szBuffer[4096];    //消息緩沖區

 

6 [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);

}

 

7 新建對話框IDD_Addr,用來輸入IP地址和Port;生成新類CAddrDlg。增加兩個Edit控件:IDC_AddrIDC_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響應函數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對話框中的“發送”按鈕,添加以下代碼

 

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]對話框中的“[關閉]”按鈕,添加以下代碼

 

void CCSockClientDlg::OnExit()

{

//關閉Socket

m_clientSocket.ShutDown(2);

//關閉對話框

EndDialog(0);

}

 

13.運行此項目,連接時輸入主機名或IP均可,CAsyncSocket類會自動處理。

 

 

二. 服務端的創建

 

Server端的編程與Client端的類似,下面主要介紹他的ListenAccept函數

1 建立一個CNewSocket類,重載CAsyncSocket類的OnReceiveOnSend函數,如何進行信息的顯示和發送可以參考Client程序。本例中采用將收到信息原封不動發回的方法來實現Echo功能,代碼如下

CNewSocket::OnReceiveint nErrorCOde

{

m_nLength=Receivem_szBuffersizeofm_szBuffer),0);

// 直接轉發消息

AsyncSelectFD_WRITE);

}

 

CNewSocket::OnSendint nErrorCode

{

Sendm_szBufferm_nLength0);

}

 

2 建立一個CMyServerSocket類,重載CAsyncSocket類的OnAccept函數代碼如下

MyServerSocket.h中聲明變量

public:

CNewSocket*    m_pSocket

 

void CMyServerSocket::OnAcceptint nErrorCode

{

//偵聽到連接請求,調用Accept函數

CNewSocket* pSocket = new CNewSocket();

if Accept*pSocket))

{

      pSocket- >AsyncSelectFD_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(UserPorSOCK_STREAMFD_ACCEPT)

      if (!bFlag

      {

       AfxMessageBox(“Socket Error!”);

       M_srvrSocket.Close();

       PostQuitMessage0);

       Return

      }

}

//“偵聽”成功,等待連接請求

if (!m_srvrSocket.Listen1))

{

      int nErrorCode = m_srvrSocket.GetLastError();

      if nError=WSAEWOULDBLOCK

      {

       AfxMessageBox(“Socket Error!”);

       M_srvrSocket.Close();

       PostQuitMessage0);

       Return

      }

}

}

 測試

 

注意:

1. InitInstance()中加上

if (!AfxSocketInit())

       {

              AfxMessageBox("AfxSocketInit() failed");

              return FALSE;

       }

2. [stdafx.h]中添加#include <afxsock.h>      

 

     zhangtaolmq