| ---- 一. 客戶端
---- 1. 創建一個Dialog Based項目:CSockClient。
---- 2. 設計對話框
---- 去掉Ok和Cancle兩個按鈕,增加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
---- 3. CAsyncSocket類用DoCallBack函數處理MFC消息,當一個網絡事件發生時,DoCallBack函數按網絡事件類型:FD_READ、FD_WRITE、FD_ACCEPT、FD_CONNECT分別調用OnReceive、OnSend、OnAccept、OnConnect函數。由於MFC把這些事件處理函數定義為虛函數,所以要生成一個新的C++類,以重載這些函數,做法如下:
---- 以Public方式繼承CAsyncSocket類,生成新類MySock;
---- 為MySock類添加虛函數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]; //消息緩沖區
---- 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_Addr、IDC_Port按下表在ClassWizard中為CAddrDlg類添加變量。
Control ID Type Member
IDC_Addr CString m_Addr IDC_Port Int m_Port
---- 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); }
---- 12.運行此項目,連接時輸入主機名或IP均可,CAsyncSocket類會自動處理。 ---- 二. 服務端 ---- Server端的編程與Client端的類似,下面主要介紹他的Listen及Accept函數 ---- 1. 建立一個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); }
---- 2. 建立一個CMyServerSocket類,重載CAsyncSocket類的OnAccept函數代碼如下 ---- 在MyServerSocket.h中聲明變量 public:: CNewSocket* m_pSocket;
void CMyServerSocket::OnAccept(int nErrorCode) { //偵聽到連接請求,調用Accept函數 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 (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; } } }
---- 4. 目前程序只能實現Echo功能,將信息原封不動的轉發,若能將Accept中由CNewSocket* pSocket = new CNewSocket();得到的Socket指針存入一個CList或一個數組中,便像Client端那樣,對所有的連接進行讀寫控制。 ---- 三. 總結 ---- CAsyncSocket類為我們使用Socket提供了極大方便。建立Socket的WSAStartup過程和bind過程被簡化成為Create過程,IP地址類型轉換、主機名和IP地址轉換的過程中許多復雜的變量類型都被簡化成字符串和整數操作,特別是CAsyncSocket類的異步特點,完全可以替代繁瑣的線程操作。MFC提供了大量的類庫,我們若能靈活的使用他們,便會大大提高編程的效率。 一、TCP/IP 體系結構與特點 1、TCP/IP體系結構 TCP/IP協議實際上就是在物理網上的一組完整的網絡協議。其中TCP是提供傳輸層服務,而IP則是提供網絡層服務。TCP/IP包括以下協議:(結構如圖1.1) (圖1.1) IP: 網間協議(Internet Protocol) 負責主機間數據的路由和網絡上數據的存儲。同時為ICMP,TCP, UDP提供分組發送服務。用戶進程通常不需要涉及這一層。
ARP: 地址解析協議(Address Resolution Protocol) 此協議將網絡地址映射到硬件地址。
RARP: 反向地址解析協議(Reverse Address Resolution Protocol) 此協議將硬件地址映射到網絡地址
ICMP: 網間報文控制協議(Internet Control Message Protocol) 此協議處理信關和主機的差錯和傳送控制。
TCP: 傳送控制協議(Transmission Control Protocol) 這是一種提供給用戶進程的可靠的全雙工字節流面向連接的協議。它要為用戶進程提供虛電路服務,並為數據可靠傳輸建立檢查。(注:大多數網絡用戶程序使用TCP)
UDP: 用戶數據報協議(User Datagram Protocol) 這是提供給用戶進程的無連接協議,用於傳送數據而不執行正確性檢查。
FTP: 文件傳輸協議(File Transfer Protocol) 允許用戶以文件操作的方式(文件的增、刪、改、查、傳送等)與另一主機相互通信。
SMTP: 簡單郵件傳送協議(Simple Mail Transfer Protocol) SMTP協議為系統之間傳送電子郵件。
TELNET:終端協議(Telnet Terminal |