Windows Socket套接字:MFC套接字編程

網絡名詞解釋


同步:指發送方發出數據後,等收到接收方發回的響應,才發下一個數據包的通信方式
異步:指的是發送方不等接收方響應,便接着發下個數據包的通信方式;
阻塞:指調用某函數時,直到該函數完成操作,才返回;否則一直阻塞在該調用上
非阻塞:指調用某操作時,不管操作是否成功都立即返回,而不會掛在該操作

一.CAsyncSocket類


提供基於事件的I/O異步模型


1.create


該方法用於創建一個Windows套接字,並將其附加在CAsyncSocket類對象上
BOOL Create(UINT nSocketPort=0,int nSocketType=SOCK_STREAM,long IEvent=FD_READ|FD_WRITE|FD_OOB|FD_ACCEPT|FD_CONNECT|FD_CLOSE,LPCTSTR lpszSocketAddress=NULL);
nSocketPort:套接字端口,如果爲0,系統自動選擇一個端口
nSocketType:套接字類型,如果爲SOCK_STREAM=流式套接字,SOCK_DGRAM=數據報套接字
IEvent:套接字能夠處理的網絡事件
FD_READ:當套接字中有數據需要讀取時觸發事件
FD_WRITE:當向套接字寫入數據時觸發事件
FD_OOB:當接收到外帶數據時觸發事件
FD_ACCEPT:當接收連接請求時觸發事件
FD_CONNECT:當連接完成時觸發事件
FD_CLOSE:當套接字關閉時觸發事件
lpszSocketAddress:套接字的IP地址


2.GetLastError


該方法用於獲取最後一次操作失敗的狀態信息
static int GetLastError();


3.GetPeerName


該方法用於獲取套接字連接中的IP地址信息
BOOL GetPeerName(CString&rPeerAddress,UINT&rPeerPort);
BOOL GetPeerName(SOCKADDR*lpSockAddr,int*lpSockAddrLen);
rPeerAddress:用於接收函數返回的IP地址
rPeerPort:用於記錄端口號
lpSockAddr:一個socketaddr結構指針,用於記錄套接字名稱
lpSockAddrLen:用於確定lpSocketAddr的大小


4.Accept


該方法用於接受客戶端的連接
virtual BOOL Accept(CAsyncSocket&rConnectedSocket,SOCKADDR*lpsockAddr=NULL,int*lpSockAddrLen=NULL);
rConnectedSocket:對應當前連接的套接字引用
lpSockAddr:一個sockaddr結構指針,用於記錄套接字地址
lpSockAddrLen:用於確定lpsockaddr的大小


5.bind


該方法用於將IP地址和端口號綁定到套接字上
BOOL Bind(UINT nSocketPort,LPCTSTR lpszSocketAddress=NULL);
BOOL Bind(const SOCKADDR*lpSockAddr,int nSockAddrLen);
nSocketport:套接字端口
lpszSocketAddress:IP地址
lpSockADdr:一個sockaddr結構指針
nSockAddrLen:用於確定lpsockAddr的大小

6.connect

該方法用於發送一個連接請求
BOOL Connect(LPCTSTR lpszHostAddress,UINT nHostPort);
BOOL Connect(const SOCKADDR* lpSockAddr,int nSockAddrLen);
lpszHostAddress:主機的IP地址或網址
nHostPort:主機的端口
lpSockADdr:一個sockaddr結構指針
nSockAddrLen:用於確定lpsockAddr的大小

7.close

該方法用於關閉套接字
virtual void Close();

8.Listen

該方法用於將套接字置於監聽模式
BOOL Listen(int nConnectionBacklog=5);
nConnectionBacklog:等待連接的最大隊列長度

9.receive

該方法用於在流式套接字中接受數據
virtual int Receive(void* lpBuf,int nBuflen,int nFlags=0);
lpBuf:接受數據的緩衝區
nBufLen:確定緩衝區的長度
nFlags:確定函數的調用模式,MSG_PEEK=表示查看傳來的數據,數據被複制到接受緩衝區,但不會從輸入隊列中移走;MSG_OOB=處理帶外數據.

10.ReceiveFrom

