3.8 一個使用CSocket類的網絡通訊實例編程

本例採用CSocket編程模型創建一個聊天程序的簡單實例。創建項目時注意選上「Windows套接字」複選框。服務器

3.8.1 服務器端應用程序設計(ServerDemo)網絡

1)界面函數

 

各控件屬性以下post

控件類型this

控件IDspa

Caption屬性設計

控件變量htm

變量類型

列表框

IDC_Log

m_LogCtrl

CListBox

編輯框

IDC_Message

m_MsgCtrl

CEdit

按鈕

IDC_Send

發送

M_SendCtrl

CButton

靜態控件

IDC_STATIC

記錄

靜態控件

IDC_STATIC

待發消息

列表框控件IDC_Log屬性「sort」值爲false,屬性「Horizontal Scroll」值爲true。

按鈕IDC_Send屬性Disable值爲true。

2)定義CSocket類的派生類CServSocket和CRecvSocket。

    從CSocket編程模型知道,服務器端須要兩種套接字,一個用來偵聽鏈接請求,一個用來與請求鏈接的套接字創建鏈接。所以,爲程序添加兩個CSocket派生類:SServSocket和CRecvSocket,它們與對話框類密切配合,共同完成程序所要求實現的功能。

3)創建套接字與對話框類的關聯

在程序中,對話框類要用到套接字類,而套接字類在響應某些消息,如在函數OnAccept、OnReceive中進行處理時,也要改變對話框的某些控件狀態,以反映給用戶這些事情的發生。

這裏存在着兩個類相互使用的狀況,把套接字類對象定義成對話框類的成員變量,同時在套接字類中也把對話框類定義爲成員變量。如何實現這樣的用法呢?在對話框類頭文件中加入套接字頭文件的聲明,而後在套接字類頭文件中加入對話框類頭文件的聲明,這樣的作法顯然行不通。

具體作法應該以下:

首先,在ServerDemoDlg.h中加入套接字類頭文件的聲明,語句#pragma once的後面加入以下語句:

#include "ServSocket.h"

#include "RecvSocket.h"

而後在該文件中爲CServerDemoDlg類增長兩個公有成員變量,語句以下:

CServSocket *ServSock;

CRecvSocket *RecvSock;

這樣在對話框類中就可使用套接字類了。

繼續在套接字類中加入對話框類信息。

首先,在ServSocket.h文件的開頭,語句#pragma once的後面加入以下語句:

class CServerDemoDlg;

而後,在該文件中爲CServSocket類添加一個公有成員變量和一個構造函數:

CServSocket(CServerDemoDlg *Dlg);

CServerDemoDlg *m_Dlg;

接着在ServSocket.cpp文件中添加新的構造函數的實現,並添加一條關於ServerDemoDlg.h文件的預編譯聲明,代碼以下:

#include 「ServerDemoDlg.h」

CServSocket::CServSocket(CServerDemoDlg *Dlg)

{

       m_Dlg=Dlg;

}

這樣,在套接字類中也能夠經過成員變量使用對話框了。

使用一樣的方法對CRecvSocket類進行設置,使其也能夠經過成員變量使用對話框。

4)爲套接字添加串行化讀寫信息的功能。在服務器端的兩個套接字中,只有CRecvSocket套接字是真正與客戶端套接字創建鏈接,發送與接收數據的,所以,咱們只爲該類添加串行化讀寫信息功能。在RecvSocket.h文件中爲類CRecvSocket添加三個公有成員變量。

    CSocketFile *m_File;

       CArchive *m_ArIn;

       CArchive *m_ArOut;

5)在對話框中初始化套接字並偵聽鏈接請求。在OnInitDialog函數中添加以下代碼:

    // TODO: 在此添加額外的初始化代碼

     if(ServSock=new CServSocket(this))

     {

         if(ServSock->Create (9547))

         {

              m_LogCtrl.AddString ("等待鏈接......");

              ServSock->Listen ();

         }

         else

         {

              m_LogCtrl.AddString ("初始化失敗,請從新啓動程序!");

              delete ServSock;

         }

     }

     else

     {

         m_LogCtrl.AddString ("初始化失敗,請從新啓動程序!");

     }

上述代碼主要是建立並初始化ServSock套接字,並開始偵聽鏈接請求。

6)接受鏈接請求。因爲是CServSocket類的ServSock對象在偵聽鏈接請求,所以由該類來接受鏈接請求。

首先,在ServSocket.h文件中加入以下語句:

#iinclude 「RecvSocket.h」

而後,重載該類的OnAccept函數,在該函數中添加以下代碼:

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 ("客戶端當前的鏈接嘗試失敗");

              delete tempSock;

         }

     }

     else

     {

         m_Dlg->m_LogCtrl .AddString ("鏈接套接字初始化失敗");

     }

上述代碼首先調用Accept函數接受鏈接請求,而後爲該鏈接建立一個CRecvSocket類型的套接字,併爲該套接字關聯CArchive對象,使其能實現串行化傳輸信息的功能。最後把關聯好的套接字傳回給對話框對象供其使用。這樣,對話框對象的成員變量RecvSock套接字便與客戶端套接字之間創建了一條信息通道,信息將在兩個套接字之間傳遞。

7)接收信息,鏈接創建成功後,當有信息到達服務器端時,就會引起RecvSock套接字對象的OnReceive函數,所以須要重載CRecvSocket類的OnReceive函數。添加代碼以下:

CString str;

(*m_ArIn)>>str;

