版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
1、2.12.1 MFCMFC及其SocketSocket類2.22.2 C/SC/S模式下網絡程序的SocketSocket通信實例2.32.3 與第三方程序的互通及Socket編程的本質2.1.1 MFC2.1.1 MFC簡介簡介MFCMFC,微軟基礎類(MicrosoftFoundationClasses),同VCL類似,是一種應用框架(Application Framework),隨微軟Visual C+ 開發(fā)工具發(fā)布。MFC是很龐大的,如最新版本中包含了大約兩百多個不同的類(如圖2.1所示)。1. CAsyncSocket1. CAsyncSocket類類CAsyncSocket類是從M
2、FC的根類CObject派生出來的,它在較低的級別上封裝Windows Socket API。CAsyncSocket類在MFC套接字類中的繼承位置如圖2.2所示。CAsyncSocket類的成員函數(shù)如表2.1所示。2 2CSocketCSocket類及其相關類類及其相關類(1)CSocketCSocket類。類。CSocket類是從CAsyncSocket派生而來的,它繼承了CAsyncSocket對Windows Socket API的封裝。(2)CSocketCSocket與與CArchiveCArchive、CSocketFileCSocketFile類的配合使用。類的配合使用。用CS
3、ocket類編寫網絡程序,既可以使用如CAsyncSocket類網絡程序一樣的Send和Receive函數(shù)來收發(fā)信息,也可以與CSocketFile類和CArchive類一起來管理對數(shù)據(jù)的發(fā)送和接收。2.2.1 2.2.1 客戶客戶服務器方式(服務器方式(C/SC/S模式)模式)1 1網絡軟件的通用體系結構網絡軟件的通用體系結構客戶(Client)和服務器(Server)是指通信中所涉及的兩個應用進程。客戶服務器方式所描述的是進程之間服務和被服務的關系。在圖2.3中,主機A運行客戶程序而主機B運行服務器程序。2 2最簡單的最簡單的SocketSocket通信流程通信流程一個只有客戶方向服務方發(fā)
4、信息的單向通信,并且也只有客戶方會主動提出斷開連接的最簡單的情形(相反過程的原理是一樣的),其雙方Socket之間的關系如圖2.4所示。由上面這個十分簡單的過程很容易得出最簡單的Socket通信流程,如圖2.5所示。1 1對象分析對象分析由2.2.1節(jié)分析的最簡單的Socket通信流程可見,要實現(xiàn)這樣一個完整流程需要三個套接字對象:客戶端一個(我們稱為“客戶Socket”),服務器端兩個(一個用于監(jiān)聽,稱為“監(jiān)聽Socket”;另一個用于接收客戶發(fā)來的信息,稱為“服務Socket”),這三個套接字對象對應三個Socket類,都繼承自CAsyncSocket,分別給它們取名如表2.2所示。Soc
5、ket對象類 名客戶SocketCClientSocket監(jiān)聽SocketCListenSocket服務SocketCServerSocket2 2創(chuàng)建工程和套接字對象創(chuàng)建工程和套接字對象首先創(chuàng)建客戶端工程。打開Visual Studio 2008環(huán)境,建立一個新的基于對話框的MFC項目,項目名稱為ChatClient,接著一直單擊“下一步”按鈕,直到設置程序“高級功能”對話框,如圖2.6所示。創(chuàng)建類名為CClientSocket的客戶Socket對象,下面給工程添加類,選擇菜單命令“項目”“添加類”,如圖2.7所示。在彈出的“添加類”對話框中選擇“MFC類”項,單擊“添加”按鈕(如圖2.8所
6、示)。在“MFC類向導”對話框中輸入類名CClientSocket,向導將自動為這個添加的類生成名為“ClientSocket.h”和“ClientSocket.cpp”的頭文件和源文件,如圖2.9所示。單擊“完成”按鈕,可以在類視圖中看到剛剛添加的類CClientSocket(如圖2.10所示)。3 3理清程序文件的組織結構理清程序文件的組織結構(1)客戶端程序文件。打開已經創(chuàng)建好的客戶端工程,在解決方案資源管理器中可以看到工程的所有程序文件(如圖2.11所示)。全部程序文件可以分成三類:l第一類是.h后綴的頭文件,用來集中聲明定義程序中用到的類、變量、函數(shù)、宏等;l第二類是.cpp后綴的源
7、文件,這是程序代碼的主體,集中存放程序的源代碼;l第三類是資源文件,存放程序中用到的資源,如圖標、圖像、音頻、視頻等,(2)服務器端的源文件。同理,服務器端也對應這幾種程序文件,打開服務器工程,可以看到它們(如圖2.12所示)。4 4用頭文件和類對象將程序源文件聯(lián)成有機整體用頭文件和類對象將程序源文件聯(lián)成有機整體例如,已經創(chuàng)建了各個Socket對象并且也有了它們各自對應的程序文件,但這些文件仍然是孤立的,相互之間的代碼無法訪問,要使它們能聯(lián)系在一起,使控制程序的主文件能夠自如地操縱Socket,就必須通過頭文件聲明頭文件聲明和創(chuàng)建類對象成員創(chuàng)建類對象成員變量變量將各個類聯(lián)成有機的整體。要使客戶
8、端程序能夠創(chuàng)建和控制本地的Socket,在客戶端工程界面控制模塊的頭文件ChatClientDlg.h中添加如下兩行代碼:#include ClientSocket.h /使主界面程序能夠訪問Socket類的代碼文件CClientSocket m_ClientSocket;/為了后面與服務器通信而定義的Socket成員變量以上兩行代碼的添加位置如圖2.13所示中“/ADD”記號之間標出的部分。反過來要使Socket能夠訪問到主界面上的控件,以便能夠將自己的狀況隨時反映給主程序并在主界面上顯示出來,也需要在Socket源文件ClientSocket.cpp中添加頭文件聲明:#include Ch
9、atClientDlg.h同理,要使服務端程序能夠創(chuàng)建和控制本地的Socket,也要在服務端工程界面控制模塊的頭文件ChatServerDlg.h中添加如下代碼:#include ListenSocket.h /使主界面程序能夠訪問監(jiān)聽Socket類的代碼文件#include ServerSocket.h /使主界面程序能夠訪問服務Socket類的代碼文件CServerSocket m_ServerSocket;/為了后面與客戶通信而定義的Socket成員變量CListenSocket m_ListenSocket;/為了監(jiān)聽客戶端的連接請求而定義的Socket成員變量以上四行代碼的添加位置如
10、圖2.14所示中“/ADD”記號之間標出的部分。5 5布局簡潔的界面布局簡潔的界面在客戶端“資源視圖”展開的目錄樹下雙擊Dialog文件夾下第二個項目,轉到用戶界面設計工作區(qū)(如圖2.15所示)。完成的客戶端簡化界面(如圖2.16所示)上,包括IP地址控件、列表框各1個,文本框兩個,四個按鈕。用相同的方法設置服務器端的用戶界面如圖2.17所示,也一樣為IP地址控件關聯(lián)變量ServerIP,為文本框控件關聯(lián)int型變量sPort,為列表框關聯(lián)變量m_ListWords。6 6雛形程序試運行雛形程序試運行到此為止,這個程序的雛形已經形成,分別運行客戶和服務器端的工程如圖2.18所示。7 7程序代碼
11、的組織程序代碼的組織接下來就要為這個程序雛形注入靈性,添加核心源代碼了。在此之前,先來回顧一下前面設計的那個最簡單的Socket通信流程,如圖2.19所示。從上面已經標注了所用函數(shù)的通信流程圖,可以進一步得出下面的源程序代碼組織框圖(如圖2.20所示)。8 8源代碼完全剖析源代碼完全剖析(1)客戶端源碼。客戶端用戶首先主動發(fā)起連接,以下是“連接”按鈕的事件過程,位于ChatClientDlg.cpp文件中:/連接服務器BYTE nFild4;CString sIP;UpdateData();ServerIP.GetAddress(nFild0,nFild1,nFild2,nFild3);sIP
12、.Format(%d.%d.%d.%d,nFild0,nFild1,nFild2,nFild3);m_ClientSocket.Createm_ClientSocket.Create();();/創(chuàng)建客戶端Socketm_ClientSocket.Connectm_ClientSocket.Connect( (sIP,sPortsIP,sPort); );/發(fā)起連接請求Create()函數(shù)用來創(chuàng)建和初始化套接字,具體過程為:構造套接字對象m_ClientSocket后,調用Create()成員函數(shù)創(chuàng)建Socket句柄,并調用Bind()成員函數(shù)將其與指定的地址綁定,Create()函數(shù)原型為:
13、BOOL Create(UINT nSocketport = 0, int nSocketType = SOCK_STREAM, long lEvent = FD_READ FD_WRITEFD_OOBFD_ACCEPTFD_CONNECTFD_CLOSE,LPCTSTR lpszSocketaddress = NULL);Connect()函數(shù)用于未連接的數(shù)據(jù)流或者數(shù)據(jù)報套接字建立連接。其函數(shù)原型為:BOOL Connect(LPCTSTR lpszHostAddress, UINT nHostPort);BOOL Connect(const SOCKADDR*lpSockAddr, int
14、 nSockAddrLen);客戶端也可以隨時主動斷開通信連接,下面是“斷開”按鈕的事件過程:/斷開與服務器的連接m_ClientSocket.Closem_ClientSocket.Close();();/關閉客戶端Socketm_ListWords.AddString(從服務器斷開);Close()函數(shù)用來關閉套接字并釋放Socket描述符,其函數(shù)原型為:virtual void Close();客戶端可以向服務器發(fā)送信息,“發(fā)送”按鈕的事件過程為:/向服務器發(fā)信息UpdateData();m_ClientSocket.Sendm_ClientSocket.Send( (m_sWords,
15、m_sWords.GetLengthm_sWords,m_sWords.GetLength();();/發(fā)信息m_ListWords.AddString(發(fā)送: + m_sWords);m_ListWords.SetTopIndex(m_ListWords.GetCount() - 1);Send()函數(shù)通過數(shù)據(jù)報或者數(shù)據(jù)流向對方套接字發(fā)送數(shù)據(jù),其函數(shù)原型為:virtual int Send(const void* lpBuf, int nBufLen, int nFlags = 0);在類視圖中選中CClientSocket,在界面右下角的屬性窗口中單擊“重寫”按鈕,就可以為該Socket類
16、編寫被動響應網絡事件的函數(shù)(如圖2.21所示)。選擇OnConnect()函數(shù),為其添加代碼,系統(tǒng)將該函數(shù)的代碼自動置于ClientSocket.cpp文件中。OnConnect()函數(shù)代碼如下:/確認客戶端是否成功連接到服務器if(if(nErrorCodenErrorCode) ) AfxMessageBox(連接失敗,請您重試!);return;return; (CChatClientDlg*)(AfxGetApp()-m_pMainWnd)-m_ListWords.AddString(連接服務器成功!);(CChatClientDlg*)(AfxGetApp()-m_pMainWnd)
17、-m_ListWords.SetTopIndex(CChatClientDlg*)(AfxGetApp()-m_pMainWnd)-m_ListWords.GetCount()- 1);(2)服務器端源碼(ChatServerDlg.cpp文件中)?!伴_始監(jiān)聽”按鈕的事件過程如下:/監(jiān)聽開始,服務器等待連接請求的到來BYTE nFild4;CString sIP,sP;UpdateData();ServerIP.GetAddress(nFild0,nFild1,nFild2,nFild3);sIP.Format(%d.%d.%d.%d,nFild0,nFild1,nFild2,nFild3);
18、sP.Format(%d,sPort);m_ListenSocket.Createm_ListenSocket.Create(sPort,1,FD_ACCEPT,sIP);(sPort,1,FD_ACCEPT,sIP);/創(chuàng)建服務端監(jiān)聽Socketm_ListenSocket.Listenm_ListenSocket.Listen(1);(1);/開始監(jiān)聽m_ListWords.AddString(監(jiān)聽開始:);m_ListWords.AddString(地址 + sIP + 端口 + sP);m_ListWords.AddString(等待客戶端連接);Listen()函數(shù)用于偵聽連接請求,
19、原型為:BOOL Listen(int nConnectionBackloh = 5);Accept()函數(shù)接受一個套接字的連接請求,從連接請求隊列中取出第一個連接,創(chuàng)建一個與這個套接字具有相同屬性的套接字,并與參數(shù)rConnectedSocket相關聯(lián),原始的套接字依然保持打開并且偵聽狀態(tài)。函數(shù)原型為:virtual BOOL Accept(CAsyncSocket& rConnectedSocket, SOCKADDR *lpSockAddr = NULL,int* lpSockAddrlen = NULL);表2.3列出了套接字可以偵測到的網絡事件類型。事 件 標 記事 件FD_
20、READ接收讀準備好的通知FD_WRITE接收寫準備好的通知FD_OOB接收帶外數(shù)據(jù)到達的通知FD_ACCEPT接收等待連接成功的通知FD_CONNECT接收已連接好的通知FD_CLOSE接收套接字關閉的通知OnReceive()函數(shù)代碼(ServerSocket.cpp文件中):/接收客戶端發(fā)來的信息char szTemp200;intint n = Receive(szTemp,200); n = Receive(szTemp,200);/接收信息szTempn = 0;CString sTemp;sTemp.Format(收到:%s,szTemp);(CChatServerDlg *)(
21、AfxGetApp()-m_pMainWnd)-m_ListWords.AddString(sTemp);/顯示信息(CChatServerDlg *)(AfxGetApp()-m_pMainWnd)-m_ListWords.SetTopIndex( (CChatServerDlg *)(AfxGetApp()-m_pMainWnd)-m_ListWords.GetCount() - 1);OnClose()代碼:/關閉與客戶端的通信信道(CChatServerDlg *)(AfxGetApp()-m_pMainWnd)-m_ListWords.AddString(客戶端斷開連接);(CCha
22、tServerDlg *)(AfxGetApp()-m_pMainWnd)-m_ListWords.SetTopIndex(CChatServerDlg *)(AfxGetApp()-m_pMainWnd)-m_ListWords.GetCount()-1);Close();Close();/關閉與客戶端通信的Socket9 9運行結果運行結果現(xiàn)在這個程序已經具備了最簡單的單向通信功能,客戶端發(fā)出信息服務器端可以收到,運行結果如圖2.22所示。1010從單向通信到雙向交流從單向通信到雙向交流下面就來添加服務端到客戶端的通信功能。先在服務器界面上添加一個編輯信息的文本框和一個發(fā)送按鈕,如圖2.2
23、3所示。服務器的“發(fā)送”按鈕添加與客戶端的一樣的事件過程。“發(fā)送”按鈕的事件過程代碼(在ChatServerDlg.cpp中)如下:UpdateData();m_ServerSocket.Sendm_ServerSocket.Send( (m_sWords,m_sWords.GetLengthm_sWords,m_sWords.GetLength();();m_ListWords.AddString(發(fā)送: + m_sWords);m_ListWords.SetTopIndex(m_ListWords.GetCount() - 1);是為客戶端添加OnReceive方法,代碼置于ClientS
24、ocket.cpp中。OnReceive()函數(shù)代碼如下:char szTemp200;intint n = Receive(szTemp,200); n = Receive(szTemp,200);szTempn = 0;CString sTemp;sTemp.Format(收到:%s,szTemp);(CChatClientDlg *)(AfxGetApp()-m_pMainWnd)-m_ListWords.AddString(sTemp);(CChatClientDlg *)(AfxGetApp()-m_pMainWnd)-m_ListWords.SetTopIndex( (CChatC
25、lientDlg *)(AfxGetApp()-m_pMainWnd)-m_ListWords.GetCount() - 1);參照客戶端斷開連接的原理,在服務器界面上安放一個“斷開”按鈕(如圖2.24所示)。為這個“斷開”按鈕編寫與客戶端“斷開”按鈕一樣的事件過程代碼:m_ServerSocket.Close();m_ListWords.AddString(與客戶端斷開);下來就是一樣的原理,在客戶端添加響應網絡事件的函數(shù)代碼,為客戶端Socket重寫網絡事件響應函數(shù)OnClose,代碼自動置于ClientSocket.cpp中:(CChatClientDlg *)(AfxGetApp()-
26、m_pMainWnd)-m_ListWords.AddString(服務器端斷開了);(CChatClientDlg *)(AfxGetApp()-m_pMainWnd)-m_ListWords.SetTopIndex(CChatClientDlg *)(AfxGetApp()-m_pMainWnd)-m_ListWords.GetCount()-1);Close();Close();運行后界面如圖2.25所示。1111程序界面的優(yōu)化和控制程序界面的優(yōu)化和控制先在界面上添加一些控件并重新設計布局如圖2.26和圖2.27所示。各控件關聯(lián)的變量見表2.4和表2.5???件變量(包括類型)IP地址控
27、件contrl ServerIP“端口”編輯框contrl ServerPort;int sPort“連接”按鈕contrl m_ButtonConnect“斷開”按鈕contrl m_ButtonDisconnect列表框contrl m_ListWords“清空”按鈕contrl m_ButtonClear發(fā)送信息編輯框contrl m_EditWords;CString m_sWords“發(fā)送”按鈕contrl m_ButtonSend“退出”按鈕contrl m_ButtonExit控 件變量(包括類型)IP地址控件contrl ServerIP“端口”編輯框contrl Server
28、Port;int sPort列表框contrl m_ListWords“斷開”按鈕contrl m_ButtonDisconnect“清空”按鈕contrl m_ButtonClear“退出”按鈕contrl m_ButtonExit“開始監(jiān)聽”按鈕contrl m_ButtonListen“停止監(jiān)聽”按鈕contrl m_ButtonStopListen發(fā)送信息編輯框contrl m_EditWords;CString m_sWords“發(fā)送”按鈕contrl m_ButtonSend截取上面的一小段代碼來舉一個例子讓讀者對使用EnableWindow函數(shù)控制界面的方法有個感性認識。例如:“
29、開始監(jiān)聽”事件過程代碼如下:(1)ServerIP.EnableWindow(false);/使IP地址控件不可用(2)ServerPort.EnableWindow(false);/使端口號固定無法修改(3)m_ButtonListen.EnableWindow(false);/使監(jiān)聽按鈕失效(4)m_ButtonStopListen.EnableWindow();/使用戶可以隨時停止監(jiān)聽(5)m_ButtonClear.EnableWindow();/使清空按鈕生效(6)m_ButtonExit.EnableWindow(false);/使退出按鈕失效服務器端程序在用戶按下“開始監(jiān)聽”按鈕
30、前后界面的變化對比如圖2.28所示。 “關于”事件過程代碼如下:CAboutDlg dlgAbout;dlgAbout.DoModal();/顯示“關于”對話框在資源視圖的目錄樹Dialog子目錄下的第一個項目IDD_ABOUTBOX就是“關于”對話框的ID,雙擊它可以進入“關于”對話框的設計界面(如圖2.29所示)。在此,用戶可以設計“關于”對話框的外觀,寫上版權聲明。在這個例子中,簡單地設計雙方通信軟件的“關于”對話框如圖2.30所示。1212程序完全演示程序完全演示分別開啟客戶端程序和服務器端程序(如圖2.31所示)。在服務器端輸入IP地址和端口,單擊“開始監(jiān)聽”按鈕,服務器于是在這個地
31、址上等待客戶來連接,歷史記錄欄里將自動記下這一事件,如圖2.32所示。在客戶端地址欄里輸入和服務器一樣的IP和端口,單擊“連接”按鈕,連上服務器,可以看到雙方的狀態(tài)欄里都反映了這次連接的情況(如圖2.33所示)。接下來雙方就可以互相通信了,可以互發(fā)信息,信息內容會實時自動地顯示在雙方的通信記錄列表中,并且你還會發(fā)現(xiàn):當某方的通信記錄多到一定量時,列表框會自動以滾動條方式顯示(如圖2.34所示)。通信過程中任何一方都可以主動斷開連接(如圖2.35所示)。也可以重新連接,并隨時清空通信記錄(如圖2.36所示)。單擊“關于”按鈕,查看版權信息(如圖2.37所示)。服務器隨時可以停止監(jiān)聽,客戶端連接服
32、務器失敗時會彈出失敗提示(如圖2.38所示)。1 1傳統(tǒng)程序機制的弊端傳統(tǒng)程序機制的弊端歸納起來2.2.2節(jié)的程序存在以下一些編程機制上的弊端:(1)Socket的創(chuàng)建、使用和銷毀不夠靈活,無法支持很多Socket動態(tài)參與通信過程的情況。(2)程序代碼分散在各個不同的Socket模塊中,不利于統(tǒng)一管理和維護。(3)如果在Socket的代碼中要訪問和控制主對話框界面上的控件,則需要運用AfxGetApp()全局函數(shù)獲取主窗口CWinApp類指針的方法,這意味著每一個在Socket中訪問到主對話框界面上控件的語句前都要寫上冗長的類似下面的這句代碼:(CChatClientDlg*)(AfxGetA
33、pp()-m_pMainWnd)-2 2用對話框指針機制實現(xiàn)的用對話框指針機制實現(xiàn)的SocketSocket程序程序(1)創(chuàng)建工程、添加類、用戶界面。創(chuàng)建完成后在客戶和服務器端工程中各添加一個基于CAsyncSocket類的MySocket類,它是用來給通信雙方動態(tài)生成Socket對象使用的,如圖2.39所示。參照上節(jié)例子程序給這個程序設計個一模一樣的界面,如圖2.40所示。(2)用對話框指針機制組織新的程序雛形。l客戶端。代碼添加位置如圖2.41所示。給MySocket類添加網絡事件處理函數(shù),在這里我們添加OnClose()、OnConnect()、OnReceive()三個函數(shù),分別用于響
34、應服務器斷開連接事件、客戶發(fā)起連接請求事件、接收到服務器發(fā)來的數(shù)據(jù),系統(tǒng)將自動為每個函數(shù)生成函數(shù)聲明和函數(shù)體的框架,如圖2.42所示。在ChatClientDlg.cpp中將MySocket類對象指針初始化為空,并寫出我們上面已聲明的三個函數(shù)OnClose()、OnConnect()、OnReceive()的函數(shù)體,為它們的實現(xiàn)代碼預留出空間(如圖2.43所示),同時實現(xiàn)SocketReset()函數(shù),代碼如下:void CChatClientDlg:SocketReset()/SocketReset函數(shù)實現(xiàn)if(m_ClientSocket!=NULL) delete m_ClientSoc
35、ket;m_ClientSocket=NULL; l服務端。采用與客戶端類似的方法,在MySocket.h中添加:class CChatServerDlg;/先對主對話框類進行前導聲明CChatServerDlg * m_dlg;/定義主對話框指針void GetDlg(CChatServerDlg * dlg); /指針獲取函數(shù)與客戶端一樣,系統(tǒng)自動為每個函數(shù)生成函數(shù)聲明和函數(shù)體框架,另外我們在ChatServerDlg.cpp中將MySocket類對象指針初始化為空。#include MySocket.h /引用MySocket類頭文件,為了后面定義動態(tài)Socket對象指針MySocket
36、 * m_ServerSocket; /MySocket類對象指針,用于動態(tài)生成與客戶通信的/SocketMySocket * m_ListenSocket; /定義MySocket類對象指針,用于動態(tài)生成監(jiān)聽Socketvoid OnReceive();void OnClose();void OnAccept(); /要在主對話框代碼中實現(xiàn)的服務器端功能的三個函數(shù)void SocketReset(); /重置銷毀不再使用的套接字在ChatServerDlg.cpp中寫出OnAccept()、OnClose()、OnReceive()三個函數(shù)的函數(shù)體,為它們的實現(xiàn)代碼預留出空間,同時實現(xiàn)Soc
37、ketReset函數(shù),代碼如下:void CChatServerDlg:SocketReset() /SocketReset函數(shù)實現(xiàn) if(m_ServerSocket != NULL) delete m_ServerSocket; /在此要銷毀兩個Socket,一個是與客戶通/信的Socketm_ServerSocket = NULL; if(m_ListenSocket != NULL)delete m_ListenSocket;/還有一個是監(jiān)聽Socketm_ListenSocket = NULL;試運行的界面如圖2.44所示。(3)代碼的復制、修改。l l 客戶端??蛻舳??!斑B接”按鈕
38、事件過程代碼如下:/初始化套接字,獲取對話框指針if(!if(!AfxSocketInitAfxSocketInit()() MessageBoxMessageBox( (WindowSocketWindowSocket initial initial failed!,failed!,Receive,MB_ICONSTOPReceive,MB_ICONSTOP); );return;return; m_ClientSocketm_ClientSocket = new = new MySocketMySocket; ;m_ClientSocketm_ClientSocket-GetDlgGetD
39、lg(this);(this);/連接服務器BYTE nFild4;CString sIP;UpdateData();ServerIP.GetAddress(nFild0,nFild1,nFild2,nFild3);sIP.Format(%d.%d.%d.%d,nFild0,nFild1,nFild2,nFild3);m_ClientSocketm_ClientSocket-Create();-Create();/ 創(chuàng)建客戶端Socketm_ClientSocketm_ClientSocket-Connect(-Connect(sIP,sPortsIP,sPort); );/發(fā)起連接請求“斷開
40、”按鈕事件過程代碼如下:/斷開與服務器的連接m_ClientSocketm_ClientSocket-Close();-Close();/關閉客戶端SocketSocketResetSocketReset(); (); /避免指針懸空m_ListWords.AddString(從服務器斷開);“發(fā)送”按鈕事件過程代碼如下:UpdateData();m_ClientSocketm_ClientSocket-Send(-Send(m_sWords,m_sWords.GetLengthm_sWords,m_sWords.GetLength();();/向服務器發(fā)送信息m_ListWords.AddS
41、tring(發(fā)送: + m_sWords);m_ListWords.SetTopIndex(m_ListWords.GetCount() - 1);OnClose()函數(shù)代碼如下:m_ListWords.AddString(服務器端斷開了);m_ListWords.SetTopIndex(m_ListWords.GetCount() - 1);m_ClientSocketm_ClientSocket-Close();-Close();SocketResetSocketReset();();/ /避免指針懸空OnReceive()函數(shù)代碼如下:char szTemp200;intint n =
42、n = m_ClientSocketm_ClientSocket-Receive(szTemp,200);-Receive(szTemp,200);szTempn = 0;CString sTemp;sTemp.Format(收到:%s,szTemp);m_ListWords.AddString(sTemp);m_ListWords.SetTopIndex(m_ListWords.GetCount() - 1);OnConnect()函數(shù)代碼如下:m_ListWords.AddString(連接服務器成功!);m_ListWords.SetTopIndex(m_ListWords.GetCou
43、nt() - 1);l l 服務器端。服務器端?!伴_始監(jiān)聽”按鈕事件過程代碼如下:/初始化套接字,獲取對話框指針if(!if(!AfxSocketInitAfxSocketInit()() MessageBoxMessageBox( (WindowSocketWindowSocket initial failed!, initial failed!,Send,MB_ICONSTOPSend,MB_ICONSTOP); );return;return; m_ListenSocketm_ListenSocket = new = new MySocketMySocket; ;m_ListenSock
44、etm_ListenSocket-GetDlgGetDlg(this);(this);/監(jiān)聽開始,服務器等待連接請求的到來BYTE nFild4;CString sIP,sP;UpdateData();ServerIP.GetAddress(nFild0,nFild1,nFild2,nFild3);sIP.Format(%d.%d.%d.%d,nFild0,nFild1,nFild2,nFild3);sP.Format(%d,sPort);m_ListenSocketm_ListenSocket-Create(sPort,1,FD_ACCEPT,sIP);-Create(sPort,1,FD_
45、ACCEPT,sIP);/創(chuàng)建服務端監(jiān)聽Socketm_ListenSocketm_ListenSocket-Listen(1); -Listen(1); /開始監(jiān)聽m_ListWords.AddString(監(jiān)聽開始:);m_ListWords.AddString(地址 + sIP + 端口 + sP);m_ListWords.AddString(等待客戶端連接);“停止監(jiān)聽”按鈕事件過程代碼如下:/停止監(jiān)聽m_ListenSocketm_ListenSocket-Close();-Close();/關閉服務端監(jiān)聽Socketif(if(m_ListenSocketm_ListenSocke
46、t != NULL) != NULL) /防止指針懸空 delete delete m_ListenSocketm_ListenSocket; ;m_ListenSocketm_ListenSocket = NULL; = NULL; m_ListWords.AddString(停止監(jiān)聽);“斷開”按鈕的事件過程代碼如下:m_ServerSocket-Close();if(m_ServerSocket!=NULL)delete m_ServerSocket;m_ServerSocket=NULL;m_ListWords.AddString(與客戶端斷開);“發(fā)送”按鈕的事件過程代碼如下:Upd
47、ateData();m_ServerSocketm_ServerSocket-Send(-Send(m_sWords,m_sWords.GetLengthm_sWords,m_sWords.GetLength();();/獲取文本長度m_ListWords.AddString(發(fā)送: + m_sWords);/顯示發(fā)送文件長度m_ListWords.SetTopIndex(m_ListWords.GetCount() - 1);OnAccept()函數(shù)代碼如下:/動態(tài)創(chuàng)建用于通信的Socketm_ServerSocketm_ServerSocket = new = new MySocketMy
48、Socket; ;m_ServerSocketm_ServerSocket-GetDlgGetDlg(this);(this);/ 接受客戶端的連接請求m_ListenSocketm_ListenSocket-Accept(-Accept(* *m_ServerSocketm_ServerSocket); );/接受連接請求m_ServerSocketm_ServerSocket-AsyncSelectAsyncSelect(FD_READ|FD_CLOSE);(FD_READ|FD_CLOSE);m_ListWords.AddString(接受了一個客戶端的連接請求);m_ListWords
49、.SetTopIndex(m_ListWords.GetCount() - 1);OnClose()函數(shù)代碼如下:/關閉與客戶端的通信信道m(xù)_ListWords.AddString(客戶端斷開連接);m_ListWords.SetTopIndex(m_ListWords.GetCount() - 1);m_ServerSocketm_ServerSocket-Close();-Close();/關閉與客戶端通信的Socketif(if(m_ServerSocketm_ServerSocket != NULL) != NULL) delete delete m_ServerSocketm_Ser
50、verSocket; ;m_ServerSocketm_ServerSocket = NULL; = NULL; OnReceive()函數(shù)代碼如下:/接收客戶端發(fā)來的信息char szTemp200;intint n = n = m_ServerSocketm_ServerSocket-Receive(szTemp,200);-Receive(szTemp,200);/接收信息szTempn = 0;CString sTemp;sTemp.Format(收到:%s,szTemp);m_ListWords.AddString(sTemp);/顯示信息m_ListWords.SetTopInde
51、x(m_ListWords.GetCount() - 1);“退出”按鈕的事件過程代碼如下:void CChatServerDlg:OnBnClickedCancel()/ TODO: 在此添加控件通知處理程序代碼SocketResetSocketReset();();OnCancel();(4)運行對比。來運行一下這個程序,同時運行一下2.2.2節(jié)的傳統(tǒng)機制程序,并將它們放在一個桌面上對比如圖2.45所示。3 3對話框指針機制的好處對話框指針機制的好處使用對話框指針機制的程序比傳統(tǒng)程序更有顯著的優(yōu)越性,主要體現(xiàn)在以下三個方面:(1)更加靈活。(2)代碼更集中、更有條理。(3)程序語句更加簡潔
52、。1 1創(chuàng)建工程、添加類、用戶界面創(chuàng)建工程、添加類、用戶界面分別創(chuàng)建客戶端和服務器端工程,設置和前兩節(jié)的程序完全一樣,創(chuàng)建完成后分別在客戶和服務器端工程中各添加一個基于CSocketCSocket類類的MySocket類(如圖2.46所示)。再對照前兩節(jié)實例程序的界面給這個程序設計一個一模一樣的界面,如圖2.47所示。2 2程序雛形架構程序雛形架構l l 客戶端??蛻舳?。在MySocket.h中添加如下代碼:#include Afxsock.h /引用Afxsock.h頭文件,目的是使用CSocket類class CChatClientDlg;/為了能在MySocket類中定義主對話框類指針,
53、先對主對話框類進行/前導聲明CChatClientDlg * m_dlg; /定義主對話框指針void GetDlg(CChatClientDlg * dlg); /獲取主對話框指針的函數(shù)在ChatClientDlg.h中添加如下代碼:#include MySocket.h /引用MySocket類的頭文件,是為了下面定義一個/MySocket類對象指針MySocket * m_ClientSocket; /客戶端套接字指針CArchive * m_archiveIn; /收到的信息的存儲文件CArchive * m_archiveOut; /發(fā)送的信息的存儲文件CSocketFile * m
54、_socketfile; /用于發(fā)送和接收數(shù)據(jù)的緩沖區(qū)void OnReceive();void OnClose();void SocketReset();定義變量m_Input(如圖2.48所示),用于后面接收對方發(fā)來的數(shù)據(jù):CString m_Input;在ChatClientDlg.cpp中添加初始化代碼:m_ClientSocket = NULL;m_archiveIn = NULL;m_archiveOut = NULL;m_socketfile = NULL;在ChatClientDlg.cpp中寫出我們上面聲明的函數(shù)OnClose和OnReceive的函數(shù)體,為它們的實現(xiàn)代碼預留
55、出空間,同時實現(xiàn)SocketReset()函數(shù),其代碼。在MySocket.cpp中實現(xiàn)獲取主對話框指針的GetDlg()函數(shù),并通過指針引用主對話框程序代碼中的網絡事件處理函數(shù):#include ChatClientDlg.hvoid MySocket:GetDlg(CChatClientDlg * dlg) /獲得窗口界面的指針m_dlgm_dlg= =dlgdlg; ; void MySocket:OnClose(int nErrorCode)/ TODO: 在此添加專用代碼和/或調用基類m_dlgm_dlg-OnCloseOnClose();();CSocket:OnClose(nEr
56、rorCode);void MySocket:OnReceive(int nErrorCode)/ TODO: 在此添加專用代碼和/或調用基類m_dlgm_dlg-OnReceiveOnReceive();();AsyncSelectAsyncSelect(FD_CLOSE|FD_READ|FD_WRITE);(FD_CLOSE|FD_READ|FD_WRITE);CSocket:OnReceive(nErrorCode);l l 服務端。服務端。采用與客戶端類似的方法,在MySocket.h中添加如下代碼:#include Afxsock.hclass CChatServerDlg;CCha
57、tServerDlg *m_dlg;void GetDlg(CChatServerDlg *dlg);在ChatServerDlg.h中添加如下代碼:#include MySocket.h MySocket *m_ServerSocket;/MySocket類對象指針,用于動態(tài)生成與客戶通信的SocketMySocket *m_ListenSocket;/定義MySocket類對象指針,用于動態(tài)生成監(jiān)聽SocketCArchive *m_archiveIn;CArchive *m_archiveOut;CSocketFile *m_socketfile;void OnReceive();voi
58、d OnClose();void OnAccept();void SocketReset();在ChatServerDlg.cpp中添加如下初始化代碼:m_ServerSocket = NULL;m_ListenSocket = NULL;m_archiveIn = NULL;m_archiveOut = NULL;m_socketfile = NULL;在ChatServerDlg.cpp中寫出OnAccept()、OnClose()、OnReceive()三個函數(shù)的函數(shù)體,為它們的實現(xiàn)代碼預留出空間,同時實現(xiàn)SocketReset函數(shù),其代碼。在MySocket.cpp中實現(xiàn)獲取主對話框指
59、針的GetDlg()函數(shù),并通過指針引用主對話框的三個函數(shù),其代碼。3 3代碼解析代碼解析l l 客戶端。客戶端?!斑B接”按鈕的事件過程代碼。“斷開”按鈕事件過程代碼如下:SocketReset();m_ListWords.AddString(從服務器斷開);“發(fā)送”按鈕事件過程代碼如下:UpdateData();* *m_archiveOutm_archiveOutFlush();-Flush();m_ListWords.AddString(發(fā)送: + m_sWords);m_ListWords.SetTopIndex(m_ListWords.GetCount() - 1);OnClose(
60、)函數(shù)代碼如下:m_ListWords.AddString(服務器端斷開了);m_ListWords.SetTopIndex(m_ListWords.GetCount() - 1);SocketResetSocketReset();();OnReceive()函數(shù)代碼如下:*m_archiveInm_Input;m_archiveIn-Flush();m_ListWords.AddString(收到: + m_Input);m_ListWords.SetTopIndex(m_ListWords.GetCount() - 1);“斷開”按鈕事件過程代碼如下:SocketReset();m_ListWor
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 二零二五年度土地承包租賃合同(含農業(yè)政策支持)3篇
- 一流本科專業(yè)建設的背景與意義
- 2024年食品冷鏈物流服務合同
- 曲靖市中小學生科技素養(yǎng)課程 第6課.《觸動傳感器》說課稿
- 2025廣東省安全員-B證(項目經理)考試題庫
- 城市生活垃圾無害化綜合處理項目可行性研究報告
- 2024年銷售職業(yè)勞務合同標準文本一
- 二零二五年度家庭親子旅游度假產品設計與服務合同正規(guī)范本2篇
- 2025云南省安全員C證考試題庫
- 2025云南省建筑安全員考試題庫
- 【重慶武隆區(qū)文旅品牌傳播存在的問題及優(yōu)化建議分析13000字(論文)】
- 老年綜合評估知情同意書
- 會議籌備工作分工表
- 水土保持監(jiān)理工作報告
- 時間管理學習通超星課后章節(jié)答案期末考試題庫2023年
- 2023火電機組深度調峰工況下的涉網性能技術要求
- 分子影像學概論課件
- 醫(yī)學英語術語解密-福建醫(yī)科大學中國大學mooc課后章節(jié)答案期末考試題庫2023年
- 中國移動呼叫中心的精細化管理
- 內燃機車點檢方法探討
- 2023初一語文現(xiàn)代文閱讀理解及解析:《貓》
評論
0/150
提交評論