




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
第2章Socket套接字編程套接字是由美國伯克利大學(xué)提出并設(shè)計的一種在網(wǎng)絡(luò)中不同主機之間進行數(shù)據(jù)交換的通信橋梁。在實際生活中,人們所使用的網(wǎng)絡(luò)通信軟件功能均是基于Socket套接字作為通信橋梁實現(xiàn)。所以,套接字在網(wǎng)絡(luò)編程中,有著非常重要的作用。本章將向用戶介紹使用Socket套接字編程的相關(guān)概念以及實現(xiàn)方法。2.1尋址方式和字節(jié)順序在講解套接字編程前,用戶需要首先了解一下什么是尋址方式和字節(jié)順序。在Socket套接字編程中,為了準(zhǔn)確定位通信雙方和數(shù)據(jù)傳輸?shù)挠行?、完整性,編程時必須使用統(tǒng)一的尋址方式和字節(jié)排列順序。2.1.1尋址方式因為套接字需要在各種網(wǎng)絡(luò)協(xié)議中使用,所以為了區(qū)分程序所使用的網(wǎng)絡(luò)協(xié)議必須使用統(tǒng)一的尋址方式。例如,在TCP/IP協(xié)議通信中,用戶使用IP地址和端口號進行確定通信雙方。而在其他的協(xié)議中不一定也使用該方式確定通信雙方。在Winsock〔SocketAPI〕中,用戶可以使用TCP/IP地址家族中統(tǒng)一的套接字地址結(jié)構(gòu)解決TCP/IP尋址中可能出現(xiàn)的問題。該套接字地址結(jié)構(gòu)定義如下:structsockaddr_in{
short
sin_family;
//指定地址家族即地址格式
unsignedshort
sin_port;
//端口號碼
structin_addr
sin_addr;
//IP地址
char
sin_zero[8];
//需要指定為0};在這個結(jié)構(gòu)中,成員sin_family指定使用該套接字地址的地址家族。在這里必須設(shè)置為AF_INET,表示程序所使用的地址家族是TCP/IP。注意:該結(jié)構(gòu)的最后一個成員并未實際使用,主要是為了與第一個版本的套接字地址結(jié)構(gòu)大小相同而設(shè)置。在實際使用時,將這8個字節(jié)直接設(shè)為0即可。該結(jié)構(gòu)成員變量sin_addr表示32位的IP地址結(jié)構(gòu)。其結(jié)構(gòu)定義如下:struct
in_addr
{
union{
struct{
unsigned
char
s_b1,s_b2,s_b3,s_b4;
}
S_un_b;
//用4個u_char字符描述IP地址
struct{
unsignedshorts_w1,s_w2;
}S_un_w;
//用2個u_short類型描述IP地址
unsignedlongS_addr;
//用1個u_long類型描述IP地址
}S_un;};通常,用戶在網(wǎng)絡(luò)編程中使用1個u_long類型的字符進行描述IP地址即可。例如,使用IP地址結(jié)構(gòu)in_addr進行描述IP地址“〞。代碼如下:sockaddr_inaddr;addr.sin_addr.S_un.S_addr=inet_addr("218.6.132.5");在程序中,首先定義sockaddr_in結(jié)構(gòu)對象addr,然后為IP地址結(jié)構(gòu)in_addr中的成員S_addr賦值。因為結(jié)構(gòu)成員S_addr所描述的IP地址均為網(wǎng)絡(luò)字節(jié)順序,所以程序調(diào)用inet_addr()函數(shù)將字符串IP轉(zhuǎn)換為以網(wǎng)絡(luò)字節(jié)順序排列的IP地址。2.1.2字節(jié)順序在Socket套接字編程中,傳輸數(shù)據(jù)的排列順序以網(wǎng)絡(luò)字節(jié)順序和主機字節(jié)順序為主。通常情況下,如果用戶將數(shù)據(jù)通過網(wǎng)絡(luò)發(fā)送時,需要將數(shù)據(jù)轉(zhuǎn)換成以網(wǎng)絡(luò)字節(jié)順序排列,否那么可能造成數(shù)據(jù)損壞。如果用戶是將網(wǎng)絡(luò)中接收到的數(shù)據(jù)存儲在本地計算機上,那么需要將數(shù)據(jù)轉(zhuǎn)換成以主機字節(jié)順序排列。從數(shù)據(jù)存儲的角度來講,網(wǎng)絡(luò)字節(jié)順序即將數(shù)據(jù)中最重要的字節(jié)首先進行存儲,而主機字節(jié)順序那么將不重要的字節(jié)首先存儲。注意:IP地址結(jié)構(gòu)in_addr中的成員S_addr的值均是以網(wǎng)絡(luò)字節(jié)順序排列。1.字節(jié)順序轉(zhuǎn)換函數(shù)在Winsock中提供了幾個關(guān)于網(wǎng)絡(luò)字節(jié)順序與主機字節(jié)順序之間的轉(zhuǎn)換函數(shù)。函數(shù)定義如下://將一個u_short類型的IP地址從主機字節(jié)順序轉(zhuǎn)換到網(wǎng)絡(luò)字節(jié)順序u_shorthtons
(u_short
hostshort);//將一個u_long類型的IP地址從主機字節(jié)順序轉(zhuǎn)換到網(wǎng)絡(luò)字節(jié)順序u_longhtonl
(u_long
hostlong);//將一個u_long類型的IP地址從網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換到主機字節(jié)順序u_longntohl
(u_long
netlong);//將一個u_short類型的IP地址從網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換到主機字節(jié)順序u_shortntohs
(u_short
netshort);//將一個字符串IP轉(zhuǎn)換到以網(wǎng)絡(luò)字節(jié)順序排列的IP地址unsignedlonginet_addr
(constcharFAR*cp);//將一個以網(wǎng)絡(luò)字節(jié)順序排列的IP地址轉(zhuǎn)換為一個字符串IPcharFAR*inet_ntoa
(structin_addr
in);以上函數(shù)的使用均與操作系統(tǒng)平臺無關(guān)。因此,用戶使用這些函數(shù)編寫的程序能在所有操作系統(tǒng)平臺中運行。2.實例程序在本節(jié)中,將編寫實例程序向用戶講解字節(jié)順序轉(zhuǎn)換函數(shù)的用法。代碼如下:... //省略局部代碼sockaddr_inaddr; //定義套接字地址結(jié)構(gòu)變量addr.sin_family=AF_INET;//指定地址家族為TCP/IPaddr.sin_port=htons(80); //指定端口號//將字符串IP轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)順序排列的IPaddr.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");//將網(wǎng)絡(luò)字節(jié)順序排列的IP轉(zhuǎn)換為字符串IPcharaddres[]=inet_ntoa(addr.sin_addr.S_un.S_addr);在程序中,用戶首先使用函數(shù)inet_addr()將字符串IP“〞轉(zhuǎn)換為以網(wǎng)絡(luò)字節(jié)順序排列的IP并保存在IP地址結(jié)構(gòu)成員S_addr中。然后,再使用函數(shù)inet_ntoa()那么將該成員所表示的IP值轉(zhuǎn)換成字符串IP。2.1.3Socket相關(guān)函數(shù)由于Windows網(wǎng)絡(luò)程序開發(fā)均是基于Windows套接字實現(xiàn),所以本節(jié)將重點介紹MFC中的CSocket類以及使用CSocket類編程的根本流程。1.創(chuàng)立套接字使用CSocket類創(chuàng)立套接字對象是通過該類的構(gòu)造函數(shù)創(chuàng)立的。其原型如下:CSocket::CSocket();例如,用戶創(chuàng)立CSocket類對象,代碼如下:CSocketsock;如果用戶需要創(chuàng)立套接字對象指針,那么應(yīng)該使用關(guān)鍵字new進行創(chuàng)立。代碼如下:CSocket*sock; //定義套接字指針對象sock=newCSocket; //使用new關(guān)鍵字創(chuàng)立套接字2.綁定地址信息如果用戶創(chuàng)立效勞器套接字,那么用戶應(yīng)該調(diào)用該類的函數(shù)Bind()將套接字對象與效勞器地址信息綁定在一起。其原型如下:BOOLBind(constSOCKADDR*lpSockAddr,intnSockAddrLen);該函數(shù)的作用是將套接字對象與效勞器地址結(jié)構(gòu)綁定在一起。如果函數(shù)調(diào)用成功,那么返回true。否那么,返回false。參數(shù)lpSockAddr指定將要綁定的效勞器地址結(jié)構(gòu),參數(shù)nSockAddrLen表示地址結(jié)構(gòu)的長度。例如,用戶將上面創(chuàng)立的套接字對象與地址結(jié)構(gòu)綁定。代碼如下:CSocketsock; //創(chuàng)立套接字對象sockaddr_inaddr; //定義套接字地址結(jié)構(gòu)變量addr.sin_family=AF_INET; //指定地址家族為TCP/IPaddr.sin_port=htons(80);//指定端口號//將字符串IP轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)順序排列的IPaddr.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");sock.Bind((SOCKADDR*)addr,sizeof(addr));//綁定套接字與地址結(jié)構(gòu)... //省略局部代碼在效勞器端,當(dāng)?shù)刂沸畔⒔壎ㄌ捉幼殖晒?,還需要調(diào)用函數(shù)Listen()在指定端口監(jiān)聽客戶端的連接請求。函數(shù)Listen()的原型如下:BOOLListen(intnConnectionBacklog=5);參數(shù)nConnectionBacklog表示套接字監(jiān)聽客戶端請求的最大數(shù)目。該參數(shù)的有效范圍是1~5。默認為5,表示該套接字只能監(jiān)聽5個客戶端所發(fā)送的連接請求。例如,套接字監(jiān)聽5個客戶端的連接請求,代碼如下:CSocketsock; //創(chuàng)立套接字對象sockaddr_inaddr; //定義套接字地址結(jié)構(gòu)變量addr.sin_family=AF_INET; //指定地址家族為TCP/IPaddr.sin_port=htons(80); //指定端口號//將字符串IP轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)順序排列的IPaddr.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");sock.Bind((SOCKADDR*)addr,sizeof(addr));//綁定套接字與地址結(jié)構(gòu)sock.Listen(5);//監(jiān)聽端口3.連接效勞器客戶端創(chuàng)立套接字成功以后,可以調(diào)用函數(shù)Connect()向效勞器發(fā)送連接請求。函數(shù)原型如下:BOOLConnect(constSOCKADDR*lpSockAddr,intnSockAddrLen);該函數(shù)調(diào)用成功,那么返回true。否那么,將返回false。參數(shù)lpSockAddr表示將連接的效勞器地址結(jié)構(gòu)。參數(shù)nSockAddrLen表示地址結(jié)構(gòu)的長度大小。例如,效勞器IP地址為“〞,端口為80,客戶端連接效勞器,代碼如下:CSocketsock; //創(chuàng)立套接字對象sockaddr_inaddr; //定義套接字地址結(jié)構(gòu)變量addr.sin_family=AF_INET; //指定地址家族為TCP/IPaddr.sin_port=htons(80); //指定端口號//將字符串IP轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)順序排列的IPaddr.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");sock.Connect((SOCKADDR*)addr,sizeof(addr));//連接效勞器4.?dāng)?shù)據(jù)交換無論是效勞器,還是客戶端都是通過函數(shù)Send()和Receive()進行數(shù)據(jù)交換。函數(shù)原型如下:virtualintSend(constvoid*lpBuf,intnBufLen,intnFlags=0);virtualintReceive(void*lpBuf,intnBufLen,intnFlags=0);其中,函數(shù)Send()用于發(fā)送指定緩沖區(qū)的數(shù)據(jù),函數(shù)Receive()用于接收對方發(fā)送的數(shù)據(jù),并將數(shù)據(jù)存放在指定緩沖區(qū)中。參數(shù)lpBuf表示數(shù)據(jù)緩沖區(qū)地址。參數(shù)nBufLen表示數(shù)據(jù)緩沖區(qū)的大小。參數(shù)nFlags表示數(shù)據(jù)發(fā)送或接收的標(biāo)志,一般情況下,該參數(shù)均設(shè)置為0。例如,使用這兩個函數(shù)進行數(shù)據(jù)的發(fā)送和接收。代碼如下:... //省略局部代碼charbuff[]='a'; //定義并初始化數(shù)據(jù)緩沖區(qū)sock.Send(&buff,sizeof(buff),0);//發(fā)送數(shù)據(jù)緩沖區(qū)中的數(shù)據(jù)sock.Receive(&buff,sizeof(buff),0);//接收數(shù)據(jù)并將數(shù)據(jù)存放在數(shù)據(jù)緩沖區(qū)中5.關(guān)閉套接字對象當(dāng)效勞器和客戶端的通信完成以后,用戶還必須調(diào)用函數(shù)Close()將套接字對象關(guān)閉。否那么,程序可能在退出時發(fā)生錯誤。該函數(shù)原型如下:virtualvoidClose();例如,客戶端關(guān)閉套接字對象,代碼如下:... //省略局部代碼sock.Close(); //關(guān)閉套接字對象套接字關(guān)閉的同時,也將效勞器與客戶端之間連接關(guān)閉了。本節(jié)主要向用戶介紹了CSocket類的常用函數(shù)以及用法。當(dāng)用戶創(chuàng)立VC應(yīng)用程序時,如果沒有為應(yīng)用程序指定支持WindowsSocket,那么用戶必須手動添加該類的頭文件afxsock.h。否那么,程序?qū)⒉荒苁褂肅Socket類。2.2Winsock網(wǎng)絡(luò)程序開發(fā)流程本節(jié)將向用戶講述基于WindowsSocket的應(yīng)用程序開發(fā)步驟,并將編寫實例程序向用戶介紹網(wǎng)絡(luò)應(yīng)用程序的開發(fā)過程以及CSocket類的具體使用方法。本節(jié)中的實例程序均在VC中進行編寫、調(diào)試。2.2.1VC中創(chuàng)立工程的步驟用戶在VC中使用應(yīng)用程序向?qū)?chuàng)立基于套接字的應(yīng)用程序工程時,必須為該應(yīng)用程序指定支持WindowsSocket功能。否那么,創(chuàng)立的應(yīng)用程序不能進行網(wǎng)絡(luò)通信。如果用戶創(chuàng)立工程工程成功,那么在應(yīng)用程序向?qū)гO(shè)置的第二步,將詢問用戶是否需要在工程中支持WindowsSocket功能,如圖2.1所示。圖2.1支持WindowsSocket功能如果用戶在應(yīng)用程序的第二步?jīng)]有選擇工程支持WindowsSocket功能,那么在程序中手動添加代碼也可以到達同樣的目的。其代碼如下:#include<afxsock.h> //包含CSocket類的頭文件注意:頭文件afxsock.h中包含了CSocket類的變量以及函數(shù)定義。2.2.2Winsock編程流程在本書的第1章中,已經(jīng)向用戶介紹了Winsock函數(shù)是用于網(wǎng)絡(luò)編程的WindowsAPI函數(shù)。本章在前一節(jié)中,向用戶介紹了CSocket類的根本編程流程。所以,在本節(jié)中將向用戶介紹使用SocketAPI函數(shù)進行網(wǎng)絡(luò)程序開發(fā)的根本流程與方法。1.初始化和釋放套接字庫由于所有的Winsock函數(shù)均是從動態(tài)鏈接庫WS2_32.DLL中導(dǎo)出的,但是,VC在默認情況下并沒有與該庫進行連接。所以,用戶需要在VC中進行相關(guān)設(shè)置,使其連接動態(tài)庫WS2_32.DLL。添加方法是選擇“Project〞|“Settings〞命令,將彈出ProjectSettings對話框,如圖2.2所示。圖2.2添加動態(tài)鏈接庫用戶在工程設(shè)置對話中,可以修改或添加庫模塊〔如圖2.2所示〕。在庫模塊中添加動態(tài)鏈接庫WS2_32.DLL。這樣,程序就可以調(diào)用Winsock函數(shù)了。用戶必須首先從動態(tài)鏈接庫中調(diào)用函數(shù)WSAStartup()對該庫進行初始化,之后才能從該庫中繼續(xù)正確調(diào)用其他Winsock函數(shù)。否那么,將出現(xiàn)錯誤。函數(shù)WSAStartup()的原型如下:intWSAStartup(WORDwVersionRequested,LPWSADATAlpWSAData);該函數(shù)調(diào)用成功,將返回0。否那么,調(diào)用函數(shù)失敗。參數(shù)wVersionRequested表示當(dāng)前套接字庫的版本號。例如,當(dāng)前套接字版本號為2.0,那么將該參數(shù)設(shè)置為2.0。代碼如下:WORDwVersionRequested=MAKEWORD(2,0);參數(shù)lpWSAData指向結(jié)構(gòu)體WSADATA的指針變量,表示獲取到的套接字庫詳細信息。該結(jié)構(gòu)體定義如下:typedefstructWSAData
{
WORD
wVersion;
//庫文件建議應(yīng)用程序使用的版本號
WORD
wHighVersion;
//庫文件支持的最高版本
char
szDescription[WSADESCRIPTION_LEN+1];
//描述庫文件的字符串
char
szSystemStatus[WSASYS_STATUS_LEN+1];
//系統(tǒng)狀態(tài)字符串
unsignedshort
iMaxSockets;
//同時支持的最大套接字數(shù)
unsignedshort
iMaxUdpDg;
//已廢棄
charFAR*lpVendorInfo;
//已廢棄}
WSADATA,FAR*LPWSADATA;用戶初始化套接字庫,代碼如下:WSAData
data;
//定義WSAData變量WORDwVersionRequested=MAKEWORD(2,0);
//定義套接字庫版本號::WSAStartup(wVersionRequested,&data);
//初始化套接字庫當(dāng)程序退出時,用戶還應(yīng)該調(diào)用函數(shù)WSACleanup()釋放該套接字庫。代碼如下:::WSACleanup();2.創(chuàng)立套接字句柄在SocketAPI中,創(chuàng)立套接字句柄的函數(shù)是socket()。該函數(shù)原型如下:SOCKETsocket(intaf,//指定套接字所使用的地址格式,在本章中只能設(shè)置為AF_INETinttype,//套接字類型intprotocol//如果參數(shù)type已經(jīng)指定套接字類型為TCP或UDP,那么該參數(shù)可以設(shè)置為0);該函數(shù)執(zhí)行成功,將返回新創(chuàng)立的套接字句柄。否那么,將返回INVALID_SOCKET表示失敗。參數(shù)type的取值如表2.1所示。表2.1套接字類型取值例如,創(chuàng)立流式套接字的句柄。代碼如下:SOCKETs; //定義套接字句柄s=::socket(AF_INET,SOCK_STREAM,0);//創(chuàng)立并返回套接字句柄
套接字類型取值
含
義SOCK_STREAM創(chuàng)建流式套接字(基于TCP協(xié)議)SOCK_DGRAM創(chuàng)建數(shù)據(jù)報套接字(基于UDP協(xié)議)SOCK_RAW創(chuàng)建原始套接字(本書中未使用)3.綁定地址信息對于效勞器而言,套接字創(chuàng)立成功后,還應(yīng)該將套接字與地址結(jié)構(gòu)信息相關(guān)聯(lián)。實現(xiàn)這一功能的函數(shù)是bind()。該函數(shù)原型如下:intbind(SOCKETs, //套接字句柄conststructsockaddrFAR*name,//地址結(jié)構(gòu)信息intnamelen //地址結(jié)構(gòu)的大小);該函數(shù)調(diào)用成功,那么返回0。否那么,函數(shù)調(diào)用失敗。例如,將套接字句柄綁定到本地地址,代碼如下:... //省略局部代碼sockaddr_inaddr; //定義套接字地址結(jié)構(gòu)變量addr.sin_family=AF_INET; //指定地址家族為TCP/IPaddr.sin_port=htons(80);//指定端口號addr.sin_addr.S_un.S_addr=INADDR_ANY //表示效勞器能夠接收任何計算機發(fā)來的請求::bind(s,(sockaddr)&addr,sizeof(addr));//綁定套接字到指定地址結(jié)構(gòu)當(dāng)效勞器程序?qū)⑻捉幼志浔壎ㄌ捉幼值刂烦晒r,那么調(diào)用函數(shù)listen()實現(xiàn)監(jiān)聽端口的功能。該函數(shù)原型如下:intlisten(SOCKETs, //實現(xiàn)監(jiān)聽功能的套接字句柄intbacklog //指定監(jiān)聽的最大連接數(shù)量);該函數(shù)僅被用于流式套接字上。如果多個客戶端同時向效勞器發(fā)出連接請求,并且已超過了最大監(jiān)聽數(shù),那么客戶端將返回錯誤代碼。例如,程序在已創(chuàng)立的套接字s上進行監(jiān)聽,代碼如下:... //省略局部代碼::listen(s,5);//在套接字上進行監(jiān)聽,并且將最大監(jiān)聽數(shù)指定為54.連接客戶端程序連接效勞器使用函數(shù)connect()實現(xiàn)。函數(shù)原型如下:intconnect(SOCKETs, //套接字句柄conststructsockaddrFAR*name,//將要連接的效勞器地址信息結(jié)構(gòu)指針intnamelen //地址信息結(jié)構(gòu)體長度);例如,客戶端使用該函數(shù)連接地址為“〞,端口為80的效勞器。代碼如下:sockaddr_inaddr; //定義套接字地址結(jié)構(gòu)變量addr.sin_family=AF_INET; //指定地址家族為TCP/IPaddr.sin_port=htons(80); //指定端口號addr.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");//指定效勞器地址SOCKETs;//定義套接字句柄s=::socket(AF_INET,SOCK_STREAM,0); //創(chuàng)立并返回套接字句柄::connect(s,(sockaddr)&addr,sizeof(addr));//連接效勞器...如果效勞器接收到客戶端的連接請求,那么可以調(diào)用函數(shù)accept()接受該請求。函數(shù)原型如下:SOCKETaccept(SOCKETs, //套接字句柄 structsockaddrFAR*addr, //獲取連接對方的地址信息intFAR*addrlen //地址長度);該函數(shù)如果調(diào)用成功,那么返回一個新的套接字句柄,用于通信雙方數(shù)據(jù)的傳輸。5.?dāng)?shù)據(jù)收發(fā)當(dāng)用戶使用Winsock編程時,都是調(diào)用函數(shù)send()和recv()進行數(shù)據(jù)的發(fā)送和接收。函數(shù)原型如下://發(fā)送數(shù)據(jù)函數(shù)intsend(SOCKET
s,constcharFAR*buf,int
len,intflags);//接收數(shù)據(jù)函數(shù)intrecv(SOCKET
s,charFAR*buf,int
len,int
flags);
兩個函數(shù)的各個參數(shù)表示的意義均相同。參數(shù)buf是指向數(shù)據(jù)緩沖區(qū)的指針變量,參數(shù)flags通常設(shè)置為0。注意:如果效勞器使用上面的函數(shù)進行數(shù)據(jù)收發(fā),那么參數(shù)s應(yīng)該為監(jiān)聽函數(shù)返回的新套接字句柄。如果客戶端使用以上函數(shù)進行數(shù)據(jù)收發(fā),那么參數(shù)s應(yīng)該為客戶端創(chuàng)立的套接字句柄。6.關(guān)閉套接字當(dāng)套接字使用完畢或程序退出時,用戶應(yīng)該調(diào)用函數(shù)closesocket()關(guān)閉套接字句柄。函數(shù)原型如下:intclosesocket(SOCKETs//將關(guān)閉的套接字句柄);參數(shù)s表示即將關(guān)閉的套接字句柄。例如,用戶關(guān)閉前面創(chuàng)立的套接字句柄s,代碼如下:::closesocket(s);本節(jié)主要向用戶講述了使用Winsock函數(shù)進行程序設(shè)計的根本流程,并講解了局部常用函數(shù)的用法等知識。希望用戶在實際編程的過程中,能不斷地對本節(jié)知識進行回憶,加深理解。2.2.3基于TCP的Sockets編程在本節(jié)中,將編寫一個簡單的TCP效勞器和TCP客戶端程序。這兩個實例程序均為控制臺程序窗口。1.TCP效勞器首先,在VC中新建一個基于控制臺的應(yīng)用程序工程,并將該工程命名為“TCP效勞器〞,如圖2.3所示。圖2.3新建控制臺應(yīng)用程序然后,單擊“OK〞按鈕進行應(yīng)用程序類型的設(shè)置。在本節(jié)中,將新建的控制臺程序類型指定為一個空工程,如圖2.4所示。圖2.4設(shè)置空的控制臺程序用戶還需要在VC中添加一個空白的C++源文件,名稱為TCPSEVER,如圖2.5所示。圖2.5新建C++資源文件用戶在新建的C++源文件中進行代碼編寫。代碼如下:編譯并運行程序,如圖2.6所示。圖2.6效勞器啟動界面效勞器程序啟動以后,如果沒有客戶端向其發(fā)送連接請求,那么效勞器將一直等待直到有客戶端程序連接。2.TCP客戶端在VC中創(chuàng)立基于控制臺的應(yīng)用程序,命名為“TCP客戶端〞。其方法與TCP效勞器的創(chuàng)立過程相同。所以,在這里不再贅述,請讀者復(fù)習(xí)前面的相關(guān)內(nèi)容。在新建的C++源文件TCPClient中,用戶可以編寫客戶端的功能代碼。代碼如下:編譯并運行程序,如圖2.7所示。圖2.7客戶端啟動界面如果用戶首先翻開效勞器程序,再翻開客戶端程序,那么效勞器會接受客戶端的連接請求,而客戶端會顯示效勞器發(fā)送的歡送信息,如圖2.8所示。圖2.8翻開效勞器與客戶端本節(jié)向用戶講解了TCP效勞器與客戶端的通信過程,并編寫了實例代碼。用戶在學(xué)習(xí)的過程中,如果對本章實例有興趣,可以將隨書光盤中的相應(yīng)的實例代碼進行改寫,以到達自己的要求。2.2.4基于UDP的Sockets編程基于UDP的網(wǎng)絡(luò)程序是面向無連接,不可靠的一種應(yīng)用程序。所以,當(dāng)程序創(chuàng)立套接字句柄成功以后,便可以直接調(diào)用函數(shù)進行數(shù)據(jù)收發(fā),最后,關(guān)閉套接字對象。在整個過程中,程序都不用調(diào)用任何函數(shù)連接效勞器或者接受客戶端的連接等操作。這種類型的應(yīng)用程序多用在即時通信中。在UDP中進行數(shù)據(jù)收發(fā)的函數(shù)是sendto()和recvfrom()。函數(shù)原型如下:intsendto(//發(fā)送函數(shù)
SOCKET
s,//套接字句柄
constcharFAR*buf,//數(shù)據(jù)緩沖區(qū)
int
len,//數(shù)據(jù)的長度
int
flags,//一般設(shè)置為0
conststructsockaddrFAR*to,//目標(biāo)地址結(jié)構(gòu)信息
inttolen//目標(biāo)地址結(jié)構(gòu)大小);intrecvfrom(SOCKET
s,charFAR*buf,int
len,int
flags,structsockaddrFAR*from,intFAR*fromlen);
//接收函數(shù)函數(shù)recvfrom()的各個參數(shù)與函數(shù)sendto()的參數(shù)根本一致。參數(shù)from是指向地址結(jié)構(gòu)sockaddr_in的指針,表示數(shù)據(jù)發(fā)送方的地址信息。參數(shù)fromlen表示該地址結(jié)構(gòu)的大小。1.UDP效勞器首先,在VC中創(chuàng)立基于控制臺程序窗口的應(yīng)用程序,并命名為“UDP效勞器〞,如圖2.9所示。圖2.9新建UDP效勞器然后,將該工程類型同樣指定為空工程。在新建的工程中新建一個C++源文件,名稱為UDPSever,如圖2.10所示。圖2.10新建C++源文件現(xiàn)在用戶可以在該源文件中編寫UDP效勞器的代碼。代碼如下:編譯并運行程序,如圖2.11所示。圖2.11UDP效勞器啟動界面2.UDP客戶端在VC中創(chuàng)立UDP客戶端程序時,與UDP效勞器相同,工程類型均為空工程。所以,用戶只需在C++源文件中編寫代碼實現(xiàn)UDP客戶端。代碼如下:編譯并運行程序,如圖2.12所示。圖2.12客戶端啟動界面如果用戶先啟動UDP效勞器,再啟動UDP客戶端,那么會在效勞器界面中顯示客戶端連接信息。而客戶端界面中顯示效勞器發(fā)送的信息,如圖2.13所示。圖2.13UDP客戶端與效勞器進行通信在本小節(jié)中,向用戶講解了在VC中使用Winsock函數(shù)進行網(wǎng)絡(luò)程序開發(fā),并結(jié)合TCP與UDP實例程序介紹了基于以上兩種協(xié)議的網(wǎng)絡(luò)程序編寫方法。2.3網(wǎng)絡(luò)程序?qū)嵗龖?yīng)用用戶通過本章前面兩節(jié)知識的學(xué)習(xí),已經(jīng)對網(wǎng)絡(luò)程序的根本原理和程序編寫方法有了進一步了解。在本節(jié)中,將引導(dǎo)用戶在VC中編寫基于對話框的TCP效勞器和TCP客戶端程序并且進行詳細講解。2.3.1TCP客戶端程序在本小節(jié)中,將向用戶介紹在VC中創(chuàng)立基于對話框模式的TCP客戶端程序界面以及各個功能的實現(xiàn)等。1.創(chuàng)立工程在VC中創(chuàng)立一個基于MFC的應(yīng)用程序工程,并且將該工程名修改為“TCP客戶端程序〞。步驟如下:〔1〕選擇“File〞|“New〞命令,翻開“New〞對話框,如圖2.14所示。圖2.14創(chuàng)立TCP客戶端實例工程〔2〕單擊“OK〞按鈕,進入應(yīng)用程序向?qū)гO(shè)置的第一步,修改應(yīng)用程序的類型為“Dialogbased〞,如圖2.15所示。圖2.15修改應(yīng)用程序類型為根本對話框〔3〕單擊“Next〞按鈕,進入應(yīng)用程序向?qū)гO(shè)置的第二步,設(shè)置應(yīng)用程序支持WindowsSocket的功能,如圖2.16所示。圖2.16設(shè)置應(yīng)用程序支持套接字功能〔4〕單擊“Finish〞按鈕,完成工程的創(chuàng)立以及相關(guān)配置?,F(xiàn)在,用戶通過應(yīng)用程序向?qū)б呀?jīng)完成了TCP客戶端工程的創(chuàng)立以及為該工程添加了支持Windows套接字功能等相關(guān)的一些配置。接下來,用戶需要翻開該工程的資源管理器進行程序界面的設(shè)計。2.界面設(shè)計當(dāng)工程創(chuàng)立以后,用戶可以翻開資源管理器查看該工程的對話框資源,如圖2.17所示。圖2.17VC默認情況下的對話框資源用戶可以通過向該對話框面板中添加相應(yīng)的控件,以到達TCP客戶端程序的根本功能,如圖2.18所示。圖2.18完成設(shè)計后的界面效果其中,用戶添加了多個控件,新添加的控件ID、類型以及作用,如表2.2所示。表2.2控件ID、類型以及作用控件ID控件類型控件在實例中的作用IDC_ADDR編輯框控件輸入服務(wù)器IP地址IDC_PORT編輯框控件輸入服務(wù)器端口IDC_TEXT編輯框控件顯示相關(guān)信息IDC_SENDTEXT編輯框控件輸入發(fā)送消息IDC_SEND按鈕控件發(fā)送消息按鈕IDC_CONNECT按鈕控件連接服務(wù)器IDC_STATIC1靜態(tài)控件標(biāo)識服務(wù)器地址IDC_STATIC2靜態(tài)控件標(biāo)識服務(wù)器端口3.界面初始化TCP客戶端程序啟動時,應(yīng)該首先連接效勞器以后,用戶才能通過程序發(fā)送消息。所以,該程序初始化時的界面,如圖2.19所示。圖2.19程序初始化界面在界面初始化時,已經(jīng)屏蔽了發(fā)送消息的功能。所以對于應(yīng)用程序而言,防止了錯誤的發(fā)生。初始化代碼如下:用戶使用函數(shù)GetDlgItem()獲取對應(yīng)ID控件的指針,然后使用該指針調(diào)用函數(shù)EnableWindow()將控件禁用。函數(shù)EnableWindow()的參數(shù)如果為true,那么表示該控件可以被使用。如果該參數(shù)為false,那么表示該控件被禁用。在界面初始化時,除了初始化界面中的各按鈕之外,還應(yīng)該對套接字進行初始化。初始化套接字功能的代碼應(yīng)該在函數(shù)CTCPDlg::OnInitDialog()中實現(xiàn)。代碼如下:在代碼中,用戶在類中聲明了套接字對象和套接字地址結(jié)構(gòu)變量。然后,在初始化函數(shù)中創(chuàng)立了TCP套接字s。4.功能實現(xiàn)在這一節(jié)中,用戶可以為各個功能控件編寫相應(yīng)的代碼,以實現(xiàn)其功能。首先,為“連接〞按鈕添加消息響應(yīng)函數(shù)。在該控件上雙擊鼠標(biāo),將彈出AddMemberFunction〔添加成員函數(shù)〕對話框,如圖2.20所示。圖2.20添加成員函數(shù)對話框在該對話框中,用戶可以將“連接〞按鈕的消息響應(yīng)函數(shù)名修改為OnConnect()。函數(shù)代碼如下:將上面的代碼保存以后,進行編譯并運行。如果客戶端連接效勞器成功,那么程序會提示用戶連接成功,如圖2.21所示。圖2.21客戶端連接效勞器成功否那么,程序提示用戶連接效勞器失敗,如圖2.22所示。圖2.22客戶端連接效勞器失敗當(dāng)客戶端與效勞器連接成功之后,用戶便可以發(fā)送消息到效勞器了。現(xiàn)在,用戶需要為“發(fā)送〞按鈕添加相應(yīng)的消息響應(yīng)函數(shù),并指定該函數(shù)名為OnSend()。該函數(shù)相關(guān)代碼如下:在代碼中,用戶通過調(diào)用函數(shù)send()將消息發(fā)送到指定的效勞器,并將該消息顯示在本地的信息顯示框中,如圖2.23所示。圖2.23客戶端發(fā)送消息作為客戶端,還應(yīng)該具有接收并顯示效勞器所發(fā)送的消息。在本實例中,將采用異步套接字模式實現(xiàn)該功能。在VC中,將套接字設(shè)置為異步模式,可以調(diào)用函數(shù)WSAAsyncSelect()實現(xiàn)。該函數(shù)原型如下:intWSAAsyncSelect(SOCKETs,HWNDhWnd,unsignedintwMsg,longlEvent); 函數(shù)各個參數(shù)如下:參數(shù)s表示需要設(shè)置為異步模式的套接字句柄。參數(shù)hWnd表示接收消息響應(yīng)的窗口句柄。參數(shù)wMsg表示響應(yīng)消息標(biāo)識。參數(shù)lEvent表示發(fā)生在該套接字上的事件,取值如表2.3所示。表2.3套接字事件局部標(biāo)識及其意義套接字事件取值含義表示套接字事件取值含義表示FD_READ套接字上發(fā)生讀取事件FD_ACCEPT套接字上發(fā)生連接事件FD_WRITE套接字上發(fā)生寫入事件FD_CLOSE套接字上發(fā)生關(guān)閉事件首先,在程序初始化函數(shù)OnInitDialog中,將套接字設(shè)置為異步模式。代碼如下:代碼中,將異步套接字處理的事件指定為讀取事件FD_READ,并且將該事件的處理消息指定為WM_SOCKET。該消息是在CTCPDlg類頭文件中定義的自定義消息。代碼如下:用戶自定義消息以及該消息的響應(yīng)函數(shù)成功后,還需要在消息映射表中將消息與響應(yīng)函數(shù)相關(guān)聯(lián)。代碼如下:最后,在自定義消息響應(yīng)函數(shù)OnSocket()中,實現(xiàn)套接字事件的處理。代碼如下:由于在本實例中,僅處理了套接字的讀取事件,所以使用了代碼“if(lParam==FD_READ)〞。如果用戶需要處理的套接字事件比較多,那么應(yīng)該在代碼中使用關(guān)鍵字switch進行分類判斷。程序運行效果如圖2.24所示。圖2.24程序運行效果到這里,用戶根本上完成了客戶端應(yīng)有的功能。在客戶端程序中,需要用戶注意連接效勞器之前,必須首先知道效勞器的IP地址等相關(guān)信息。否那么,程序?qū)o法正確連接到效勞器。2.3.2TCP效勞器程序在節(jié)中,已經(jīng)向用戶講解了制作TCP客戶端程序的相關(guān)方法。所以,在本節(jié)中將向用戶繼續(xù)講解在VC中怎樣制作TCP效勞器程序。1.創(chuàng)立工程在VC中,創(chuàng)立TCP效勞器工程的步驟與創(chuàng)立TCP客戶端工程的步驟一樣,只是在修改工程名稱時應(yīng)該為“TCP效勞器程序〞,如圖2.25所示。圖2.25創(chuàng)立TCP效勞器程序工程其他相關(guān)設(shè)置步驟均與TCP客戶端工程的設(shè)置步驟一樣。所以,在本章中不再對此內(nèi)容進行講述,請用戶復(fù)習(xí)上一節(jié)中的內(nèi)容。注意:用戶在VC中創(chuàng)立實例工程的步驟大體相同。2.界面設(shè)計效勞器工程創(chuàng)立完成之后,用戶可以翻開資源管理器中的
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 光伏變壓器知識培訓(xùn)課件
- 2025年法制宣傳日普法知識競賽搶答題庫及答案(共80題)
- 新員工入職培訓(xùn)流程與要點
- 《藝術(shù)概論:西方繪畫藝術(shù)的發(fā)展歷程及特點》
- 共享經(jīng)濟與協(xié)作式消費作業(yè)指導(dǎo)書
- 福建省龍巖市2024-2025學(xué)年高二上學(xué)期1月期末生物學(xué)試題(含答案)
- 兒童繪本中的教育意義解讀
- 人力資源外包合作協(xié)議
- 小學(xué)生讀書筆記讀后感
- 水資源開發(fā)與保護聯(lián)合協(xié)議
- 北京工裝合同范本
- 建筑工地道路養(yǎng)護的進度與措施
- 加油站合作經(jīng)營協(xié)議書范本
- 《苗圃生產(chǎn)與管理》教案-第二章 園林苗木的種實生產(chǎn)
- 2025年西安鐵路職業(yè)技術(shù)學(xué)院高職單招高職單招英語2016-2024歷年頻考點試題含答案解析
- 化工原理完整(天大版)課件
- 2025年陜西延長石油有限責(zé)任公司招聘筆試參考題庫含答案解析
- 《淞滬會戰(zhàn)》課件
- Excel辦公技巧培訓(xùn)
- 新時代大學(xué)生勞動教育 課件 第5章 勞動素養(yǎng)及其養(yǎng)成
- 2024年度英語課件容貌焦慮
評論
0/150
提交評論