該方法用於從數據包套接字中接受數據
int ReceiveFrom(void*lpBuf,int nBufLen,CString&rSocketAddress,UINT&rSocktPort,int nFlags=0);
int RecerveFrom(void *lpBuf,int nBufLen,SOCKADDR* lpSockAddr,int *lpSockAddrLen,int Flags=0);
lpBuf:接受數據的緩衝區
nBufLen:緩衝區的大小
rSocketAddress:用於接收數據報的目的地(IP地址)
rSocketPort:用於記錄端口號
lpSockAddr:一個sockaddr結構指針,用於記錄套接字地址信息
lpSockAddrLen:用於確定lpSockAddr的大小
nFlags:函數的調用模式

11.send


該方法用於向流式套接字中發送數據
virtual Send(const void* lpBuf,int nBufLen,int nFlags=0);
lpBuf:要發送數據的緩衝區
nBufLen:緩衝區大小
nFlags:函數調用方法

12.sendTo


該方法用於在流式套接字或數據包套接字上發送數據
int SendTo(const void*lpBuf,int nBuflen,UINT nHostPort,LPCTSTR lpszHostAddress=NULL,int nFlags=0);
int Sendto(const void*lpBuf,int nBufLen,const SOCKADDR* lpSockAddr,int nSockAddrLen,int nFlags=0);
lpBuf:要發送數據的緩衝區
nBufLen:緩衝區大小
nHostPort:主機端口號
lpszHostAddress:主機地址
lpSockAddr:一個sockaddr結構指針
nSockAddrLen:lpsockAddr的大小
nFlags:函數調用方式


13.shutDown


該方法用於在套接字上斷開數據的發送或接受
BOOL ShutDown(int nHow=sends);
nHow:用於確定函數的行爲,0表示不允許接收,1表示不允許發送,2表示不允許接收和發送

14.OnAccept

當套接字接收連接請求時觸發該事件
virtual void OnAccept(int nErrorCode);
nErrorCode:錯誤代碼

15.OnClose

當套接字關閉時觸發該事件
virtual void OnClose(int nErrorCode);

16.OnConnect

當套接字連接後觸發該事件
virtual OnConnect(int nErrorCode);

17.OnReiceive

當套接字有數據被接收時觸發該事件
virtual OnReiceive(int nErrorCode);

18.Onsend

當套接字發送數據時觸發該事件
virtual Onsend(int nErrorCode);

二、CSocket類


CSocket類派生於CAsyncSocket類,該類對套接字函數進行更高層次封裝,並提供了同步技術。

1.create

該方法用於創建一個Windows套接字,並將其附加在CSocket類對象上
BOOL Create(UINT nSocketPort=0,int nSocketType=SOCK_STREAM ,LPCTSTR lpszSocketAddress=NULL);
nSocketPort:套接字端口,如果爲0,系統自動選擇一個端口
nSocketType:套接字類型,如果爲SOCK_STREAM=流式套接字,SOCK_DGRAM=數據報套接字
lpszSocketAddress:套接字IP地址

2.Attach

該方法用於講一個套接字句柄附加到CSocket類對象
BOOL Atach(SOCKET hSocket);
hSocket:套接字句柄

3.FromHandle

該方法根據套接字句柄獲得CSocket對象指針
static CSocket* PASCAL FromHandle(SOCKET hSocket);
hSocket:套接字句柄
返回值:CSocket對象指針

4.lsBlocking

該方法用於判斷套接字是否處於阻塞模式
BOOL lsBlocking();
返回值:0=非阻塞,非0=則塞

5.CancelBlockingCall

該方法用於取消套接字的阻塞模式
void CancelBlockingCall();

三、客戶端實現過程


1.創建對話框,工程client


2.InitInstance方法中初始化套接字

 

[cpp] view plain copy

  1. BOOL CClientApp::InitInstance()  
  2. {  
  3.  WSADATA wsd; //定義WSADATA對象  
  4.  WSAStartup(MAKEWORD(2,2),&wsd); //初始化套接字  
  5. }  


 

 

3.從CSocket類派生一個子類CClientSocket,在該類中添加m_pDialog成員

 

[cpp] view plain copy

  1. CClientDlg *m_pDialog;//添加成員變量  


 

 

4.在CClientSocket中添加SetDialog方法,用於社會成員變量

