Visual C++網絡編程是指用戶使用MFC類庫(微軟基礎類庫)在VC編譯器中,以實現網絡應用。用戶經過VC編程實現的
網絡軟件能夠在網絡中不一樣的計算機之間互傳文件,圖像等信息。
基礎知識:
若是用戶要進行VC網絡編程,則必須首先了解計算機網絡通訊的基本框架和工做原理。在兩臺或多臺計算機之
間進行網絡通訊時,其通訊的雙方還必須遵循相同的通訊原則好數據格式。編程
1.OSI七層網絡模型
OSI網絡模型是一個開放式系統互聯的參考模型。
發送信息的計算機 接收信息的計算機
7.應用層 7.應用層 表示計算機網絡中的物理設備。常見的有計算機網卡等
6.表示層 6.表示層 將傳輸數據進行壓縮與解壓縮
5.會話層 5.會話層 將傳輸數據進行網絡傳輸
4.數據傳輸層 4.數據傳輸層 進行信息的網絡傳輸
3.網絡層 3.網絡層 創建物理網絡的連接
2.數據鏈路層 2.數據鏈路層 將傳輸數據以某種格式進行表示
1.物理硬件層 1.物理硬件層 應用程序接口
發送方數據傳輸由高到低,接收方數據傳輸由低到高。各層數據對等通訊。服務器
2.TCP/IP協議
TCP/IP協議其實是一個協議簇,其包含了不少協議。例如,FTP(文本傳輸協議),SMTP(郵件傳輸協議)等應用層協議。
TCP/IP協議的網絡模型有4層:
數據鏈路層:網卡等網絡硬件設備以及驅動程序
網絡層: IP協議等互聯協議
數據傳輸層:爲應用程序提供通訊方法,一般爲TCP,UDP協議
應用層: 負責處理應用程序的實際用於層協議
在數據傳輸層中,包括了TCP和UDP協議。其中,TCP協議是基於面向鏈接的通訊協議。其具備重發機制,即當數據破壞或者丟失時,發送方將重發該數據。而UDP協議是基於用戶數據報協議,屬於不可靠鏈接通訊協議。(沒有重發機制)網絡
3.C/S編程模型
C/S編程模型是基於可靠鏈接的通訊模型。在通訊的雙方必須使用各自的IP地址以及端口進行通訊。不然,通訊過程將沒法實現。一般狀況下,當用戶使用C/S模型進行通訊時,其通訊的一方稱爲客戶端,則另外一端稱爲服務端。
服務器端等待客戶端鏈接請求的到來,這個過程稱爲監聽過程。一般,服務器監聽功能是在特定的IP地址和端口上進行。而後客戶端像服務器發送鏈接請求,服務器響應鏈接請求則鏈接成功。不然,客戶端的連接請求失敗。
因爲客戶端連接服務器時,須要使用服務器的IP地址和監聽端口號才能完成鏈接。因此,服務器的IP地址和端口號必須是固定的。部分協議所使用的端口號:HTTP協議(網頁瀏覽服務)所使用的端口號是80,FTP(超文本傳輸協議)端口號是21
注意:用戶在實際編程中,通訊雙方的連接以及數據通訊均是基於Socket(套接字)進行的框架
4.Socket套接字
網絡應用程序可使用MFC中封裝的套接字類進行編程,也可使用WindowsAPI函數進行程序開發。相比較而言,MFC網絡編程比較簡單一點,用戶使用也比較方便。
用戶在Windows中編寫網絡通訊程序時,須要使用WindowsSockets(Windows套接字)。與Windows套接字相關的API函數稱爲Winsock函數。
在網絡通訊的雙方,均有各自的套接字,而且該套接字與特定的IP地址和端口號相關聯。一般,套接字主要有兩種類型,分別是流式套接字(SOCK_STREAM)和數據報套接字(SOCK_DGRAM)。其中,流式套接字是專門用於使用TCP協議通訊的應用程序中,而數據報套接字則是專門用於使用UDP協議進行通訊的應用程序中。異步
5.網絡字節順序
網絡字節順序是指TCP/IP協議中規定的數據傳輸使用格式,與之相對的字節順序是主機字節順序。網絡字節順序表示首先將數據中最重要的字節進行存儲。例如,當數據0x357451使用網絡字節順序進行存儲時,該值在內存中的存放順序將是0X35,.0x74,0x51。由於通訊數據可能會在不一樣的機器之間進行傳輸,因此通訊數據必須以相同的格式進行整理。只有通過格式處理的通訊數據,才能在不一樣的機器之間進行傳輸。函數
6.Windows Sockets介紹
在MFC類庫中,幾乎封裝了Windows Sockets的所有功能。介紹兩個主要的套接字相關類,分別是CAsyncSocket類和CSocket類
微軟基礎庫中,CAsyncSocket類封裝了異步套接字的基本功能。使用該類進行網絡數據傳輸的步驟以下:
1).調用構造函數建立套接字對象
2).若是建立服務器端套接字,則調用函數Bind()綁定本地IP和端口,而後調用函數Listen()監聽客戶端的請求。若是請求到來,則調用函數Accept()響應該請求。若是建立客戶端套接字,則直接調用函數Connect()鏈接服務器便可。
3).調用Send()等功能函數進行數據傳輸與處理
4).關閉或銷燬套接字對象。
注意:在MFC中,全部類中均有一個變量m_hWnd表示該類的實例句柄
CSocket類派生於CAsyncSocket類。該類不但具備CAsyncSocket類的基本功能,還具備串行化功能。用戶在實際編程中,經過將CSocket類與CSocketFile類和CArchive類一塊兒使用,可以很好地管理數據以及發送數據。使用該類進行網絡編程的步驟以下:
1).建立CSocket類對象
2).若是建立服務器端套接字,則調用函數Bind()綁定本地IP和端口,而後調用函數Listen()監聽客戶端的請求。若是請求到來,則調用函數Accept()響應該請求。若是建立客戶端套接字,則直接調用函數Connect()鏈接服務器便可。
3).建立與CSocket類對象相關聯的CSocketFile類對象
4).建立與CSocket類相關聯的CArchive對象
5).使用CArchive類對象在客戶端和服務器之間進行數據傳輸
6).關閉或銷燬CSocket類,CSocketFile類和CArchive類的三個對象spa
7.Socket套接字編程
套接字是由美國伯克利大學提出並設計的一種在網絡中不不一樣主機之間進行數據交互的通訊橋樑。在實際生活中,人麼所使用的網絡通訊軟件功能均是基於Socket套接字做爲通訊橋樑實現。因此,套接字在網絡編程中,有着很是重要得做用。
在Socket套接字編程中,爲了準肯定位通訊雙方和數據傳輸的有效性,完整性,編程時必須使用統一的尋址方式和字節排序順序。
1)尋址方式:由於套接字須要在各類網絡協議中使用,因此爲了區分程序所使用的網絡協議必須使用同一的尋址方式。例如,在TCPIP協議通訊中,用戶使用IP地址和端口號進行肯定通訊雙方。而在其餘的協議中不必定也使用該方式肯定通訊雙方。
在Winsock(Socket API)中,用戶可使用TCP/IP地址家族中統一的套接字地址結構解決TCP/IP尋址中可能出現的問題。該套接字地址結構定義以下:計算機網絡
1 struct sockaddr_in{ 2 short sin_family; //指定地址家族即地址格式
3 unsigned short sin_port; //端口號碼
4 struct in_addr sin_addr; //IP地址
5 char sin_zero[8]; //留做備用,須要指定爲0
6 }
在這個結構中,成員sin_family指定使用該套接字地址的地址家族。在這裏必須設置爲AF_INET,表示程序所使用的的地址家族是TCP/IP
注意:該結構的最後一個成員並未實際使用,主要是爲了與第一個版本的套接字地址結構大小相同而設置。在實際使用時,將這8個字節直接設爲0便可。
該結構成員變量sin_addr表示32位的IP地址結構。其結構定義以下:設計
1 struct in_addr{ 2 union{ //聯合函數(公用一塊內存,各成員中最長的爲準)
3 struct{ 4 unsigned char s_b1,s_b2,s_b3,s_b4; 5 }S_un_b; //用4個u_char字符描述IP地址
6 struct{ 7 unsigned short s_w1,s_w2; 8 }S_un_w; //用2個u_short類型描述IP地址
9 struct{
10 unsigned long S_addr; //用1個u_logn類型描述IP地址
11 }S_un; 12 };
一般咱們在網絡編程中使用一個u_long類型的字符進行描述IP地址,例如:指針
1 sockaddr_in addr; 2 addr.sin_addr.S_un.S_addr = inet_addr("192.168.1.0")
在程序中,首先定義sockaddr_in結構對象addr,而後爲IP地址結構in_addr中的成員S_addr賦值。由於結構成員S_addr所描述的IP地址均爲網絡字節順序,因此程序調用inet_addr()函數將字符串IP轉換爲以網絡字節順序排列的IP地址。
2)字節順序:在Socket套接字編程中,傳輸數據的排列順序以網絡字節順序和主機字節順序爲主。一般狀況下,若是用戶將數據經過網絡發送時,須要將數據轉換成以網絡字節順序排序,不然可能形成數據損壞。若是用戶是將網絡中接收到的數據存儲在本地計算機上,那麼須要將數據轉換成以主機字節順序排列。
網絡字節順序將數據中最重要的字節首先進行存儲,而主機字節順序則將不重要的字節首先存儲。
IP地址結構in_addr中的成員S_addr的值均是以網絡字節順序排列。
2.1)字節順序轉換函數:
在Winsock中提供了幾個關於網絡字節順序與主機字節順序之間的轉換函數。函數定義以下:
1 u_short htons(u_short hostshort); //將一個u_short類型的IP地址從主機字節順序轉換到網絡字節順序
2 u_long htonl(u_long hostlong); //將一個u_long類型的IP地址從主機字節順序轉換到網絡字節順序
3 u_long ntohl(u_long netlong) //將一個u_long類型的IP地址從網絡字節順序轉換到主機字節順序
4 u_short ntohs(u_short netshort) //將一個u_short類型的IP地址從網絡字節順序轉換到主機字節順序
5 unsigned long inet_addr(const char FAR*cp) //將一個字符串IP轉換到以網絡字節順序排列的IP地址
6 char FAR* inet_ntoa(struct in_addr in) //將一個以網絡字節順序排列的IP地址轉換爲一個字符串IP
實例:
1 sockaddr_in addr; //定義套接字地址結構變量
2 in_addr in_add; //定可以以IP地址結構變量
3 addr.sin_family = AF_INET; //定義地址家族爲TCP/IP
4 addr.sin_port = htons(80); //指定端口號
5 addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1") //將字符串IP轉換爲網絡字節順序排列的IP
6 char address[] = inet_ntoa(addr.sin_addr.S_un.S_addr) //將網絡字節順序排列的IP裝換爲字符串IP
在程序中,首先使用inet_addr()將字符串IP 「127.0.0.1」轉換爲以網絡字節順序排列的IP地址結構成員S_addr中。而後,再使用函數inet_ntoa()將成員所表示的IP值轉換成字符串IP
8.Socket相關函數
1)建立套接字
使用CSocket類建立套接字對象是經過該類的構造函數建立的。CSocket:CSocket()
建立CSocket類對象
1 CSocket sock;
若是用戶須要建立套接字對象指針,則應該使用關鍵字new進行建立。
1 CSocket* sock = new CSocket
2)綁定地址信息
BOOL Bind(const SOCKADDR* lpSockAddr,int nSockAddrLen);
該函數的做用是將套接字對象與服務器地址結構綁定在一塊兒。若是函數調用成功,則返回true。不然,返回false。參數lpSockAddr指定將要綁定的服務器地址結構,參數nSockAddrLen表示地址結構的長度。
在服務器端,當地址信息綁定套接字成功後,還須要調用函數Listen()在指定端口監聽客戶端的連接請求。
1 BOOL Listen(int nConnectionBacklog = 5)
參數nConectionBacklog表示套接字監聽客戶端請求的最大數目。
1 CSocket sock; //建立套接字對象
2 sockaddr_in addr; //定義套接字地址結構變量
3 in_addr in_add; //定義IP地址結構變量
4 addr.sin_family = AF_INET //指定地址家族爲TCP/IP
5 addr.sin_port = htons(80) //指定端口號
6 addr.sin_addr.S_un,S_addr = inet_addr("127.0.0.1") //將字符串IP轉換爲網絡字節順序排列的IP
7
8 sock.Bind((SOCKADDR*)addr,sizeof(addr)) //綁定套接字與地址結構
9 sock.Listen(5); //監聽端口
3)鏈接服務器
客戶端建立套接字成功之後,能夠調用函數Connect()向服務器發送鏈接請求。函數原型以下:
1 Bool Connect(const const SOCKADDR* lpSockAddr,int nSockAddrLen);
實例:
1 CSocket sock; //建立套接字對象
2 sockaddr_in addr; //定義套接字地址結構變量
3 in_addr in_add; //定義IP地址結構變量
4 addr.sin_family = AF_INET; //指定地址家族爲TCP/IP
5 addr.sin_port = htons(80); //指定端口號
6 addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //將字符串IP轉換爲網絡字節順序排列的IP
7
8 sock.Connect((SOCKADDR*)addr,sizeof(addr)) //鏈接服務器
4)數據交換
不管是服務端,仍是客戶端都是經過函數Send()和Receive()進行數據交換。函數原型:
1 Virtual int Send(const void* lpBuf,int nBufLen,int nFlags = 0); 2 Virtual int Receive(void* lpBuf,int nBufLen,int nFlags = 0);
其中,函數send()用於發送指定緩衝區的數據,函數Recive()用於接收對方發送的數據,並將數據存放在指定緩衝區中。參數lpBuf表示數據緩衝區地址。參數nBufLenvia表示數據緩衝區的大小。參數nFlags表示數據發送或接收的標誌,通常狀況下,該參數均設置爲0。例如,使用這兩個函數進行數據的發送和接收。
1 char buff[] = '123456'; 2 sock.Send(&buff,sizeof(buff),0) 3 sock.Receive(&buff,sizeof(buff),0) 4 Virtual int Receive(void* lpBuf,int nBufLen,int nFlags = 0); 5 Virtual int Receive(void* lpBuf,int nBufLen,int nFlags = 0);
5)關閉套接字對象
1 Virtual void close();sock.Close(); //關閉套接字對象套接字關閉的同時,也將服務器與客戶端之間鏈接關閉了。