m_Dlg->m_LogCtrl .AddString ("對方發來的信息以下:");

    m_Dlg->m_LogCtrl .AddString (str);

m_Dlg->m_LogCtrl .SetCurSel (m_Dlg->m_LogCtrl .GetCount() - 1);

8)發送信息。爲對話框「發送」按鈕添加事件處理函數OnBnClickedSend(),代碼以下:

void CServerDemoDlg::OnBnClickedSend()

{

     // TODO: 在此添加控件通知處理程序代碼

     CString str;

     m_MsgCtrl.GetWindowText (str);

     if(str.GetLength ()==0)

         AfxMessageBox("空信息,因此不發出");

     else

     {

         m_LogCtrl.AddString ("你發出的信息以下:");

         m_LogCtrl.AddString (str);

         m_LogCtrl.SetCurSel (m_LogCtrl.GetCount ()-1);

          *(RecvSock->m_ArOut )<<str;

         RecvSock->m_ArOut ->Flush ();

     }

}

3.8.2 客戶端應用程序設計(項目名稱ClientDemo)

1)界面

 

各控件屬性以下

控件類型

控件ID

Caption屬性

控件變量

變量類型

列表框

IDC_Log

m_LogCtrl

CListBox

編輯框

IDC_Message

m_MsgCtrl

CEdit

按鈕

IDC_Send

發送

M_SendCtrl

CButton

靜態控件

IDC_STATIC

記錄

靜態控件

IDC_STATIC

待發消息

列表框控件IDC_Log屬性「sort」值爲false,屬性「Horizontal Scroll」值爲true。

按鈕IDC_Send屬性Disable值爲true。

2)建立套接字類(從CSocket類派生)。客戶端只須要一個套接字,命名爲CClientSocket。

3)創建對話框類與套接字類的關聯。

首先,在ClientDemoDlg.h文件的開頭,語句#pragma once後面加入以下語句:

#include 「ClientSocket.h」

而後,在該文件中爲CClientDemoDlg類添加一個公有成員變量,語句以下:

CClientSocket *ClientSock;

接着,在ClientSocket.h文件的開頭,語句#pragma once後面加入以下語句:

class CClientDemoDlg;

而後,在該文件中爲CClientSocket類添加一公有成員變量和一個構造函數,語句以下:

CClientSocket(CClientDemoDlg *Dlg);

CClientDemoDlg *m_Dlg;

接着,在ClientSocket.cpp文件中添加新的構造函數的實現代碼,並添加一條關於CClientDemoDlg.h文件的預編譯聲明,代碼以下:

#include "ClientDemoDlg.h"

CClientSocket::CClientSocket(CClientDemoDlg *Dlg)

{

         m_Dlg=Dlg;

}

這樣,便完成了對話框和套接字之間的鏈接了。

4)爲套接字添加串行化讀寫信息的功能。在ClientSocket.h文件中,爲類CClientSocket添加三個公有成員變量,代碼以下:

CSocketFile *m_File;

CArchive *m_ArIn;

CArchive *m_ArOut;

5)在對話框中初始化套接字並創建鏈接

在對話框類的OnInitDialog函數中添加以下代碼

// 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 ("初始化失敗,請從新啓動程序");

              delete ClientSock;

         }

     }

     else

     {

         m_LogCtrl.AddString ("初始化失敗,請從新啓動程序");

     }

6)接收消息。消息到來時,會引起套接字的OnReceive消息,所以要重載CClientSocket類的OnReceive函數,在其中添加代碼以下

// TODO: 在此添加專用代碼和/或調用基類

    CString str;

     m_Dlg->m_LogCtrl .AddString ("對方發來消息以下:");

     *m_ArIn>>str;

     m_Dlg->m_LogCtrl .AddString (str);

     m_Dlg->m_LogCtrl .SetCurSel (m_Dlg->m_LogCtrl .GetCount ()-1);

7)發送信息。爲對話框「發送」按鈕添加事件處理函數OnBnClickedSend(),代碼以下:

    void CClientDemoDlg::OnBnClickedSend()

{

     // TODO: 在此添加控件通知處理程序代碼

     CString str;

     m_MsgCtrl.GetWindowText (str);

     if(str.GetLength ()==0)

         AfxMessageBox("空信息,因此不發出");

     else

     {

         m_LogCtrl.AddString ("你發的信息以下:");

         m_LogCtrl.AddString (str);

         m_LogCtrl.SetCurSel (m_LogCtrl.GetCount ()-1);

         *(ClientSock->m_ArOut )<<str;

         ClientSock->m_ArOut ->Flush ();

     }

}

 

#  re: 一個使用CSocket類的網絡通訊實例
服務器程序按照您的步驟編譯好後
if(ServSock=new CServSocket(this))
{
if(ServSock->Create (9547))
{
m_LogCtrl.AddString ("等待鏈接......");
ServSock->Listen ();
}
else
{
m_LogCtrl.AddString ("初始化失敗,請從新啓動程序!");
delete ServSock;
}
}
create失敗,老是跳到else裏面去了,請問是什麼緣由啊?若是方便的話,能夠把源程序發我郵箱號碼,我本身研究!zqxwce111@163.com
#  re: 一個使用CSocket類的網絡通訊實例
加入  if (!AfxSocketInit())  {  AfxMessageBox(IDP_SOCKETS_INIT_FAILED);  return FALSE;  }  在資源頭文件中加入 #define IDP_SOCKETS_INIT_FAILED 104  還要在string table中加入  ID IDP_SOCKETS_INIT_FAILED Caption Windows 通訊端口初始化失敗。