[cpp] view plain copy

  1. void CClientSocket::SetDialog(CClientDlg *pDialog)  
  2. {  
  3.  m_pDialog = pDialog;//設置成員變量  
  4. }  



5.改寫CClientSocket類的OnReceive方法,在套接字有數據接收時調用該方法

[cpp] view plain copy

  1. void CClientSocket::OnReceive(int nErrorCode)   
  2. {  
  3.  CSocket::OnReceive(nErrorCode);  
  4.  if (m_pDialog != NULL)//判斷成員變量是否爲空  
  5.   m_pDialog->ReceiveText();//調用對話框類的ReceiveText方法接收數據  
  6. }  



6.在對話框添加如下成員變量

[cpp] view plain copy

  1. CClientSocket m_SockClient;//定義套接字成員變量  
  2. CString m_Name;//定義一個字符串變量  



7.向對話框類中添加ReceiveText方法接收數據

[cpp] view plain copy

  1. void CClientDlg::ReceiveText()  
  2. {  
  3.  char buffer[BUFFERSIZE];//定義接收數據的緩衝區  
  4.  int len =  m_SockClient.Receive(buffer,BUFFERSIZE);//開始接收數據  
  5.  if (len != -1)  
  6.  {  
  7.   buffer[len] = '\0';//設置結束標記  
  8.   m_List.AddString(buffer);//向列表中添加接收到的信息  
  9.  }  
  10. }  



8.在對話框初始化時創建套接字

 

[cpp] view plain copy

  1. m_SockClient.Create();//創建套接字  
  2. m_SockClient.SetDialog(this);//設置套接字的成員變量  


9.處理“登錄”按鈕,開始登陸服務器

 

[cpp] view plain copy

  1. void CClientDlg::OnLogin()   
  2. {  
  3.  CString strIP,strPort;//定義兩個字符串變量  
  4.  UINT port ;//定義一個整形端口變量  
  5.  m_ServerIP.GetWindowText(strIP);//獲取服務器IP  
  6.  m_NickName.GetWindowText(m_Name);//獲取用戶暱稱  
  7.  m_ServerPort.GetWindowText(strPort);//獲取端口  
  8.  if (strIP.IsEmpty() || strPort.IsEmpty() || m_Name.IsEmpty())  
  9.  {  
  10.   MessageBox("請設置服務器信息","提示");  
  11.   return;  
  12.  }  
  13.  port = atoi(strPort);//將端口字符串轉換爲整數  
  14.  if (m_SockClient.Connect(strIP,port))//開始連接服務器  
  15.  {  
  16.   MessageBox("連接服務器成功!","提示");  
  17.   CString str;  
  18.   str.Format("%s----->%s",m_Name,"進入聊天室");  
  19.   m_SockClient.Send(str.GetBuffer(0),str.GetLength());//向服務器發送數據,再由服務器轉發  
  20.  }  
  21.  else  
  22.  {  
  23.   MessageBox("連接服務器失敗!","提示");  
  24.  }  
  25. }  


 


10.處理“發送”按鈕,向服務器發送數據,再由服務器轉發這些數據

 

 

[cpp] view plain copy

  1. void CClientDlg::OnSendText()   
  2. {  
  3.  CString strText,strInfo;//定義兩個字符串變量  
  4.  m_Text.GetWindowText(strText);//獲取發送的內容  
  5.  if (!strText.IsEmpty() && !m_Name.IsEmpty())  
  6.  {  
  7.   strInfo.Format("%s說: %s",m_Name,strText);//設置發送的文本  
  8. //開始發送數據  
  9.   int len = m_SockClient.Send(strInfo.GetBuffer(strInfo.GetLength()),strInfo.GetLength());  
  10.  }  
  11. }  

 

四、服務器實現過程

 

1.創建對話框,工程Server

 

2.InitInstance方法中初始化套接字

 

[cpp] view plain copy

  1. BOOL CClientApp::InitInstance()  
  2. {  
  3.   //初始化套接字  
  4.  WSADATA wsd;  
  5.  AfxSocketInit(&wsd);  
  6. }  

 


3.從CSocket類派生一個子類CServerSocket,在該類中添加m_pDlg成員

 

 

[cpp] view plain copy

  1. CServerDlg *m_pDlg;//添加成員變量  

 


4.在CServerSocket中添加SetDialog方法,用於社會成員變量

 

 

