高靈霞
摘要:闡述了Winsock的相關(guān)概念,介紹了套接字的相關(guān)技術(shù)和套接字編程原理,分析實(shí)現(xiàn)網(wǎng)絡(luò)通信的面向連接的套接字編程模型和面向無連接的套接字編程模型,給出了在VC環(huán)境下利用Winsock開發(fā)網(wǎng)絡(luò)應(yīng)用程序的具體方法和程序代碼,來實(shí)現(xiàn)面向連接的網(wǎng)絡(luò)通信。
關(guān)鍵詞:TCP/IP協(xié)議;套接字;網(wǎng)絡(luò)編程;Winsock
中圖分類號:TP393文獻(xiàn)標(biāo)識碼:A文章編號:1009-3044(2012)20-4819-04
On the Realization of Network Communication by Implementing WinSock in VC
GAO Ling-xia
(Chongqing College of Electronic Engineering, Department of Computer Science, Chongqing 401331,China)
Abstract: This paper describes the related concepts of Winsock, introduces the related technology of socket and programming principle of socket, analysis of network communication of connection-oriented socket programming model and connectionless socket programming model. In VC environment, development of network application methods and program code , Using Winsock to realize connection of ori ented network communication.
Key words: TCP/IP protocol; socket ; network programming; winsock
為了實(shí)現(xiàn)C/S模型的網(wǎng)絡(luò)編程,90年代初,由Microsoft聯(lián)合了其他幾家公司共同制定了一套WINDOWS下的網(wǎng)絡(luò)編程接口[1],即Windows Sockets規(guī)范,它不是一種網(wǎng)絡(luò)協(xié)議,而是一套開放的、支持多種協(xié)議的Windows下的網(wǎng)絡(luò)編程接口。Socket(套接字)實(shí)際在計(jì)算機(jī)中提供了一個(gè)通信端口,可以通過這個(gè)端口與任何一個(gè)具有Socket接口的計(jì)算機(jī)通信。應(yīng)用程序在網(wǎng)絡(luò)上傳輸,接收的信息都通過這個(gè)Socket接口來實(shí)現(xiàn)。現(xiàn)在Socket接口幾乎是TCP/IP網(wǎng)絡(luò)標(biāo)準(zhǔn)API,很多TCP/IP的網(wǎng)絡(luò)應(yīng)用程序都是基于Socket而編寫的。
雖然現(xiàn)在有很多工具如Web瀏覽器、FTP程序可以在網(wǎng)絡(luò)上傳輸數(shù)據(jù)和文件,但是通過WinSock編程有更大的靈活性,并且不需關(guān)心網(wǎng)絡(luò)連接細(xì)節(jié)。
1 Client/Server (客戶機(jī)/服務(wù)器)模型
在網(wǎng)絡(luò)編程中最常用的方案便是Client/Server (客戶機(jī)/服務(wù)器)模型[2]。在這種方案中客戶應(yīng)用程序向服務(wù)器程序請求服務(wù)。一個(gè)服務(wù)程序通常在一個(gè)眾所周知的地址監(jiān)聽對服務(wù)的請求,也就是說,服務(wù)進(jìn)程一直處于休眠狀態(tài),直到一個(gè)客戶向這個(gè)服務(wù)的地址提出了連接請求。在這個(gè)時(shí)刻,服務(wù)程序被"驚醒"并且為客戶提供服務(wù)-對客戶的請求作出適當(dāng)?shù)姆磻?yīng)。通信過程如圖1所示。
2套接字
套接字是網(wǎng)絡(luò)計(jì)算機(jī)與應(yīng)用程序之間發(fā)送和接收數(shù)據(jù)方式的一種抽象描述[1]。用來實(shí)現(xiàn)主機(jī)和主機(jī)通信的一個(gè)接口,完成主機(jī)間的通信操作。它位于協(xié)議之上,屏蔽了底層的協(xié)議,能夠?qū)崿F(xiàn)各種類型的通信操作。它是網(wǎng)絡(luò)通信中應(yīng)用程序?qū)?yīng)的進(jìn)程和網(wǎng)絡(luò)協(xié)議之間的接口。網(wǎng)絡(luò)應(yīng)用程序調(diào)用Winsock API函數(shù)實(shí)現(xiàn)相互之間的通信,同時(shí),Winsock利用下層的網(wǎng)絡(luò)通信協(xié)議和操作系統(tǒng)實(shí)現(xiàn)實(shí)際的通信,如圖2所示。
圖2應(yīng)用進(jìn)程使用Winsock進(jìn)行通信
在網(wǎng)絡(luò)中進(jìn)行通信,至少需要一對套接字,其中一個(gè)運(yùn)行于客戶機(jī)端,稱之為ClientSocket[3];另一個(gè)運(yùn)行于服務(wù)器端,稱之為Ser verSocket。套接字主要有三個(gè)參數(shù),分別為通信的目的IP地址,使用的傳輸層協(xié)議(TCP或UDP)和使用的端口。根據(jù)這些參數(shù),應(yīng)用層就可以和傳輸層通過socket接口,實(shí)現(xiàn)數(shù)據(jù)傳輸。
2.1 Windows Sockets的相關(guān)技術(shù)分析
2.1.1 Windows Sockets的版本
常用的Windows Sockets有兩個(gè)版本:WinSock 1.1網(wǎng)絡(luò)編程接口和WinSock2.2網(wǎng)絡(luò)編程接口。WinSock1.1由動態(tài)鏈接庫WIN SOCK.DLL支持,主要應(yīng)用在Windows95中,WinSock2.2由動態(tài)鏈接庫WINSOCK32.DLL支持,主要應(yīng)用在Windows98和Win dows2000中。WinSock2.2與低版本W(wǎng)inSock1.1相比,主要對一些協(xié)議進(jìn)行了擴(kuò)充,如IPX、NETBIOS等,同時(shí)對WinSock1.1的函數(shù)完成兼容。
2.1.2阻塞與非阻塞
套接字具備兩種模式[4]:阻塞和非阻塞模式。當(dāng)套接字處于阻塞模式時(shí)調(diào)用高模式的函數(shù)時(shí),需要消耗一定的時(shí)間來等待操作的完成,而當(dāng)套接字處于非阻塞模式時(shí),調(diào)用函數(shù)立即返回,但通常會出錯(cuò)。當(dāng)一特定的WinSock API函數(shù)指出一個(gè)錯(cuò)誤發(fā)生時(shí),需要獲取對應(yīng)的錯(cuò)誤代碼,根據(jù)WinSock版本的不同,包含在不同的頭文件中。
2.1.3網(wǎng)絡(luò)字節(jié)順序
網(wǎng)絡(luò)字節(jié)就是網(wǎng)絡(luò)上描述整數(shù)或浮點(diǎn)數(shù)的字節(jié)發(fā)送順序(哪個(gè)字節(jié)被先發(fā)出去,哪個(gè)字節(jié)后發(fā)出去)。網(wǎng)絡(luò)字節(jié)通常先傳遞高字節(jié),而再傳遞低字節(jié)。而主機(jī)字節(jié)就是計(jì)算機(jī)內(nèi)存中存放的整數(shù)或浮點(diǎn)數(shù)的方式,字節(jié)在內(nèi)存中低字節(jié)在前,高字節(jié)在后。為了數(shù)據(jù)的一致性,就要把本地的數(shù)據(jù)轉(zhuǎn)換成網(wǎng)絡(luò)使用的格式,然后發(fā)送出去,接收的時(shí)候要轉(zhuǎn)換成主機(jī)方式才能使用。下面的API函數(shù)將從主機(jī)字節(jié)順序向網(wǎng)絡(luò)字節(jié)順序的轉(zhuǎn)換:
1)htonl():參數(shù)是主機(jī)字節(jié)順序的一個(gè)4字節(jié)數(shù),函數(shù)返回網(wǎng)絡(luò)字節(jié)順序的數(shù)。
2)WSAHtonl():參數(shù)是主機(jī)字節(jié)順序的一個(gè)4字節(jié)數(shù),函數(shù)返回網(wǎng)絡(luò)字節(jié)順序的數(shù)。3)htons():參數(shù)是主機(jī)字節(jié)順序的一個(gè)2字節(jié)數(shù),函數(shù)返回網(wǎng)絡(luò)字節(jié)順序的數(shù)。
4)WSAHtons():參數(shù)是主機(jī)字節(jié)順序的一個(gè)2字節(jié)數(shù),函數(shù)返回網(wǎng)絡(luò)字節(jié)順序的數(shù)。
2.2 WinSock技術(shù)特點(diǎn)
2.2.1套接字的分類
套接字根據(jù)通信的性質(zhì)可以分為三類,在設(shè)計(jì)網(wǎng)絡(luò)應(yīng)用程序時(shí),根據(jù)不同的要求來選擇相應(yīng)的套接字,只有相同類型的套接字才能相互通信。
流套接字(SOCK_STREAM)。提供一個(gè)面向連接可靠的數(shù)據(jù)傳輸服務(wù),數(shù)據(jù)無差錯(cuò)、無重復(fù)地發(fā)送,且按發(fā)送順序接收。
數(shù)據(jù)報(bào)式套接字(SOCK_DGRAM)。提供一個(gè)無連接服務(wù)。數(shù)據(jù)包以獨(dú)立包形式被發(fā)送,不提供無錯(cuò)保證,數(shù)據(jù)可能丟失或重復(fù),并且接收順序混亂。
原始式套接字(SOCK_RAW)。該接口允許對較低層協(xié)議,如IP、ICMP直接訪問。常用于檢驗(yàn)新的協(xié)議實(shí)現(xiàn)或訪問現(xiàn)有服務(wù)中配置的新設(shè)備,一般不提供給普通用戶。
2.2.2異步選擇機(jī)制
Windows Sockets的異步選擇機(jī)制為套接字編程提供了一種網(wǎng)絡(luò)事件驅(qū)動的程序設(shè)計(jì)方法[4]。通過異步選擇函數(shù)WSAAsyncSe lect(),可以為應(yīng)用程序程序注冊一個(gè)或多個(gè)感興趣的網(wǎng)絡(luò)事件,如讀寫事件、連接事件、接受請求事件等。用來注冊異步消息的函數(shù)是WSAAsyncSelect函數(shù),它請求Windows Sockets DLL在檢測到套接字上發(fā)生的網(wǎng)絡(luò)事件時(shí),向窗口發(fā)送一個(gè)消息。它自動地設(shè)置套接字處于非阻塞工作方式,事件可以是一個(gè)或多個(gè)組合而成。
2.2.3對錯(cuò)誤的處理
錯(cuò)誤的發(fā)現(xiàn)和處理對于應(yīng)用程序是非常重要的,對WinSock而言,經(jīng)常會返回一下錯(cuò)誤信息,最常見的是SOCKET_ERROR,它是一個(gè)值為-1的常量,這時(shí)可以用WSAGetLastError函數(shù)來獲得一段代碼,該代碼可以清楚地表明錯(cuò)誤產(chǎn)生的原因和類型,該函數(shù)的原型為:
Int WSAGetLastError(void);
2.3套接字編程模型
2.3.1面向連接的套接字編程模型
套接字編程分為面向連接的套接字和面向無連接套接字的兩種編程模型。在面向連接的套接字模型中,服務(wù)器需要等待客戶端向其提出的建立連接的申請,一旦接收到客戶端的連接請求,服務(wù)器返回一個(gè)新的套接字描述符,通過該描述符調(diào)用數(shù)據(jù)傳輸函數(shù)可以與客戶端進(jìn)行數(shù)據(jù)的收發(fā)[5]。面向有連接的編程模型如圖3所示。
圖3面向連接的套接字模型
2.3.2面向無連接的套接字編程模型
面向無連接的編程模型相對比較簡單。服務(wù)器和客戶端沒有明確的界限,而且是對等的關(guān)系。客戶端之間首先創(chuàng)建一個(gè)數(shù)據(jù)報(bào)式套接字,并將其綁定到地址和端口上??蛻舳伺c服務(wù)端的數(shù)據(jù)交換通過sendto和recvfrom函數(shù)完成,在調(diào)用這兩個(gè)函數(shù)時(shí)都指定對方的地址,而不用建立連接。最后,都需要調(diào)用closesocket關(guān)閉套接字。面向無連接的套接字編程模型如下圖4所示。
3 WinSock實(shí)現(xiàn)基于TCP的客戶端/服務(wù)器通信
此實(shí)例的目的是驗(yàn)證面向連接的通信模型,通過C++編碼實(shí)現(xiàn)程序間的網(wǎng)絡(luò)通信。實(shí)例分成兩個(gè)程序,服務(wù)器端程序和客戶端程序。
1)服務(wù)器程序主要代碼
//定義兩個(gè)socket變量,一個(gè)用來監(jiān)聽,一個(gè)用來建立連接和提供服務(wù)
SOCKET listenSocket,acceptSocket;
//設(shè)置服務(wù)器、客戶的地址和端口
struct sockaddr_in serv,cliet;
listenSocket=socket(AF_INET,SOCK_STREAM,0);//創(chuàng)建監(jiān)聽流式socket
if(listenSocket==INVALID_SOCKET)
{ printf("建立socket出錯(cuò) ");return 0;
}
//設(shè)置服務(wù)器地址信息
serv.sin_family=AF_INET;
serv.sin_port=htons(6666);
//設(shè)置監(jiān)聽端口是6666
serv.sin_addr.s_addr=htonl(INADDR_ANY);//將4字節(jié)主機(jī)字節(jié)順序轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)順序,INADDR_ANY為系統(tǒng)指定的IP地址。//將監(jiān)聽socket與服務(wù)器地址綁定
if(bind(listenSocket,(LPSOCKADDR)&serv,sizeof(serv))==SOCKET_ERROR)
{printf("綁定出錯(cuò) ");return 0;
}
if(listen(listenSocket,5)==SOCKET_ERROR)//開始監(jiān)聽
{printf("監(jiān)聽出錯(cuò) ");return 0;
}
//等待客戶連接請求,有連接請求到來,則用acceptSocket與其建立連接,進(jìn)行通信
int len=sizeof(cliet);
acceptSocket=accept(listenSocket,(struct sockaddr *)&cliet,&len); if(acceptSocket==INVALID_SOCKET)
{printf("建立連接出錯(cuò) ");break;}
char buf[1024];
ret=recv(acceptSocket,buf,sizeof(buf),0);//接收數(shù)據(jù)
if(ret==0) return 0; char str[1024]={0};
printf("來自客戶端的信息:");
printf(buf);printf(" ");
memset(buf,0,sizeof(buf));
strcpy(buf,"ok");
ret=send(acceptSocket,buf,sizeof(buf),0);//發(fā)送數(shù)據(jù)
if(ret==SOCKET_ERROR)
{printf("發(fā)送數(shù)據(jù)出錯(cuò) ");return 0;}
closesocket(acceptSocket);//關(guān)閉socket
closesocket(listenSocket);
2)客戶端主要代碼
SOCKET clientSocket;//定義客戶端socket變量
struct sockaddr_in serv; //定義服務(wù)器端地址信息
char buf[1024];//設(shè)置服務(wù)器地址信息
serv.sin_family=AF_INET;
serv.sin_port=htons(8888);
//設(shè)置需連接的服務(wù)器的監(jiān)聽端口(必須與服務(wù)器端綁定的監(jiān)聽端口一致)
serv.sin_addr.s_addr=inet_addr("192.168.1.100");
//此處應(yīng)設(shè)置為服務(wù)器端的IP地址
clientSocket=socket(AF_INET,SOCK_STREAM,0);//建立客戶端socket if(clientSocket==INVALID_SOCKET)
{printf("建立socket出錯(cuò)!");return;
}
//請求與服務(wù)器建立TCP連接
if(connect(clientSocket,(struct sockaddr *)&serv,sizeof(serv))==INVALID_SOCKET)
{printf("請求建立連接出錯(cuò)!");return;
}
//發(fā)送數(shù)據(jù)給服務(wù)器
memset(buf,0,sizeof(buf));
strcpy(buf,"hello,How are you!");
ret=send(clientSocket,buf,sizeof(buf),0);
if(ret==SOCKET_ERROR)
{printf("發(fā)送數(shù)據(jù)出錯(cuò)");return;
}
//接收服務(wù)器發(fā)回的數(shù)據(jù)
memset(buf,0,sizeof(buf));
ret=recv(clientSocket,buf,sizeof(buf),0);
if(ret==0)return;
printf("接收到服務(wù)器回復(fù)信息內(nèi)容:");
printf(buf);printf(" ");
closesocket(clientSocket);//關(guān)閉socket
(3)該實(shí)例是面向連接的TCP協(xié)議,因此套接字采用流套接字(SOCK_STREAM),服務(wù)器在與客戶端建立連接成功后,接收客戶端發(fā)來信息“Hello,How are you!”,接收成功后,向客戶端發(fā)送“ok!”字符串。實(shí)現(xiàn)服務(wù)器與客戶端的網(wǎng)絡(luò)通信。
4總結(jié)
隨著Internet的發(fā)展,網(wǎng)絡(luò)應(yīng)用軟件的開發(fā)需求與日俱增。各式各樣的網(wǎng)絡(luò)應(yīng)用軟件也催生著各種網(wǎng)絡(luò)編程技術(shù)的進(jìn)步。用套接字(Socket)來實(shí)現(xiàn)互聯(lián)網(wǎng)上的進(jìn)程通訊,從而實(shí)現(xiàn)各種網(wǎng)絡(luò)應(yīng)用功能,是開發(fā)網(wǎng)絡(luò)應(yīng)用程序的重要方法。對于分布于不同主機(jī)上的進(jìn)程通信,Sockets無疑是一種理想的手段,通過它可以方便地交換數(shù)據(jù)。Winsock技術(shù)的出現(xiàn)極好地屏蔽了網(wǎng)絡(luò)低層復(fù)雜的結(jié)構(gòu)和協(xié)議,這使得基于Winsock的網(wǎng)絡(luò)應(yīng)用程序可以在不同類型的網(wǎng)絡(luò)上運(yùn)行。同時(shí)使Winsock成為網(wǎng)絡(luò)編程的最流行技術(shù),可以說,套接字是網(wǎng)絡(luò)通信的基石。
參考文獻(xiàn):
[1]延霞,謝斐.Visual C++網(wǎng)絡(luò)編程技術(shù)[M].北京:中國水利水電出版社,2010:6-11.
[2]鄭阿奇.Visual C++6.0應(yīng)用案例教程[M].北京:電子工業(yè)出版社,2010:160-170.
[3]鄒月,陳建兵.Socket網(wǎng)絡(luò)編程與實(shí)現(xiàn)[J].電腦編程技巧與維護(hù),2009,(8):10-12.
[4]陳孚.VC環(huán)境下基于C/S模式的網(wǎng)絡(luò)考試系統(tǒng)研究與實(shí)現(xiàn)[J].電腦編程技巧與維護(hù).2011,(12):59-60.
[5]任志考,李朝玲. Visual C++6.0環(huán)境下Winsock的研究與應(yīng)用[J].信息技術(shù).2006,(8):96-99.