




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
UNIX中的套接字網(wǎng)路編程介面
2.1.1問題的提出站在應(yīng)用程式實(shí)現(xiàn)的角度,應(yīng)用程式如何方便地使用協(xié)議棧軟體進(jìn)行通信呢?如果能在應(yīng)用程式與協(xié)議棧軟體之間提供一個(gè)軟體介面,就可以方便客戶與伺服器軟體的編程。2.1UNIX套接字網(wǎng)路編程介面的產(chǎn)生與發(fā)展
套接字應(yīng)用程式編程介面是網(wǎng)路應(yīng)用程式通過網(wǎng)路協(xié)議棧進(jìn)行通信時(shí)所使用的介面,即應(yīng)用程式與協(xié)議棧軟體之間的介面,簡稱套接字編程介面(SocketAPI)。它定義了應(yīng)用程式與協(xié)議棧軟體進(jìn)行交互時(shí)可以使用的一組操作,決定了應(yīng)用程式使用協(xié)議棧的方式、應(yīng)用程式所能實(shí)現(xiàn)的功能、以及開發(fā)具有這些功能的程式的難度。
加州大學(xué)伯克利(Berkley)分校開發(fā)並推廣了一個(gè)包括TCP/IP互聯(lián)協(xié)議的UNIX,稱為BSDUNIX(BerkeleySoftwareDistributionUNIX)操作系統(tǒng),套接字編程介面是這個(gè)操作系統(tǒng)的一個(gè)部分。後來的許多操作系統(tǒng)並沒有另外搞一套其他的編程介面,而是選擇了對(duì)於套接字編程介面的支持。由於這個(gè)套接字規(guī)範(fàn)最早是由Berkeley大學(xué)開發(fā)的,一般將它稱為BerkeleySockets規(guī)範(fàn)。2.1.2套接字編程介面的起源與應(yīng)用
要想實(shí)現(xiàn)套接字編程介面,可以採用兩種實(shí)現(xiàn)方式,一種是在操作系統(tǒng)的內(nèi)核中增加相應(yīng)的軟體來實(shí)現(xiàn),一種是通過開發(fā)操作系統(tǒng)之外的函數(shù)庫來實(shí)現(xiàn)。
2.1.3套接字編程介面的兩種實(shí)現(xiàn)方式
UNIX操作系統(tǒng)對(duì)檔和所有其他的輸入/輸出設(shè)備採用一種統(tǒng)一的的操作模式,就是“打開-讀-寫-關(guān)閉”(open-read-write-close)的I/O模式。當(dāng)TCP/IP協(xié)議被集成到UNIX內(nèi)核中的時(shí)候,相當(dāng)於在UNIX系統(tǒng)中引入了一種新型的I/O操作,就是應(yīng)用程式通過網(wǎng)路協(xié)議棧來交換數(shù)據(jù)。
2.1.4套接字通信與UNIX操作系統(tǒng)的輸入/輸出
在UNIX系統(tǒng)的實(shí)現(xiàn)中,套接字是完全與其他I/O集成在一起的。操作系統(tǒng)和應(yīng)用程式都將套接字編程介面也看作一種輸入/輸出機(jī)制。但是,用戶進(jìn)程與網(wǎng)路協(xié)議的交互作用實(shí)際要比用戶進(jìn)程與傳統(tǒng)的I/O設(shè)備相互作用要複雜得多。
其次,使用套接字的應(yīng)用程式必須說明許多細(xì)節(jié)。僅僅提供open、read、write、close四個(gè)過程遠(yuǎn)遠(yuǎn)不夠。為避免單個(gè)套接字函數(shù)參數(shù)過多,套接字編程介面的設(shè)計(jì)者定義了多個(gè)函數(shù)。2.2套接字編程的基本概念
套介面是對(duì)網(wǎng)路中不同主機(jī)上應(yīng)用進(jìn)程之間進(jìn)行雙向通信的端點(diǎn)的抽象,一個(gè)套介面就是網(wǎng)路上進(jìn)程通信的一端,提供了應(yīng)用層進(jìn)程利用網(wǎng)路協(xié)議棧交換數(shù)據(jù)的機(jī)制。
圖2.1電插座與電話插座的作用2.2.1什麼是套接字(SOCKET)
我們應(yīng)當(dāng)從多個(gè)層面來理解套接字這個(gè)概念的內(nèi)涵。從套接字所處的地位來講,套接字上聯(lián)應(yīng)用進(jìn)程,下聯(lián)網(wǎng)路協(xié)議棧,是應(yīng)用程式通過網(wǎng)路協(xié)議棧進(jìn)行通信的介面,是應(yīng)用程式與網(wǎng)路協(xié)議棧進(jìn)行交互的介面。圖2.2應(yīng)用進(jìn)程、套介面、網(wǎng)路協(xié)議棧及操作系統(tǒng)的關(guān)係
從實(shí)現(xiàn)的角度來講,非常複雜。套接字是一個(gè)複雜的軟體機(jī)構(gòu),包含了一定的數(shù)據(jù)結(jié)構(gòu),包含許多選項(xiàng),由操作系統(tǒng)內(nèi)核管理。從使用的角度來講,非常簡單。對(duì)於套接字的操作形成了一種網(wǎng)路應(yīng)用程式的編程介面(API)。本書把這一套操作套接字的編程介面函數(shù)稱作套接字編程介面,套接字是它的操作對(duì)象。總之,套接字是網(wǎng)路通信的基石。
2.2.2套接字的特點(diǎn)1.通信域
接字存在於通信域中,通信域是為了處理一般的進(jìn)程通過套接字通信而引入的一種抽象概念,套接字通常只和同一域中的套接字交換數(shù)據(jù)。如果數(shù)據(jù)交換要穿越域的邊界,就一定要執(zhí)行某種解釋程式?,F(xiàn)在,僅僅針對(duì)Internet域,並且使用Internet協(xié)議族(即TCP/IP協(xié)議族)來通信。2.套接字具有三種類型每一個(gè)正被使用的套接字都有它確定的類型,只有相同類型的套接字才能相互通信。(1)數(shù)據(jù)報(bào)套接字(DatagramSOCKET)數(shù)據(jù)報(bào)套接字提供無連接的不保證可靠的獨(dú)立的數(shù)據(jù)報(bào)傳輸服務(wù)。在Internet通信域中,數(shù)據(jù)報(bào)套接字使用UDP數(shù)據(jù)報(bào)協(xié)議形成的進(jìn)程間通路,具有UDP協(xié)議為上層所提供的服務(wù)的所有特點(diǎn)。圖2.3在Internet通信域中,數(shù)據(jù)報(bào)套接字基於UDP協(xié)議
(2)流式套接字(StreamSOCKET)流式套接字提供雙向的、有序的、無重複的、無記錄邊界的可靠的數(shù)據(jù)流傳輸服務(wù)。在Internet通信域中,流式套接字使用TCP協(xié)議形成的進(jìn)程間通路,具有TCP協(xié)議為上層所提供的服務(wù)的所有特點(diǎn),在使用流式套接字傳輸數(shù)據(jù)之前,必須在數(shù)據(jù)的發(fā)送端和接收端之間建立連接,如圖2.4所示。
圖2.4在Internet通信域中,流式套接字基於TCP協(xié)議(3)原始式套接字(RAWSOCKET)
原始式套接字允許對(duì)較低層次的協(xié)議,如IP、ICMP直接訪問,用於檢驗(yàn)新的協(xié)議的實(shí)現(xiàn)。3.套接字由應(yīng)用層的通信進(jìn)程創(chuàng)建,並為其服務(wù)就是說,每一個(gè)套接字都有一個(gè)相關(guān)的應(yīng)用進(jìn)程,操作該套接字的代碼是該進(jìn)程的組成部分。4.使用確定的IP地址和傳輸層端口號(hào)
往往在生成套接字的描述符後,要將套接字與電腦上的特定的IP地址和傳輸層端口號(hào)相關(guān)聯(lián),這個(gè)過程稱為綁定。一個(gè)套介面要使用一個(gè)確定的三元組網(wǎng)絡(luò)地址資訊,才能使它在網(wǎng)路中唯一地被標(biāo)識(shí)。(1)不管是採用對(duì)等模式或者客戶機(jī)/伺服器模式,通信雙方的應(yīng)用程式都需要開發(fā)。(2)雙方所交換數(shù)據(jù)的結(jié)構(gòu)和交換數(shù)據(jù)的順序有特定的要求,不符合現(xiàn)在成熟的應(yīng)用層協(xié)議,甚至需要自己去開發(fā)應(yīng)用層協(xié)議,自己設(shè)計(jì)最適合的數(shù)據(jù)結(jié)構(gòu)和資訊交換規(guī)程。
2.2.3套接字的應(yīng)用場合2.2.4套接字使用的數(shù)據(jù)類型和相關(guān)的問題1.三種表示套接字地址的結(jié)構(gòu)在套接字編程介面中,專門定義了三種結(jié)構(gòu)型的數(shù)據(jù)類型,用來存儲(chǔ)協(xié)議相關(guān)的網(wǎng)路地址,在套接字編程介面的函數(shù)調(diào)用中要用到它們。(1)sockaddr結(jié)構(gòu),針對(duì)各種通信域的套接字,存儲(chǔ)它們的地址資訊。struct
sockaddr{unsignedshortsa_family;//地址家族charsa_data;//協(xié)議地址}
(2)sockaddr_in結(jié)構(gòu),專門針對(duì)Internet通信域,存儲(chǔ)套接字相關(guān)的網(wǎng)路地址資訊,例如IP地址,傳輸層端口號(hào)等資訊。struct
sockaddr_in{shortintsin_family;//地址家族unsignedshortintsin_port;//端口號(hào)struct
in_addr
sin_addr;//IP地址unsignedcharsin_zero[8];//全為0}
(3)in_addr結(jié)構(gòu),專門用來存儲(chǔ)IP地址。Struct
in_addr{Unsignedlongs_addrl;}(4)這些數(shù)據(jù)結(jié)構(gòu)的一般用法:首先,定義一個(gè)Sockaddr_in的結(jié)構(gòu)實(shí)例,並將它清零。比如:struct
sockaddr_in
myad;memset(&myad,0,sizeof(structsockaddr_in));
然後,為這個(gè)結(jié)構(gòu)賦值,比如:myad.sin_family=AF_INET;
myad.sin_port=htons(8080);
myad.sin_addr.s_addr=htonl(INADDR-ANY);第三步:在函數(shù)調(diào)用中使用時(shí),將這個(gè)結(jié)構(gòu)強(qiáng)制轉(zhuǎn)換為sockaddr類型。如:accept(listenfd,(sockaddr*)(&myad),&addrlen);
2.本機(jī)位元組順序和網(wǎng)路位元組順序在具體電腦中的多位元組數(shù)據(jù)的存儲(chǔ)順序,稱為本機(jī)位元組順序。多位元組數(shù)據(jù)在網(wǎng)路協(xié)議報(bào)頭中的存儲(chǔ)順序,稱為網(wǎng)路位元組順序。
網(wǎng)路應(yīng)用程式要在不同的電腦中運(yùn)行,本機(jī)位元組順序是不同的,但是,網(wǎng)路位元組順序是一定的。所以,應(yīng)用程式在編程的時(shí)候,在把IP地址和端口號(hào)裝入套接字的時(shí)候,應(yīng)當(dāng)把它們從本機(jī)位元組順序轉(zhuǎn)換為網(wǎng)路位元組順序;相反,在本機(jī)輸出時(shí),應(yīng)將它們從網(wǎng)路位元組順序轉(zhuǎn)換為本機(jī)位元組順序。
套接字編程介面特為解決這個(gè)問題設(shè)置了四個(gè)函數(shù):htons()短整數(shù)本機(jī)順序轉(zhuǎn)換為網(wǎng)路順序,用於端口號(hào)。htonl()長整數(shù)本機(jī)順序轉(zhuǎn)換為網(wǎng)路順序,用於IP地址。ntohs()短整數(shù)網(wǎng)路順序轉(zhuǎn)換為本機(jī)順序,用於端口號(hào)。ntohl()長整數(shù)網(wǎng)路順序轉(zhuǎn)化為本機(jī)順序,用於IP地址。這四個(gè)函數(shù)將被轉(zhuǎn)換的數(shù)值作為函數(shù)的參數(shù),函數(shù)返回值是轉(zhuǎn)換後的結(jié)果。
3.點(diǎn)分十進(jìn)位的IP地址的轉(zhuǎn)換在因特網(wǎng)中,IP地址常常用點(diǎn)分十進(jìn)位的表示方法,但在套接字中,IP地址是無符號(hào)的長整型數(shù),套接字編程介面設(shè)置了兩個(gè)函數(shù),專門用於兩種形式的IP地址的轉(zhuǎn)換。(1)inet-addr函數(shù)
unsignedlonginet-addr(constchar*cp)入口參數(shù)cp:點(diǎn)分十進(jìn)位形式的IP地址。返回值:網(wǎng)路位元組順序的IP地址,是無符號(hào)的長整數(shù),(2)inet_ntoa函數(shù)char*inet_ntoa(struct
in_addrin)入口參數(shù)in:包含長整型IP地址的in_addr
結(jié)構(gòu)變數(shù),返回值:指向點(diǎn)分十進(jìn)位IP地址的字串的指針。
通常,我們使用功能變數(shù)名稱來標(biāo)識(shí)站點(diǎn),可以將文字型的主機(jī)功能變數(shù)名稱直接轉(zhuǎn)換成IP地址,struct
hostent*
gethodtbyname(constchar*name);入口參數(shù):是站點(diǎn)的主機(jī)功能變數(shù)名稱字串,返回值:是指向hostent
結(jié)構(gòu)的指針,hostent結(jié)構(gòu)包含主機(jī)名,主機(jī)別名數(shù)組,返回地址的類型(一般是AF-INET),地址長度的位元組數(shù),已符合網(wǎng)路位元組順序的主機(jī)網(wǎng)路地址等。
4.功能變數(shù)名稱服務(wù)
2.3面向連接的套接字編程2.3.1套接字的工作過程2.3.2UNIX套接字編程介面的系統(tǒng)調(diào)用1.創(chuàng)建套接字SOCKET()SOCKET過程創(chuàng)建一個(gè)套接字並返回一個(gè)整型描述符:intSOCKET(int
Protofamily,intType,intProtocol);
2.綁定套接字到指定的地址BIND()
intBIND(int
Sockfd,struct
sockaddr*
My_addr,int
Addrlen);
3.啟動(dòng)監(jiān)聽Listen()
intLISTEN(int
Sockfd,int
Queuesize);舉例:LISTEN(Sockfe,10);
圖2.6監(jiān)聽套接字使用緩衝區(qū)接納多個(gè)客戶端的連接請(qǐng)求
4.接收連接請(qǐng)求ACCEPT()intACCEPT(int
Sockfd,struct
sockaddr*
Addr,int*
addrlen);舉例:int
clientfd;//定義回應(yīng)套接字描述符變數(shù)int
addrler=sizeof(sockaddr);//獲得套接字地址結(jié)構(gòu)長度。struct
sockaddr_in
cltsockaddr;//定義用於返回客戶端地址的結(jié)構(gòu)。clientfd=ACCEPT(listenfd,(sockaddr*)(&cltsockaddr),&addrlen);//接收客戶連接請(qǐng)求
5.請(qǐng)求建立連接CONNECT()intCONNECT(int
Sockfd,struct
sockaddr*
Service_addr,int
Addrlen);舉例:if(CONNECT(sockfd,(struct
sockaddr*)(&serv_addr),sizeof(struct
sockaddr))<0){報(bào)錯(cuò),並退出}
6.讀/寫套接字READ()和WRITE()intREAD(int
sockfd,void*buffer,int
len);intWRITE(int
sockfd,void*buffer,int
len)
7.向套接字發(fā)送SEND()和從套接字接收RECV()
intSEND(int
sockfd,char*buf,int
len,intflags);intRECV(int
sockfd,char*buf,int
len,intflags);
8.關(guān)閉套接字CLOSE()
intCLOSE(int
sockfd);2.3.3面向連接的套接字編程實(shí)例1.實(shí)例的功能伺服器對(duì)來訪的客戶計(jì)數(shù),並向客戶報(bào)告這個(gè)計(jì)數(shù)值??蛻艚⑴c伺服器的一個(gè)連接並等待它的輸出。每當(dāng)連接請(qǐng)求到達(dá)時(shí),伺服器生成一個(gè)可列印的ASCII串資訊,將它在連接上發(fā)回,然後關(guān)閉連接??蛻麸@示收到的資訊,然後退出。
例如,對(duì)於伺服器接收的第10次客戶連接請(qǐng)求,該客戶將收到並列印如下資訊:Thisserverhasbeencontacted10times.2.實(shí)例程式的命令行參數(shù)實(shí)例是UNIX環(huán)境下的C程式,客戶和服務(wù)器程式在編譯後,均以命令行的方式執(zhí)行。伺服器程式執(zhí)行時(shí)可以帶一個(gè)命令行參數(shù),是用來接受請(qǐng)求的監(jiān)聽套接字的協(xié)議端口號(hào)。這個(gè)參數(shù)是可選的。如果不指定端口號(hào),代碼將使用程式內(nèi)定的缺省端口號(hào)5188。
客戶程式執(zhí)行時(shí)可以帶兩個(gè)命令行參數(shù):一個(gè)是伺服器所在電腦的主機(jī)名,另一個(gè)是伺服器監(jiān)聽的協(xié)議端口號(hào)。這兩個(gè)參數(shù)都是可選的。如果沒有指定協(xié)議端口號(hào),客戶使用程式內(nèi)定的缺省值5188。如果一個(gè)參數(shù)也沒有,客戶使用缺省端口和主機(jī)名localhost,localhost是映射到客戶所運(yùn)行的電腦的一個(gè)別名。允許客戶與本地機(jī)上的伺服器通信,對(duì)調(diào)試是很有用的。3.客戶程式代碼/*----------------------------------------------------*程式:client.c*目的:創(chuàng)建一個(gè)套接字,通過網(wǎng)路連接一個(gè)伺服器,並列印來自伺服器的資訊*語法:client[host[port]]*host-運(yùn)行伺服器的電腦的名字*port-伺服器監(jiān)聽套接字所用協(xié)議端口號(hào)*注意:兩個(gè)參數(shù)都是可選的。如果未指定主機(jī)名,客戶使用localhost;如果未指定端口號(hào),*客戶將使用PROTOPORT中給定的缺省協(xié)議端口號(hào)*----------------------------------------------------*/
#include<sys/types.h>#include<sys/socket.h>/*UNIX下,套接字的相關(guān)包含檔。*/#include<netinet/in.h>#include<arpa/inet.h>#include<netdb.h>#include<stdio.h>#include<string.h>
#definePROTOPORT5188/*缺省協(xié)議端口號(hào)*/externint
errno;charlocalhost=“l(fā)ocalhost”;/*缺省主機(jī)名*/
main(argc,argv)int
argc;char*argv[];{struct
hostent
*ptrh;/*指向主機(jī)列表中一個(gè)條目的指針*/struct
sockaddr_in
servaddr;/*存放伺服器端網(wǎng)路地址的結(jié)構(gòu)*/int
sockfd;/*客戶端的套接字描述符*/int port;/*伺服器端套接字協(xié)議端口號(hào)*/char*host;/*伺服器主機(jī)名指針*/intn;/*讀取的字元數(shù)*/charbuf[1000];/*緩衝區(qū),接收伺服器發(fā)來的數(shù)據(jù)*/
memset((char*)&servaddr,0,sizeof(servaddr));/*清空sockaddr結(jié)構(gòu)*/servaddr.sin_family=AF_INET;/*設(shè)置為因特網(wǎng)協(xié)議族*/
/*檢查命令行參數(shù),如果有,就抽取端口號(hào)。否則使用內(nèi)定的缺省值*/if(argc>2){port=atoi(argv[2]);/*如果指定了協(xié)議端口,就轉(zhuǎn)換成整數(shù)*/}else{port=PROTOPORT;/*否則,使用缺省端口號(hào)*/}if(port>0)/*如果端口號(hào)是合法的數(shù)值,就將它裝入網(wǎng)路地址結(jié)構(gòu)*/
servaddr.sin_port=htons((u_short)port);else{/*否則,列印錯(cuò)誤資訊並退出*/fprintf(stderr,”badportnumber%s\n”,argv[2]);exit(1);}
/*檢查主機(jī)參數(shù)並指定主機(jī)名*/if(argc>1){host=argv[1];/*如果指定了主機(jī)名參數(shù),就使用它*/}else{host=localhost;/*否則,使用缺省值*/}
/*將主機(jī)名轉(zhuǎn)換成相應(yīng)的IP地址並複製到servaddr
結(jié)構(gòu)中*/ptrh=gethostbyname(host);/*從伺服器主機(jī)名得到相應(yīng)的IP地址*/if((char*)ptrh==null){/*檢查主機(jī)名的有效性,無效則退出*/fprintf(stderr,”invalidhost:%s\n”,host);exit(1);}memcpy(&servaddr.sin_addr,ptrh->h_addr,ptrh->h_length);
/*創(chuàng)建一個(gè)套接字*/sockfd=SOCKET(AF_INET,SOCK_STREAM,0);if(sockfd<0){fprintf(stderr,”socketcreationfailed\n”);exit(1);}
/*請(qǐng)求連接到伺服器*/if(connect(sockfd,(struct
sockaddr*)&servaddr,sizeof(servaddr))<0){fprintf(stderr,”connectfailed\n”);/*連接請(qǐng)求被拒絕,報(bào)錯(cuò)並退出*/exit(1);}
/*從套接字反復(fù)讀數(shù)據(jù),並輸出到用戶螢?zāi)簧?/n=recv(sockfd,buf,sizeof(buf),0);while(n>0){write(1,buf,n);n=recv(sockfd,buf,sizeof(buf),0);}
/*關(guān)閉套接字*/closesocket(sockfd);
/*終止客戶程式*/exit(0);}
4.伺服器實(shí)例代碼/*------------------------------------------*程式:server.c*目的:分配一個(gè)套接字,然後反復(fù)執(zhí)行如下幾步:*(1)等待客戶的下一個(gè)連接*(2)發(fā)送一個(gè)短消息給客戶*(3)關(guān)閉與客戶的連接*(4)轉(zhuǎn)向(1)步*命令行語法:server[port]*port–伺服器端監(jiān)聽套接字使用的協(xié)議端口號(hào)*注意:端口號(hào)可選。如果未指定端口號(hào),伺服器使用PROTOPORT中指定的缺省*端口號(hào)*-----------------------------------------------*/
#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#include<netdb.h>#include<stdio.h>#include<string.h>#definePROTOPORT5188/*監(jiān)聽套接字的缺省協(xié)議端口號(hào)*/#define QLEN6/*監(jiān)聽套接字的請(qǐng)求佇列大小*/intvisits=0;/*對(duì)於客戶連接的計(jì)數(shù)*/
main(argc,argc)int
argc;char*argv[];{struct
hostent
*ptrh;/*指向主機(jī)列表中一個(gè)條目的指針*/struct
sockaddr_in
servaddr;/*存放伺服器網(wǎng)路地址的結(jié)構(gòu)*/struct
sockaddr_in
clientaddr;/*存放客戶網(wǎng)路地址的結(jié)構(gòu)*/int
listenfd; /*監(jiān)聽套接字描述符*/int
clientfd; /*回應(yīng)套接字描述符*/intport;/*協(xié)議端口號(hào)*/int
alen;/*地址長度*/charbuf[1000];/*供伺服器發(fā)送字串所用的緩衝區(qū)*/
memset((char*)&servaddr,0,sizeof(servaddr));/*清空sockaddr結(jié)構(gòu)*/servaddr.sin_family=AF_INET;/*設(shè)置為因特網(wǎng)協(xié)議族*/servaddr.sin_addr.s_addr=INADDR_ANY;/*設(shè)置本地IP地址*/
/*檢查命令行參數(shù),如果指定了,就是用該端口號(hào),否則使用缺省端口號(hào)*/if(argc>1){port=atoi(argv[1]);/*如果指定了端口號(hào),就將它轉(zhuǎn)換成整數(shù)*/}else{port=PROTOPORT;/*否則,使用缺省端口號(hào)*/}
if(port>0)/*測試端口號(hào)是否合法*/servaddr.sin_port=htons((u_short)port);else{/*列印錯(cuò)誤資訊並退出*/fprintf(stderr,”badport number%s\n”,argv[1]);exit(1);}
/*創(chuàng)建一個(gè)用於監(jiān)聽的流式套接字*/listenfd=SOCKET(AF_INET,SOCK_STREAM,0);if(listenfd<0){fprintf(stderr,“socketcreationfailed\n”);exit(1);}
/*將本地地址綁定到監(jiān)聽套接字*/if(bind(listenfd,(struct
sockaddr*)&servaddr,sizeof(servaddr))<0){fprintf(stderr,”bindfailed\n”);exit(1);}
/*開始監(jiān)聽,並指定監(jiān)聽套接字請(qǐng)求佇列的長度*/if(listen(listenfd,QLEN)<0){fprintf(stderr,”listenfiled\n”);exit(1);}
/*伺服器主迴圈—接受和處理來自客戶端的連接請(qǐng)求*/while(1){alen=sizeof(clientaddr);/*接受客戶端連接請(qǐng)求,並生成回應(yīng)套接字*/if((clientfd=accept(listenfd,(struct
sockaddr*)&clientaddr,&alen))<0){fprintf(stderr,“acceptfailed\n”);exit(1);}visits++;/*累加訪問的客戶數(shù)*/sprintf(buf,“thisserverhasbeencontacted%dtime\n”,visits);send(clientfd,buf,strlen(buf),0);/*向客戶端發(fā)送資訊*/closesocket(clientfd);/*關(guān)閉回應(yīng)套接字*/}}
關(guān)於阻塞的問題
圖2.7伺服器進(jìn)程因調(diào)用ACCEPT()而被阻塞
2.3.4進(jìn)程的阻塞問題和對(duì)策
1.什麼是阻塞阻塞是指一個(gè)進(jìn)程執(zhí)行了一個(gè)函數(shù)或者系統(tǒng)調(diào)用,該函數(shù)由於某種原因不能立即完成,因而不能返回調(diào)用它的進(jìn)程,導(dǎo)致進(jìn)程受控於這個(gè)函數(shù)而處於等待的狀態(tài),進(jìn)程的這種狀態(tài)稱為阻塞。圖2.8RECV()函數(shù)的兩種執(zhí)行方式
2.能引起阻塞的套接字調(diào)用在Berkeley套接字網(wǎng)路編程介面的模型中,套接字的默認(rèn)行為是阻塞的,具體地說,在一定情況下,有多個(gè)操作套接字的系統(tǒng)調(diào)用會(huì)引起進(jìn)程阻塞。(1)ACCEPT()(2)READ()、RECV()和READFORM()(3)WRITE()、SEND()和SENDTO()(4)CONNECT()(5)SELECT()(6)CLOSESOCKET()
3.阻塞工作模式帶來的問題採用阻塞工作模式的單進(jìn)程伺服器是不能很好地同時(shí)為多個(gè)客戶服務(wù)的。圖2.9是一個(gè)例子。
圖2.9採用阻塞工作模式的伺服器不能很好地為多個(gè)客戶服務(wù)
4.一種解決方案
利用UNIX操作系統(tǒng)的FORK()系統(tǒng)調(diào)用,編制多進(jìn)程併發(fā)執(zhí)行的伺服器程式??梢詣?chuàng)建子進(jìn)程。對(duì)於每一個(gè)客戶端,用一個(gè)專門的進(jìn)程為它服務(wù),通過進(jìn)程的併發(fā)執(zhí)行,來實(shí)現(xiàn)對(duì)多個(gè)客戶的併發(fā)服務(wù)。基本的編程框架是:父進(jìn)程代碼If((pid=FORK())==0){…….子進(jìn)程代碼........}elseif(pid<0){報(bào)錯(cuò)資訊}父進(jìn)程代碼
舉例:#include<sys/types.h>#include<sys/socket.h>#include<stdio.h>#include<arpa/inet.h>
voidmain(int
argc,char**argv){int
listenfd,clientfd,pid;struct
sockaddr_in
ssockaddr,csockaddr;charbuffer[1024];int
addrlen,n;
/*創(chuàng)建監(jiān)聽套接字*/listenfd=socket(AF_INET,SOCK_STREAM,0);if(listenfd<0){fprintf(stderr,"socketerror!\n");exit(1);}
/*為監(jiān)聽套接字綁定網(wǎng)路地址*/memset(&ssockaddr,0,sizeof(structsockaddr_in));ssockaddr.sin_family=AF_INET;ssockaddr.sin_addr.s_addr=htonl(INADDR_ANY);ssockaddr.sin_port=htons(8080);if(bind(listenfd,&ssockaddr,sizeof(struct
sockaddr_in))<0){fprintf(stderr,"binderror!\n");exit(2);}
/*啟動(dòng)套接字的監(jiān)聽*/listen(listenfd,5);addrlen=sizeof(sockaddr);
/*伺服器進(jìn)入迴圈,接受並處理來自不同客戶端的連接請(qǐng)求*/while(1){clientfd=accept(listenfd,(sockaddr*)(&csockaddr),&addrlen);/*accept調(diào)用返回時(shí),表明有客戶端請(qǐng)求連接,創(chuàng)建子進(jìn)程處理連接*/If((pid=FORK())==0){/*顯示客戶端的網(wǎng)路地址*/printf("ClientAddr:%s%d\n",inet_ntoa(csockaddr.sin_addr),ntohs(csockaddr.sin_port));/*讀取客戶端發(fā)送來的數(shù)據(jù),在將它們返回到客戶端*/while((n=read(clientfd,buffer,1024))>0){buffer[n]=0;printf("ClientSend:%s",buffer);write(clientfd,buffer,n);}if(n<0){fprintf(stderr,"readerror!\n");exit(3);}
/*通信完畢,關(guān)閉與這個(gè)客戶連接的套接字*/printf("clent%sclosed!\n",inet_ntoa(csockaddr.sin_addr));close(clientfd);exit(1);}elseif(pid<0)printf("forkfailed!\n");close(clientfd);}close(listenfd);/*關(guān)閉監(jiān)聽套接字*/}
2.4無連接的套接字編程2.4.1無連接的套接字編程的兩種模式使用數(shù)據(jù)報(bào)套接字開發(fā)網(wǎng)路應(yīng)用程式,既可以採用客戶/伺服器模式,也可以採用對(duì)等模式。圖2.10對(duì)等模式的數(shù)據(jù)報(bào)套接字的編程模型
1.對(duì)等模式
2.客戶/伺服器模式
圖2.11C/S模式的數(shù)據(jù)報(bào)套接字的編程模型
2.4.2兩個(gè)專用的系統(tǒng)調(diào)用1.發(fā)送數(shù)據(jù)報(bào)SENDTO()intSENDTO(int
sockfd,constvoid*msg,int
len,unsignedintflags,struct
sockaddr*to,int
tolen);
2.接收數(shù)據(jù)報(bào)RECVFROM()intRECVFROM(int
sockfd,void*buf,int
len,unsignedintflags,struct
sockaddr*from,int*
fromlen)
2.4.3數(shù)據(jù)報(bào)套接字的對(duì)等模式編程實(shí)例聊天程式#include<sys/types.h>#include<unistd.h>#include<error.h>#include<sys/socket.h>#include<arpa/inet.h>#include<stdio.h>
/*中斷處理過程*/voidint_proc(int
signo){}
voidmain(int
argc,char**argv){struct
sockaddr_in
daddr,saddr,cmpaddr;int
sockfd;inttimer=3;charbuffer[1024];int
addrlen,n;
/*判斷用戶輸入的命令行是否正確,如果有錯(cuò),提示用法*/if(argc!=5){printf("用法:%s目的IP目的端口源IP源端口\n",argv[0]);exit(0);}
/*設(shè)定中斷處理函數(shù),並設(shè)置時(shí)間限制*/signal(SIGALRM,int_proc);alarm(timer);
/*建立數(shù)據(jù)報(bào)套接字*/sockfd=socket(AF_INET,SOCK_DGRAM,0);if
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024年特許金融分析師考試學(xué)習(xí)交流群體試題及答案
- 顱內(nèi)血腫常用藥物護(hù)理
- 特許金融分析師考試重要趨勢試題及答案
- 車貸內(nèi)勤風(fēng)控工作總結(jié)
- 八年級(jí)上冊(cè)《三角形的內(nèi)角和》課件與練習(xí)
- 【名師課件】4.4 課件:用雙縫干涉測量光的波長-2025版高一物理必修二
- 2024年特許金融分析師考試常見誤區(qū)試題及答案
- 浙江省寧波市鄞州中學(xué)2024-2025學(xué)年高一下學(xué)期返校測試(選考)歷史試題(解析版)
- 大學(xué)生交通安全知識(shí)教育
- 河南省駐馬店市正陽縣2024-2025學(xué)年八年級(jí)(上)期末語文試卷
- 空調(diào)維保服務(wù)投標(biāo)方案(技術(shù)標(biāo))
- 22S803 圓形鋼筋混凝土蓄水池
- 煙供.火供.火施儀軌
- 幕墻維修施工方案
- 輪機(jī)工程船舶柴油機(jī)主動(dòng)力推進(jìn)裝置培訓(xùn)課件
- 外墻保溫、仿磚飾面、涂料分包工程技術(shù)標(biāo)書投標(biāo)書(技術(shù)標(biāo))
- 網(wǎng)絡(luò)系統(tǒng)規(guī)劃與部署(中級(jí))PPT完整高職全套教學(xué)課件
- 成人still病專題知識(shí)
- 駕駛員安全教育課
- 醫(yī)院污水處理技術(shù)指南(環(huán)發(fā)2023年197號(hào)2023年20實(shí)施)
- 求職登記表(標(biāo)準(zhǔn)模版)
評(píng)論
0/150
提交評(píng)論