[cpp] view plain copy

  1. void CServerSocket::SetDialog(CServerDlg *pDialog)  
  2. {  
  3.  m_pDlg= pDialog;//設置成員變量  
  4. }  

 


5.改寫CServerSocket類的OnAccept方法,在套接字有連接請求時接受其連接

 

 

[cpp] view plain copy

  1. void CServerSocket::OnAccept(int nErrorCode)   
  2. {  
  3.  CSocket::OnAccept(nErrorCode);  
  4.  if (m_pDlg)  
  5.   m_pDlg->AcceptConnect();  
  6. }  

 

6.從CSocket類再次派生一個新類CClientSocket,在該類中定義成員變量m_pDlg

 

[cpp] view plain copy

  1. CServerDlg* m_pDlg;  


7.向CClientSocket類中添加SetDlialog函數,爲m_pDlg成員變量賦值

 

 

[cpp] view plain copy

  1. void CClientSocket::SetDialog(CServerDlg* pDialog)  
  2. {  
  3.  m_pDlg = pDialog;  
  4. }  

 


8.改寫CClientSocket類的OnReceive方法,在套接字有數據時接收數據

 

[cpp] view plain copy

  1. void CClientSocket::OnReceive(int nErrorCode)   
  2. {  
  3.  CSocket::OnReceive(nErrorCode);  
  4.  if(m_pDlg)  
  5.  {  
  6.   m_pDlg->ReceiveData(*this);  
  7.  }  
  8. }  

 


9.在對話框類中添加如下成員變量

 

[cpp] view plain copy

  1. CPtrList m_socketlist;//定義套接字列表容器  
  2. CServerSocket m_ServerSock;//定義套接字  

 


10.向對話框類中添加AccceptConnect方法,接受客戶端的連接

 

[cpp] view plain copy

  1. void CServerDlg::AcceptConnect()  
  2. {  
  3.  CClientSocket* psocket = new CClientSocket();//創建一個套接字  
  4.  psocket->SetDialog(this);//設置套接字成員變量  
  5.  if (m_ServerSock.Accept(*psocket))//接受套接字連接  
  6.   m_socketlist.AddTail(psocket);//將套接字添加到列表容器中  
  7.  else  
  8.   delete psocket;//連接失敗,釋放套接字  
  9. }  

 


11.向對話框類中添加ReceiveData方法接收數據

[cpp] view plain copy

  1. void CServerDlg::ReceiveData(CSocket &socket)  
  2. {  
  3.  char bufferdata[BUFFERSIZE];  
  4.  int len = socket.Receive(bufferdata,BUFFERSIZE);//開始接收數據  
  5.  if (len != -1)//判斷是否接收到數據  
  6.  {  
  7.   bufferdata[len] = 0;//設置數據結束標記  
  8.   POSITION pos = m_socketlist.GetHeadPosition();//獲取容器列表的首位置  
  9.   while (pos != NULL)//遍歷容器列表  
  10.   {//獲取容器列表中的指定套接字  
  11.    CClientSocket* socket = (CClientSocket*)m_socketlist.GetNext(pos);  
  12.    if (socket != NULL)//判斷套接字是否爲空  
  13.     socket->Send(bufferdata,len);//向套接字發送數據  
  14.   }  
  15.  }  
  16. }  



 
12.處理“設置”按鈕,創建並開始監聽套接字

 

[cpp] view plain copy

  1. void CServerDlg::OnConfig()   
  2. {  
  3.  m_ServerSock.SetDialog(this);//設置套接字成員變量  
  4.  CString strPort,strIP;//定義兩個字符串變量  
  5.  m_ServerPort.GetWindowText(strPort);//獲取端口字符串  
  6.  m_ServerIP.GetWindowText(strIP);//獲取服務武IP  
  7.  if (!strPort.IsEmpty() && !strIP.IsEmpty())  
  8.  {  
  9.   UINT port = atoi(strPort);  
  10.   m_ServerSock.Create(port,SOCK_STREAM,strIP);//創建套接字  
  11.   BOOL ret = m_ServerSock.Listen();//將套接字置於監聽模式  
  12.   if (ret)  
  13.    MessageBox("設置成功!","提示");  
  14.  }  
  15. }  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章