




版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
第2章基于TCP套接字的編程
2.1概述2.2套接字和套接字地址2.3基本套接字函數(shù)2.4高級(jí)套接字函數(shù)2.5多路復(fù)用2.6網(wǎng)絡(luò)字節(jié)傳輸順序及主機(jī)字節(jié)順序2.7DNS與域名訪問(wèn)2.8基于IP和域名的通信編程2.9基于TCP套接字編程示例習(xí)題2.1概述 20世紀(jì)80年代早期,美國(guó)國(guó)防高級(jí)研究計(jì)劃局(ARPA,AdvancedResearchProjectsAgency)資助了加利福尼亞大學(xué)伯克利分校一個(gè)研究組,將TCP/IP軟件移植到UNIX操作系統(tǒng)中,并將結(jié)果提供給其他網(wǎng)點(diǎn)。作為項(xiàng)目的一部分,設(shè)計(jì)者們希望像訪問(wèn)文件一樣去訪問(wèn)網(wǎng)絡(luò),因此,創(chuàng)建了一個(gè)接口,應(yīng)用進(jìn)程使用這個(gè)接口可以方便的進(jìn)行通信。為了支持TCP/IP功能增加的新系統(tǒng)調(diào)用接口,形成了BerkeleySocket,這個(gè)系統(tǒng)被稱為BerkeleyUNIX或BSDUNIX(TCP/IP首次出現(xiàn)在BSD4.1版本(release4.1ofBerkeleySoftwareDistribution))。由于許多計(jì)算機(jī)廠商都采用了BerkeleyUNIX,Socket得到了迅速普及并被廣泛使用。
在UNIX系統(tǒng)中,網(wǎng)絡(luò)應(yīng)用編程界面有兩類:UNIX
BSD的套接字(socket)和UNIX
System
V的TLI。由于Sun公司采用了支持TCP/IP的UNIX
BSD操作系統(tǒng),使TCP/IP的應(yīng)用有更大的發(fā)展,其網(wǎng)絡(luò)應(yīng)用編程接口──套接字在網(wǎng)絡(luò)軟件中也被廣泛應(yīng)用,至今已引進(jìn)到Linux和Windows系統(tǒng)中,成為開(kāi)發(fā)網(wǎng)絡(luò)應(yīng)用軟件的強(qiáng)有力工具,本章將詳細(xì)討論套接字的使用。
Linux產(chǎn)生時(shí),UNIX系統(tǒng)的網(wǎng)絡(luò)功能已經(jīng)相當(dāng)成熟了,Linux網(wǎng)絡(luò)的開(kāi)發(fā)者選擇了重新開(kāi)發(fā)網(wǎng)絡(luò)功能。在Linux網(wǎng)絡(luò)代碼開(kāi)發(fā)的過(guò)程中,很多程序員做出了貢獻(xiàn)。它提供的套接字有三種類型:流式套接字(SOCK_STREAM),數(shù)據(jù)報(bào)套接字(SOCK_DGRAM)及原始套接字(SOCK_RAW)。下面將詳細(xì)介紹這些套接字的定義與應(yīng)用。2.2套接字和套接字地址
2.2.1套接字 套接字是兩個(gè)通信通道上的端節(jié)點(diǎn)。套接字函數(shù)可以用來(lái)產(chǎn)生通信信道,通過(guò)信道兩個(gè)應(yīng)用程序間可以傳送數(shù)據(jù)。圖2-1顯示了利用套接字進(jìn)行通信的示例。
圖2-1套接字(a)套接字作為網(wǎng)絡(luò)傳輸端點(diǎn);(b)套接字對(duì)多種協(xié)議的支持
套接字是信道的末端,當(dāng)應(yīng)用程序產(chǎn)生一個(gè)套接字后,套接字函數(shù)就返回所用文件的描述符。在這里,可以把支持虛電路服務(wù)的信道看做電話線,套接字就像一個(gè)電話。同樣,可以把提供數(shù)據(jù)報(bào)服務(wù)的信道看做郵局系統(tǒng),套接字看做信箱,人們可以向郵箱投遞信件,信件通過(guò)郵局系統(tǒng)到達(dá)另一個(gè)信箱。應(yīng)用程序利用套接字發(fā)數(shù)據(jù)報(bào),數(shù)據(jù)報(bào)通過(guò)信道傳向另一個(gè)套接字。在產(chǎn)生信道時(shí),用戶可以指定所用的傳輸提供者。例如,可以用TCP、UDP、XNS作傳輸提供者。 Linux支持多種類型的套接字,也叫做套接字尋址簇,這是因?yàn)槊糠N類型的套接字都有自己的尋址方法。Linux支持以下的套接字類型:
UNIX UNIX域套接字
INET Internet地址簇CP/IP協(xié)議支持通信
IPX NovellIPX APPLETALK AppletalkDDP X25 X25
這些類型的套接字代表各種不同的連接服務(wù)。
Linux的BSD套接字支持下面的幾種套接字類型:
(1)流式(stream)。這種套接字提供了可靠的雙向順序數(shù)據(jù)流連接。它可以保證數(shù)據(jù)傳輸中的完整性、正確性和單一性。INET尋址簇中的TCP協(xié)議支持這種類型的套接字。
(2)數(shù)據(jù)報(bào)(datagram)。這種類型的套接字也可以像流式套接字一樣提供雙向的數(shù)據(jù)傳輸,但它們不能保證傳輸?shù)臄?shù)據(jù)一定能夠到達(dá)目的節(jié)點(diǎn),也無(wú)法保證到達(dá)數(shù)據(jù)以正確的順序到達(dá)以及數(shù)據(jù)的單一性、正確性。UDP協(xié)議支持這種類型的套接字。
(3)原始(raw)。這種類型的套接字允許進(jìn)程直接存取下層的協(xié)議。
(4)可靠遞送消息(reliabledeliveredmessages)。這種套接字和數(shù)據(jù)報(bào)套接字一樣,只能保證數(shù)據(jù)的到達(dá)。 (5)順序數(shù)據(jù)包(sequencedpackets)。這種套接字和流式套接字相同,但是它的數(shù)據(jù)包的大小是固定的。
(6)數(shù)據(jù)包(packet)。這不是標(biāo)準(zhǔn)的BSD套接字類型,而是Linux中的一種擴(kuò)展。它允許進(jìn)程直接存取設(shè)備層的數(shù)據(jù)包。 套接字的特點(diǎn):
(1)套接字沒(méi)有與它相連的設(shè)備文件。應(yīng)用程序可以用scoket()產(chǎn)生套接字,指定所用的信道類型。scoket()返回與所用信道末端相適應(yīng)的文件描述符。
(2)只要進(jìn)程保存文件描述符,套接字就一直存在,直到?jīng)]有進(jìn)程打開(kāi)文件描述符為止,套接字才被撤消。 (3)可以產(chǎn)生一個(gè)套接字,也可以同時(shí)產(chǎn)生一對(duì)套接字。如果產(chǎn)生一對(duì)套接字,則操作系統(tǒng)會(huì)自動(dòng)在它們之間建立信道。如果只產(chǎn)生一個(gè)套接字,則用戶程序就需要用套接字函數(shù)在該套接字與其他套接字間建立信道。 因此,socket是一個(gè)工具,或者說(shuō)是一種不可見(jiàn)控件,應(yīng)用程序可以通過(guò)socket函數(shù),來(lái)訪問(wèn)底層網(wǎng)絡(luò)協(xié)議。 2.2.2套接字地址
套接字接口利用傳送提供者進(jìn)行工作,不同的傳送提供者有不同的地址,套接字接口允許指定任意類型的地址。
Linux系統(tǒng)的套接字是一個(gè)通用的網(wǎng)絡(luò)編程接口,它支持多種協(xié)議,每一種協(xié)議使用不同的套接字地址結(jié)構(gòu)。Linux系統(tǒng)定義了一種通用的套接字地址結(jié)構(gòu),可以保持套接字函數(shù)調(diào)用參數(shù)的一致性。如下所示: structsockaddr { unsignedshortsa_family; /*
地址類型,AF_xxx*/ charsa_data[14]; /*
協(xié)議地址
*/ };
其中:
sa_family:保存協(xié)議標(biāo)識(shí)符。
AF_INET:代表TCP/IP協(xié)議簇。
sa_data:保存具體的協(xié)議地址。
TCP/IP協(xié)議簇的套接字地址也可以采用如下結(jié)構(gòu):
#include<netinet/in.h>
#include<sys/socket.h>
structin_addr { _u32s_addr; /*?UINT類型
*/ } structsockaddr_in { shortintsin_family; /*
地址類型:AF_XXX*/ unsignedshortintsin_port; /*
端口號(hào)
*/ structin_addrsin_addr; /*?Internet地址
*/ unsignedcharsin_zero[8];}; (3)?sin_port和sin_addr必須保證以網(wǎng)絡(luò)字節(jié)順序傳輸。
(4)?IP地址作為參數(shù)傳送時(shí),注意×××addr.sin_addr與×××addr.sin_addr.s_addr之間的差別?!痢痢羇ddr.sin_addr形式引用的是structsin_addr結(jié)構(gòu)類型的數(shù)據(jù),×××addr.sin_addr.s_addr形式的引用是整數(shù)類型的數(shù)據(jù)。
(5)由于<linux/in.h>和<linux/socket.h>是Linux特有的頭文件,為了能夠保持代碼的可移植性,在程序中不要直接包含它們,而與平臺(tái)無(wú)關(guān)的<netinet/in.h>、<sys/socket.h>包含了它們。因此,程序中應(yīng)該包含這兩個(gè)頭文件。 2.2.3IP地址的使用 在設(shè)置sockaddr_in類型的地址時(shí),需要進(jìn)行字符串形式的IP地址和二進(jìn)制形式的地址間的轉(zhuǎn)換,有如下一系列的函數(shù)可以處理IP地址的轉(zhuǎn)換:
#include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h> intinet_aton(constchar*cp,structin_addr*inp); unsignedlongintinet_addr(constchar*cp); char*inet_ntoa(structin_addrin);
這幾個(gè)函數(shù)將點(diǎn)分十進(jìn)制數(shù)字形式表示的IP地址與32位的網(wǎng)絡(luò)字節(jié)順序的二進(jìn)制形式的IP地址進(jìn)行轉(zhuǎn)換。例如,可以使用 inet_addr()程序把諸如“0”形式的IP地址轉(zhuǎn)化為無(wú)符號(hào)的整型數(shù)C0A8000A。函數(shù)inet_addr和inet_aton功能相同,函數(shù)inet_addr已過(guò)時(shí),編程時(shí)應(yīng)使用函數(shù)inet_aton。也可以調(diào)用inet_ntoa()把地址轉(zhuǎn)換成數(shù)字和句點(diǎn)的形式:printf("%s",
inet_ntoa(ina.sin_addr));
這將會(huì)打印出IP地址。它返回的是一個(gè)指向字符串的指針。例如: char*a1,
*a2; … a1=inet_ntoa(ina1.sin_addr); /*
這是的二進(jìn)制形式
*/ a2=inet_ntoa(ina2.sin_addr); /*
這是0的二進(jìn)制形式*/ printf("address1:%s\n",a1); printf("address2:%s\n",a2);
輸出如下:
address1: address2:02.3基本套接字函數(shù)
在各種網(wǎng)絡(luò)編程接口中,socket脫穎而出,越來(lái)越得到大家的重視,這是因?yàn)閟ocket規(guī)范是一套開(kāi)放的、支持多種協(xié)議的網(wǎng)絡(luò)編程接口。經(jīng)過(guò)不斷發(fā)展和完善,并在Intel、Microsoft、Sun、SGI、Informix、Novell等公司的全力支持下,已成為網(wǎng)絡(luò)編程的事實(shí)上的標(biāo)準(zhǔn)。下面給出套接字函數(shù)及其使用方法。
1.socket()
在利用套接字進(jìn)行網(wǎng)絡(luò)通信時(shí),進(jìn)程要做的第一件事就是調(diào)用socket(),產(chǎn)生一個(gè)套接字,并指明將要使用的通信協(xié)議,如TCP、UDP、XNS、SPP等。 #include<sys/types.h>
#include<sys/socket.h>
intsocket(intfamily,inttype,intprotocol);
socket()返回一文件描述符,從應(yīng)用的角度講,該文件描述符是指通信信道的末端。如果調(diào)用失敗,則返回-1。其中參數(shù)定義為:
●family:表示所用的協(xié)議是協(xié)議簇中的哪一個(gè)。協(xié)議簇是有相同地址格式的一組傳送提供者。例如,TCP和UDP有同樣的地址格式,因此它們屬于同一協(xié)議簇。family的值可以為:
AF_INET:TCP/IP協(xié)議集合。
AF_UNIX:UNIX域協(xié)議簇,在本機(jī)的進(jìn)程間通信時(shí)使用。
AF_ISO:ISO協(xié)議簇。 ●type:表示套接字類型: sock_STREAM:提供虛電路服務(wù)的流套接字。
sock_DGRAM:提供數(shù)據(jù)報(bào)服務(wù)的套接字。
sock_RAW:原始套接字,只對(duì)Internet協(xié)議有效,可以用來(lái)直接訪問(wèn)IP協(xié)議。
sock_SEQPACKET:有序分組套接字。
sock_RDM:能可靠交付信息的數(shù)據(jù)報(bào)套接字。 ●protocol:表示指定所用協(xié)議。對(duì)于大多數(shù)應(yīng)用protocol都被設(shè)置為0,表示使用默認(rèn)協(xié)議。但是,如果對(duì)給定的family及服務(wù)類型有多種協(xié)議可選,就須指定所用協(xié)議。例如,套接字要用TCP協(xié)議。因?yàn)門(mén)CP是TCP/IP協(xié)議集合中的一員,要用這個(gè)協(xié)議簇,應(yīng)將family設(shè)為AF_INET。TCP/IP支持虛電路服務(wù),故應(yīng)將第二個(gè)參數(shù)types設(shè)為SOCK_STREAM。因?yàn)門(mén)CP是TCP/IP協(xié)議集合中惟一提供虛電路服務(wù)的傳送提供者,所以protocol可以設(shè)為0。socket()調(diào)用為: intfd; fd=socket(AF_INET,SOCK_STREAM,0);
又例如,用UDP協(xié)議,支持?jǐn)?shù)據(jù)報(bào)服務(wù)。因?yàn)閁DP屬于TCP/IP協(xié)議簇,是TCP/IP協(xié)議集合中僅有的提供數(shù)據(jù)報(bào)服務(wù)的傳輸提供者,所以,protocol設(shè)置為0。該socket()調(diào)用為:
intfd; fd=socket(AF_INET,SOCK_DGRAM,0);
例如,產(chǎn)生一個(gè)套接字,訪問(wèn)TCP/IP協(xié)議集合的低層協(xié)議,可將套接字類型設(shè)為SOCK_RAW,這樣允許訪問(wèn)低層協(xié)議,包括IP和ICMP。要讓產(chǎn)生的套接字直接訪問(wèn)IP,protcol應(yīng)設(shè)為IPPROTO_RAW。socket()調(diào)用為: intfd fd=socket(AF_INET,SOCK_RAW,IPPROTO_RAW);
調(diào)用socket函數(shù)時(shí),socket執(zhí)行體將建立一個(gè)socket,并返回一個(gè)指向描述符表入口的socket句柄。實(shí)際上創(chuàng)建一個(gè)socket意味著為一個(gè)socket數(shù)據(jù)結(jié)構(gòu)分配存儲(chǔ)空間,如圖2-2所示。圖2-2socket數(shù)據(jù)結(jié)構(gòu)
表2-1給出了適應(yīng)AF_INET及AF_UNIX協(xié)議簇的type及protocol的屬性取值。表2-1AF_INET及
AF_UNIX協(xié)議簇的type及protocol的屬性取值 2.socketpair()
socketpair()產(chǎn)生兩個(gè)套接字,連接這兩個(gè)套接字,然后返回相應(yīng)的文件描述符,它也稱為UNIX域套接字。其調(diào)用格式為:
#include<sgs/types.h>
#include<sys/socket.h>
intsocketpair(intfamily,inttype,intprotocol,intfd_array[2]);
函數(shù)socketpair()返回兩個(gè)套接字描述符:socket[0]和socket[1],與管道pipe相似,只是socketpair()返回一對(duì)套接字描述符,而不是文件描述符。socketpair()返回的兩個(gè)套接字描述符是雙向的,而管道是單向的。 Family、type、protocol參數(shù)含義與socket()函數(shù)一樣,但是
socket()返回一個(gè)無(wú)連接套接字的文件描述符,socketpair()返回兩個(gè)連接好的套接字。調(diào)用成功返回2,否則返回-1。
family只能取值A(chǔ)F_UNIX,由于此系統(tǒng)調(diào)用僅用于UNIX支配協(xié)議,因此只有兩種可用形式。一種是:intrc,fd_array[2]; rc=socketpair(AF_UNIX,SOCK_STREAM,0,fd_array);
socketpair()用SVR4面向連接的傳輸提供者之一產(chǎn)生一個(gè)通信信道。另一種是: intrc,fd_array[2]; rc=socketpair(AF_UNIX,SOCK_DGRAM,0,fd_array);
這里,函數(shù)socketpair()用無(wú)連接的傳輸提供者產(chǎn)生一個(gè)通信信道。 下面的函數(shù)可以用來(lái)生成一個(gè)域套接字間的通信信道。
#include<sys/types.h> #include<sys/socket.h> /*
如果成功,則返回0,否則返回-1*/ /*
用fd返回兩個(gè)文件描述符
*/
intfd[2]; { return(socketpair(AF_UNIX,SOCK_STREAM,0,fd)); } 3.bind()
調(diào)用socket函數(shù)創(chuàng)建套接字后,存在一個(gè)名字空間,但它沒(méi)有被命名。bind()將套接字地址(包括本地主機(jī)地址和本地端口地址)與所創(chuàng)建的套接字句柄聯(lián)系起來(lái),即將名字賦予套接字,以指定本地址中的{協(xié)議,本地地址,本地端口}。其調(diào)用格式如下:
#include<sys/types.h> #include<sys/socket.h> int=bind(intfd,structsockaddr*addressp,
intaddrlen);
其中:
fd:由socket()函數(shù)返回的套接字描述符。
addressp:向協(xié)議傳送地址的指針,包含有關(guān)的地址信息:名稱、端口和IP地址。
addrlen:地址結(jié)構(gòu)的字節(jié)數(shù)。
bind()把傳送地址與套接字連接在一起,調(diào)用成功返回
0,否則返回-1。地址的格式取決于與套接字相連的信道。例如,如果套接字的family設(shè)置為AF_UNIX,那么地址取sockaddr_un結(jié)構(gòu)地址。 服務(wù)器和客戶機(jī)均可以調(diào)用函數(shù)bind綁定套接字地址,服務(wù)器往往綁定公認(rèn)端口號(hào),提供公共服務(wù)。
處理地址綁定的程序片段如下:
bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family=AF_INET; /*
地址簇
*/ servaddr.sin_port=htons(serverport); /*
端口號(hào)
*/ servaddr.sin_addr.s_addr=htonl(INADDR_ANY); /*
使用本機(jī)IP地址
*/
if(bind(sockfd,(structsockaddr*)&servaddr,
sizeof(structsockaddr))<0) { perror("bindtoserverporterror"); exit(1) }
注意,在給網(wǎng)絡(luò)地址賦值時(shí),程序使用了INADDR_ANY,而不是確定的IP地址。這里INADDR_ANY的含義是任何網(wǎng)絡(luò)設(shè)備接口。對(duì)于一臺(tái)只有一個(gè)IP地址的主機(jī),它就對(duì)應(yīng)于它的IP地址。但是有的主機(jī)可能有多個(gè)網(wǎng)絡(luò)接口,即多個(gè)網(wǎng)卡,并擁有多個(gè)IP地址,INADDR_ANY表示來(lái)自所有網(wǎng)絡(luò)接口的相應(yīng)傳輸層端口的連接請(qǐng)求,都由這個(gè)服務(wù)器進(jìn)行處理。路由器就是典型的例子。 一般來(lái)說(shuō),綁定操作具有以下幾種組合方式,見(jiàn)表2-2。表2-2綁定地址及端口號(hào)的設(shè)置方式 (1)服務(wù)器的套接字設(shè)定公認(rèn)端口號(hào)和INADDR_ANY地址。服務(wù)器的套接字設(shè)定INADDR_ANY地址表明它要接收來(lái)自任何網(wǎng)絡(luò)設(shè)備接口的客戶連接請(qǐng)求,這是服務(wù)器常用的地址綁定方式。
(2)服務(wù)器的套接字設(shè)定公認(rèn)端口號(hào)和IP地址。如果服務(wù)器的套接字綁定公認(rèn)端口號(hào)和具體IP地址,則表明服務(wù)器只接收來(lái)自這個(gè)IP地址的網(wǎng)絡(luò)接口卡的客戶機(jī)的連接請(qǐng)求。當(dāng)服務(wù)器只有一個(gè)網(wǎng)絡(luò)接口設(shè)備時(shí),這種設(shè)置和(1)沒(méi)有區(qū)別。當(dāng)服務(wù)器有多個(gè)網(wǎng)絡(luò)接口設(shè)備時(shí),這種方法可以用來(lái)限制服務(wù)器的接收范圍。 (3)客戶機(jī)指定套接字地址中的端口號(hào)。當(dāng)客戶機(jī)調(diào)用connect函數(shù)請(qǐng)求TCP連接時(shí),系統(tǒng)會(huì)自動(dòng)為它選擇一個(gè)未用端口號(hào),并且用本地的IP地址設(shè)置套接字地址的相應(yīng)項(xiàng)。因此,一般來(lái)說(shuō),客戶機(jī)不用指定自己套接字地址的端口號(hào)。當(dāng)客戶機(jī)需要使用特定端口號(hào)時(shí),如Linux系統(tǒng)中的rlogin命令,應(yīng)當(dāng)調(diào)用bind函數(shù)綁定一個(gè)未用的保留端口號(hào)。
(4)客戶機(jī)設(shè)置指定IP地址和連接端口號(hào)。當(dāng)客戶機(jī)綁定指定IP地址和連接端口號(hào)時(shí),表示要用指定的網(wǎng)絡(luò)設(shè)備接口和端口號(hào)進(jìn)行通信。
(5)指定客戶機(jī)的IP地址。客戶機(jī)使用指定的網(wǎng)絡(luò)設(shè)備接口進(jìn)行通信,由系統(tǒng)為其選擇一個(gè)未用的端口號(hào)。這種情況在系統(tǒng)具有多個(gè)網(wǎng)絡(luò)設(shè)備接口時(shí)使用。
4.connect()
客戶進(jìn)程在用socket()產(chǎn)生套接字后,用connect()將該套接字與服務(wù)器套接字相連接。
#include<sys/types.h>
#include<sys/socket.h>
intconnect(intfd,structsockaddr*addressp,intaddrlen);
其中:
fa:是套接字描述符。
addressp:套接字地址指針。
addrlen:地址結(jié)構(gòu)的字節(jié)數(shù)。 地址的格式取決于所用的信道。例如,如果信道的domain=AF_INET,那么用sockaddr_in結(jié)構(gòu)保存地址;如果domain=AF_UNIX,那么用sockaddr_un結(jié)構(gòu)保存地址。
如果用數(shù)據(jù)報(bào)服務(wù),則connect()在套接字與目的地址間建立簡(jiǎn)單的聯(lián)系。系統(tǒng)在所有通過(guò)信道發(fā)送的數(shù)據(jù)報(bào)上均放上該地址。
通常,在調(diào)用connect()前,客戶應(yīng)用程序應(yīng)綁定套接字地址,提出連接請(qǐng)求的程序片段如下:
bzero(&servaddr,
sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(serverport);
if(inet_aton("",&servaddr.sin_addr)<0)
{
perror("inet_atonerror");
exit(1);
} …
if(connect(sockfd,
(structsockaddr*)&servaddr,
sizeof(servaddr))<0)
{
perror("connecterror");
exit(1);
}
...
客戶機(jī)一般不指定自己的端口號(hào),而由系統(tǒng)為其選一個(gè)端口號(hào),即自由端口。 5.listen()
當(dāng)服務(wù)器完成當(dāng)前請(qǐng)求以前又發(fā)生多個(gè)服務(wù)請(qǐng)求時(shí),例如,假設(shè)重復(fù)服務(wù)器處理完最近服務(wù)請(qǐng)求以前,客戶程序試圖與它相連,或者假設(shè)并行服務(wù)器在為最近服務(wù)請(qǐng)求建立好一個(gè)單獨(dú)進(jìn)程以前,客戶程序請(qǐng)求和服務(wù)器建立連接。在這種情況下,服務(wù)器可以拒絕到來(lái)的服務(wù)請(qǐng)求。為了更好地處理這些問(wèn)題,服務(wù)器可以使用listen()函數(shù)將所有的服務(wù)請(qǐng)求放在一個(gè)請(qǐng)求隊(duì)列中排隊(duì),并盡快地處理這些請(qǐng)求。 socket執(zhí)行體建立一個(gè)輸入請(qǐng)求隊(duì)列,將到達(dá)的服務(wù)請(qǐng)求保存在此隊(duì)列上,直到處理完它們?yōu)橹梗磍isten()函數(shù)不僅使socket處于被動(dòng)的偵聽(tīng)狀態(tài),同時(shí)告訴socket執(zhí)行體對(duì)此socket處理多個(gè)同時(shí)的服務(wù)請(qǐng)求。面向連接的服務(wù)器用該函數(shù)指明它愿意接收連接。其調(diào)用格式為:
#include<sys/socket.h> intlisten(intfd,intqlen);
其中:
fd:套接字的文件描述符,套接字只能是SOCK_STREAM,SOCK_SEQPACKET類型。
qlen:連接請(qǐng)求隊(duì)列長(zhǎng)度。
當(dāng)一個(gè)連接請(qǐng)求到達(dá)時(shí),被插入請(qǐng)求隊(duì)列。服務(wù)器用accept()函數(shù)從隊(duì)列中移走一個(gè)請(qǐng)求并響應(yīng)這個(gè)請(qǐng)求。listen()函數(shù)不進(jìn)入睡眠狀態(tài)等待請(qǐng)求的到達(dá),它只建立一等待隊(duì)列,控制便返回應(yīng)用程序。執(zhí)行成功便返回0值,否則返回-1。
在后面的例程中,將看到listen()函數(shù)的使用。 6.a(chǎn)ccept()
面向連接的服務(wù)器執(zhí)行了listen()函數(shù)后,執(zhí)行accept()函數(shù)等待來(lái)自某一客戶進(jìn)程的實(shí)際連接請(qǐng)求。然后進(jìn)入睡眠狀態(tài),等待客戶機(jī)的連接請(qǐng)求。當(dāng)服務(wù)請(qǐng)求到達(dá)accept()函數(shù)監(jiān)視的socket時(shí),socket執(zhí)行體將自動(dòng)建立一個(gè)新的socket,并將此socket和客戶進(jìn)程連接起來(lái)。收到服務(wù)請(qǐng)求的初始socket仍具有設(shè)定的地址,因此,它可以繼續(xù)接收到達(dá)的服務(wù)請(qǐng)求。 下面是accept()函數(shù)的聲明:
#include<sys/types.h> #include<sys/socket.h> intaccept(intfd,sockaddr*addressp,int*addrlen);
參數(shù)addressp用于返回所連接的對(duì)等進(jìn)程的地址,里面存儲(chǔ)著遠(yuǎn)程連接的計(jì)算機(jī)信息(比如遠(yuǎn)程計(jì)算機(jī)的IP地址和端口)。參數(shù)addrlen是一個(gè)本地的整型數(shù)值,由協(xié)議執(zhí)行體設(shè)置它的值。一般情況下,調(diào)用者把該值設(shè)置為緩沖區(qū)的大小,該調(diào)用也可設(shè)置成sockaddr的結(jié)構(gòu)大小,在系統(tǒng)調(diào)用返回時(shí)將此值改變成為存放在緩沖區(qū)中的實(shí)際字節(jié)數(shù)。
accept()從隊(duì)列上取出第一個(gè)連接請(qǐng)求,建立一個(gè)與參數(shù)fd相同特性的套接字。參數(shù)fd所指套接字類型可以是SOCK_STREAM或SOCK_SEQPACKET。如果采用同步通信模式,則accept()將等待連接請(qǐng)求的到達(dá)。在連接請(qǐng)求到達(dá)時(shí),accept()產(chǎn)生一個(gè)新套接字,并對(duì)其行連接,返回新套接字的文件描述符。此刻用所產(chǎn)生的新套接字與客戶應(yīng)用進(jìn)行通信,而用原來(lái)的套接字接收其他連接請(qǐng)求。
如果調(diào)用accept()時(shí)隊(duì)列上無(wú)連接請(qǐng)求,那么此調(diào)用在一個(gè)請(qǐng)求到達(dá)前阻塞調(diào)用者的運(yùn)行,accept()返回-1,errno置為EWOULDBLOCK,指出無(wú)連接請(qǐng)求到達(dá)。
7.read()及write() read()函數(shù)和write()函數(shù)是用來(lái)接收和發(fā)送數(shù)據(jù)的兩個(gè)函數(shù)。read()函數(shù)用于從套接字緩沖區(qū)讀取數(shù)據(jù),write()函數(shù)用于往套接字緩沖區(qū)寫(xiě)數(shù)據(jù),它們的定義分別如下:
intread(intfd,char*buf,intlen); intwrite(intfd,char*buf,intlen);
參數(shù)fd是我們要進(jìn)行讀寫(xiě)操作的連接套接字描述符(由connect()函數(shù)或accept()函數(shù)返回),參數(shù)buf是用于存放欲接收或待發(fā)送數(shù)據(jù)的應(yīng)用緩沖區(qū),參數(shù)len指定了欲發(fā)送或者接收的數(shù)據(jù)字節(jié)數(shù)。
對(duì)于read()函數(shù),調(diào)用后將從套接字接收緩沖區(qū)中讀取len字節(jié)的數(shù)據(jù),如果函數(shù)成功返回,返回值將是實(shí)際讀取數(shù)據(jù)的字節(jié)數(shù),見(jiàn)表2-3。表2-3read()函數(shù)返回值及其意義
對(duì)于write()函數(shù),返回值意義與read()函數(shù)基本相同,只不過(guò)當(dāng)函數(shù)成功返回時(shí),返回值表示的是實(shí)際寫(xiě)入數(shù)據(jù)的字節(jié)數(shù)。 對(duì)于read()和write()函數(shù)參數(shù)中的參數(shù)“buf”,指的是接收或發(fā)送進(jìn)程的應(yīng)用緩沖區(qū)。實(shí)際上,進(jìn)程創(chuàng)建的每個(gè)TCP套接字還有一個(gè)套接字接收緩沖區(qū)和發(fā)送緩沖區(qū),分別用于存放即將從網(wǎng)絡(luò)讀或?qū)懴蚓W(wǎng)絡(luò)的數(shù)據(jù),真正從套接字緩沖區(qū)到網(wǎng)絡(luò)讀寫(xiě)數(shù)據(jù)的過(guò)程由內(nèi)核TCP協(xié)議來(lái)完成,不需要應(yīng)用進(jìn)程干涉。
即read()函數(shù)并不是從網(wǎng)絡(luò)讀取數(shù)據(jù),而只是完成從套接字接收緩沖區(qū)到接收進(jìn)程應(yīng)用緩沖區(qū)復(fù)制數(shù)據(jù)的任務(wù),此時(shí),有幾種可能情況發(fā)生:如果套接字接收緩沖區(qū)的可讀數(shù)據(jù)量小于read()函數(shù)的參數(shù)len指定的數(shù)據(jù)量時(shí),則read()函數(shù)返回套接字緩沖區(qū)的所有數(shù)據(jù),此時(shí),返回值會(huì)小于len的值;如果套接字接收緩沖區(qū)的可讀數(shù)據(jù)大于參數(shù)len指定的數(shù)據(jù)量時(shí),則read()函數(shù)將返回參數(shù)len指定的數(shù)據(jù)量;如果套接字接收緩沖區(qū)暫時(shí)無(wú)可讀數(shù)據(jù),則read()函數(shù)將阻塞,一直等到有數(shù)據(jù)返回。
write()函數(shù)執(zhí)行的操作以及返回情況類似于read()函數(shù)。特別需要注意的是,write()函數(shù)成功返回之后,并不意味著數(shù)據(jù)已經(jīng)通過(guò)網(wǎng)絡(luò)發(fā)送到對(duì)方主機(jī),而只是說(shuō)明已經(jīng)存放在套接字緩沖區(qū)中,等待TCP協(xié)議來(lái)發(fā)送。 圖2-3表示了進(jìn)程應(yīng)用緩沖區(qū)和套接字緩沖區(qū)的關(guān)系。圖2-3進(jìn)程應(yīng)用緩沖區(qū)與套接字緩沖區(qū)的關(guān)系
下面給出兩個(gè)程序段來(lái)幫助理解函數(shù)read()和write()的使用。
1)函數(shù)read_all intread_all(intfd,char*buf,intnBytes); { for(;;) { rc=read(fd,buf,nBytes); if(rc>0) /*
讀出rc個(gè)字節(jié)
*/ {returnrc;} elseif(rc==0) /*
讀通道已關(guān)閉
*/{close(fd);return0;}elseif(errno==EINTR){/*
由讀中斷引起錯(cuò)誤
*/} else { printf(stderr,"Readerror"); close(fd); return-1; } } }
當(dāng)read用于套接字讀取時(shí),應(yīng)分析read()函數(shù)的返回值,特別是返回值小于等于零的情況,當(dāng)返回值小于零時(shí)并非表示真正出錯(cuò)。 (1)返回值大于零,套接字接收緩沖區(qū)接收到數(shù)據(jù),返回值表示讀取的數(shù)據(jù)字節(jié)數(shù)。
(2)返回值等于零,并且以后這個(gè)套接字讀操作返回值均為0,表示對(duì)方已發(fā)送完所有的數(shù)據(jù)。這和讀文件時(shí)遇到文件結(jié)束符的情況相似。
(3)如果read錯(cuò)誤返回,且錯(cuò)誤號(hào)為EINTR,則是指進(jìn)程阻塞過(guò)程中接收到了信號(hào),并非真正出錯(cuò),以后可以繼續(xù)利用該套接字讀數(shù)據(jù)。
(4)當(dāng)read錯(cuò)誤返回,且錯(cuò)誤號(hào)為ECONNRESET時(shí),表示TCP協(xié)議接收到RST數(shù)據(jù)段,連接出現(xiàn)了某種錯(cuò)誤,并且以后所有在這個(gè)套接字上的讀操作均返回錯(cuò)誤。 2)函數(shù)write_all
intwrite_all(intfd,char*buf,intnBytes);
{
for(;;)
{
wc=write(sockfd,buf,
nBytes);
if(wc>0)/*
寫(xiě)入成功
*/
returnwc;
elseif(errno==EINTR) { /*
由中斷引起錯(cuò)誤
*/ } else { printf(stderr,"Writeerror"); close(sockfd); return-1; } } }
同讀操作類似,write操作也要按返回值分情況處理。
(1)?TCP協(xié)議接收到RST數(shù)據(jù)段。當(dāng)接收方已關(guān)閉連接之后,如果繼續(xù)向這個(gè)套接字上發(fā)送數(shù)據(jù)將導(dǎo)致對(duì)方的TCP協(xié)議返回RST數(shù)據(jù)段,函數(shù)wirte返回錯(cuò)誤,錯(cuò)誤類型為EPIPE,并且以后所有這個(gè)套接字上的寫(xiě)操作均以錯(cuò)誤返回。
(2)進(jìn)程阻塞期間接收到信號(hào)。如果進(jìn)程接收到信號(hào),則函數(shù)wirte()返回錯(cuò)誤,錯(cuò)誤類型為EINTR。這并不是真正出現(xiàn)了錯(cuò)誤,可以繼續(xù)利用該套接字發(fā)送數(shù)據(jù)。 以上兩程序段分別實(shí)現(xiàn)對(duì)緩存內(nèi)數(shù)據(jù)的讀(read)、寫(xiě)(write)操作,較為詳細(xì)的說(shuō)明了read()和write()兩個(gè)函數(shù)的調(diào)用方法和出錯(cuò)類型。2.4高級(jí)套接字函數(shù) 1.send()及sendto() send()與sendto()均可用來(lái)通過(guò)信道發(fā)送數(shù)據(jù)。send()可用于流式或數(shù)據(jù)報(bào)通信信道。在采用數(shù)據(jù)報(bào)傳送方式時(shí),應(yīng)利用connect()函數(shù)給出所連接的套接字地址。sendto()函數(shù)用于無(wú)連接的數(shù)據(jù)報(bào)發(fā)送,發(fā)送數(shù)據(jù)報(bào)時(shí)須指出數(shù)據(jù)要發(fā)送到的目的地址。由于這個(gè)地址是與協(xié)議相關(guān)的,因此不同協(xié)議對(duì)應(yīng)的地址也不同,其長(zhǎng)度由addrlen指定。sendto()基本上用于無(wú)連接套接字。它們的功能與write系統(tǒng)調(diào)用相似。其調(diào)用方式如下: #include<sys/types.h>
#include<sys/socket.h>
intsend(intfd,char*buf,intlen,intflags);
intsendto(intfd,char*buf,intlen,intflags,structsockaddr*toaddr,intaddrlen);
其中,參數(shù)定義為: fd:套接字的文件描述符。
buf:數(shù)據(jù)緩沖區(qū)。
len:數(shù)據(jù)緩沖區(qū)字節(jié)數(shù)。
toaddr:存放數(shù)據(jù)要發(fā)送的目的套接字地址。
addrlen:地址的字節(jié)數(shù)。
flags:取值為0時(shí),send()的功能與wrirte()相同;為MSG_OOB時(shí),發(fā)送連接帶外數(shù)據(jù)(OOB,Out_Of_Band);為MSG_DONTWAIT時(shí),如果套接字緩沖區(qū)中沒(méi)有足夠空間,則進(jìn)程不阻塞等待;MSG_DONTROULE:旁路路由選擇。
2.recv()及recvfrom()
recv()及recvfrom()功能類似于read()系統(tǒng)調(diào)用。它們都是用來(lái)從連接的套接字上讀數(shù)據(jù),可以用在client或server上的應(yīng)用中。它們的調(diào)用格式為:
#include<sys/types.h>
#include<sys/socket.h>
intrecv(intfd,char*buf,intlen,intflags);
intrecvfrom(intfd,char*buf,intlen,intflags,structsockaddr*fromaddr,intaddrlen);
其中,參數(shù)定義如下:
fd:套接字的文件描述符。
buf:數(shù)據(jù)緩沖區(qū)。
len:數(shù)據(jù)緩沖區(qū)字節(jié)數(shù)。
fromaddr:發(fā)送數(shù)據(jù)的套接字地址。
addrlen:地址的字節(jié)數(shù)。
flags:取值為:0:功能與read()相同;MSG_OOB:接收連接外數(shù)據(jù);MSG_WAITALL:如果套接字緩沖區(qū)中沒(méi)有足夠空間,則進(jìn)程不阻塞等待;MSG_PEEK:將數(shù)據(jù)放置于緩沖區(qū)中,該數(shù)據(jù)使用后并不馬上被取消,因此,下一個(gè)讀操作將會(huì)讀到同樣的數(shù)據(jù);MSG_DONTROUTE:發(fā)送數(shù)據(jù)時(shí),不需要查看路由表。
返回值:按同步方式工作時(shí),等待數(shù)據(jù)的到達(dá);按異步方式工作時(shí),如果套接字中無(wú)數(shù)據(jù),則設(shè)置errno為EWOULDBLOCK,并返回-1,表示數(shù)據(jù)未準(zhǔn)備好。只返回-1時(shí),表示出錯(cuò)。
3.sendmsg()及recvmsg() sendmsg()用來(lái)發(fā)送數(shù)據(jù),包含sendto()的功能。除此之外,它還可以規(guī)格化數(shù)據(jù)以及發(fā)送被中斷了的數(shù)據(jù),與sendto()一樣,可以用于虛電路和數(shù)據(jù)報(bào)方式。recvmsg()用于接收sendmsg()發(fā)送的數(shù)據(jù)。它們的使用方式為:
#include<sys/types.h> #include<sys/socket.h> #include<sys/uio.h> structiovec {caddr_t iov_base; /*
緩沖區(qū)的起始地址
*/ int iov_len; /*
緩沖區(qū)的字節(jié)數(shù)
*/ };
structmsghdr {caddr_t msg_name; /*
目的套接字地址
*/
int msg_namelen; /*
地址的字節(jié)數(shù)
*/ structiovec*msg_iov; /*
包含信息的數(shù)組
*/ intmsg_iovlen; /*
結(jié)構(gòu)數(shù)組的成員量
*/caddr_tmsg_accrights; /*
訪問(wèn)權(quán)限
*/intmsg_accrightslen;};
函數(shù)定義:
intsendmsg(intfd,structmsghdr*msgp,intflags); intrecvmsg(intfd,structmsghdr*msgp,intflags);
其中,結(jié)構(gòu)iovec保存信息數(shù)據(jù)。
msg_name和msg_namelen是發(fā)送信息的套接字地址。
msg_iov由sendmsg()函數(shù)填寫(xiě)結(jié)構(gòu)
iovec的內(nèi)容。 4.readv和writev函數(shù)
readv、writev函數(shù)同read、wirte功能類似,readv和writev函數(shù)用于一次讀、寫(xiě)多個(gè)非連續(xù)緩存。有時(shí)也將這兩個(gè)函數(shù)稱為散布讀(scatterread)和聚集寫(xiě)(gatherwrite)。調(diào)用格式為:
#include<sys/types.h>
#include<sys/uio.h>
ssize_treadv(intfiledes,conststructioveciov[],intiovcnt);ssize_twritev(intfiledes,conststructioveciov[],intiovcnt);
兩個(gè)函數(shù)返回:已讀、寫(xiě)的字節(jié)數(shù);若出錯(cuò),則為-1; 兩個(gè)函數(shù)中的第二個(gè)參數(shù)是指向iovec結(jié)構(gòu)數(shù)組的一個(gè)指針:
structiovec{ void*iov_base; /*
緩沖區(qū)起始地址
*/ size_tiov_len; /*
緩沖區(qū)大小
*/ };
成員變量iov_base指向緩沖區(qū)的開(kāi)始地址,iov_len標(biāo)明該緩沖區(qū)的大小。
readv和writev函數(shù)的定義以及它們使用的iovec結(jié)構(gòu)在各種有關(guān)文獻(xiàn)資料中略有差別。如果比較它們?cè)?/p>
SVR4程序員手冊(cè)〔AT&T1990e〕、SVR4的SVID〔AT&T1989〕,以及SVR4和
4.3+BSD<sys/uio.h>頭文件中的定義,可以看出它們之間都有些差別。上面給出的結(jié)構(gòu)定義為POSIX.1的定義。 圖2-4顯示了readv和writev的參數(shù)和iovec結(jié)構(gòu)之間的關(guān)系。writev以順序iov[0],iov[1]至iov[iovcnt-1]從緩存中聚集輸出數(shù)據(jù)。writev返回輸出的字節(jié)總數(shù),它應(yīng)等于所有緩存長(zhǎng)度之和,readv則將讀入的數(shù)據(jù)按上述同樣順序散布到緩存中。readv總是先填滿一個(gè)緩存,然后再填寫(xiě)下一個(gè)。readv返回讀得的總字節(jié)數(shù)。如果遇到文件結(jié)尾,已無(wú)數(shù)據(jù)可讀,則返回0。圖2-4readv和writev中的iovec結(jié)構(gòu)
#include<sys/socket.h>#include<stdio.h>#include<netinet/in.h>#defineSERV_PORT8080voidrequ_client(intsockfd){intn;charbuf1[512],hdrbuf[512];structioveciov[2];while(1){if(gets(buf1)==NULL)return;iov[0].iov_base=hdrbuf;iov[0].iov_len=strlen(hdrbuf);iov[1].iov_base=buf1;iov[1].iov_len=strlen(buf1);writev(sockfd,iov,2);n=read_all(sockfd,buf1,n);if(n==0)return;elseif(n<0){fprintf(stderr,"readerror.\n");return;}write(1,buf1,n);}}
緩沖區(qū)hdrbuf存放的是請(qǐng)求首部,buf1存放的是請(qǐng)求內(nèi)容,該函數(shù)將hdrbuf和buf1組成一個(gè)iovc結(jié)構(gòu),調(diào)用函數(shù)writev將它們一次發(fā)送出去,可見(jiàn)這種方法對(duì)有關(guān)聯(lián)的多緩沖區(qū)中的數(shù)據(jù)傳送比較方便。 因此,對(duì)于多緩沖區(qū)中的數(shù)據(jù)傳送,一般采用readv和writev,而不采用多次read和write。
5.close()
close()可以用來(lái)關(guān)閉套接字。其調(diào)用格式為:
#include<unistd.h>
intclose(intfd);
其中,fd為套接字描述符。
通常close在關(guān)閉一個(gè)TCP連接時(shí),close將立即返回。進(jìn)程將不能再使用這個(gè)套接字描述符來(lái)訪問(wèn)套接字,但是TCP可能并沒(méi)有刪除套接字結(jié)構(gòu),因?yàn)榭赡茉诎l(fā)送數(shù)據(jù)緩沖區(qū)還有數(shù)據(jù)沒(méi)有發(fā)送完。TCP將繼續(xù)發(fā)送剩余的數(shù)據(jù),并在最后的數(shù)據(jù)段附加FIN控制信息。
6.shutdown()
終止網(wǎng)絡(luò)連接除了可以利用close()系統(tǒng)調(diào)用外,還可以利用shoutdown()。shoutdown()終止網(wǎng)絡(luò)連接并停止所有信息的發(fā)送和接收工作。client和server可以用該系統(tǒng)調(diào)用關(guān)閉虛電路或數(shù)據(jù)報(bào)的傳送。其調(diào)用格式為:
#include<sys/socket.h>
intshoutdown(intfd,intaction);
參數(shù)說(shuō)明:
fd:是套接字的文件描述符。
action:可取以下值。其含義為:0表示停止所有套接字上的讀操作;1表示停止所有套接字上的寫(xiě)操作;2表示停止所有套接字上的讀、寫(xiě)操作。 返回值:
0:成功。
-1:失敗。 一般套接字是全雙工通信信道,兩個(gè)方向的信息傳送相對(duì)是獨(dú)立的,所以shoutdown()可以關(guān)閉連接的任一方向,不會(huì)影響另一方向的傳輸。 shutdown和close的比較:
(1)引用計(jì)數(shù)的比較:和文件的操作類似,函數(shù)close將會(huì)把套接字描述符指向的套接字結(jié)構(gòu)的引用計(jì)數(shù)減1,當(dāng)引用計(jì)數(shù)為0時(shí),系統(tǒng)將關(guān)閉套接字,所有的進(jìn)程都不能再訪問(wèn)這個(gè)套接字。而shutdown并不考慮套接字的引用計(jì)數(shù),無(wú)論引用計(jì)數(shù)為多少,它都將發(fā)送FIN數(shù)據(jù)段給對(duì)端,表示本端的某個(gè)信息已經(jīng)關(guān)閉。所以,shutdown實(shí)際上直接對(duì)TCP連接進(jìn)行操作,而不是對(duì)套接字描述符進(jìn)行操作。
(2)關(guān)閉信道的比較:如果引用計(jì)數(shù)為0,close調(diào)用將關(guān)閉該寫(xiě)信道。它不能單獨(dú)關(guān)閉某個(gè)單向的信道。在close后,即使發(fā)送方還有數(shù)據(jù)沒(méi)有發(fā)送完,接收方也不再接收。而shutdown可以直接控制某個(gè)單向信道的關(guān)閉。 7.getpeername() client和server雙方均可用getpeername()獲得與指定套接字連接的對(duì)等進(jìn)程名。其調(diào)用格式為:
#include<sys/types.h> #include<sys/socket.h> intgetpeername(intfd,structsockaddr*proaddr,int*addrlen);
參數(shù)說(shuō)明:
fd:套接字的文件描述符。
proaddr:緩沖區(qū)指針。
addrlen:緩沖區(qū)字節(jié)數(shù)指針。
getpeername()根據(jù)調(diào)用結(jié)果填充proaddr所指的緩沖區(qū),并以緩沖區(qū)中實(shí)際填入的字節(jié)數(shù)更新addrlen。 返回值:
0:成功。
-1:失敗。 8.getstockname()
getstockname()返回與套接字相連的本地進(jìn)程的地址和進(jìn)程元素。它的調(diào)用格式與getpeername()相似:
#include<sys/types.h>
#include<sys/socket.h>
intgetsockname(intfd,structsockaddr*proaddr,int*addrlen); 9.getsockopt()及setsockopt() getsockopt()用于檢測(cè)信道上的各選擇項(xiàng)。setsockopt()用于設(shè)置信道上的各選擇項(xiàng)。這兩個(gè)系統(tǒng)調(diào)用僅用于套接字的選擇項(xiàng),通過(guò)設(shè)置選擇項(xiàng)來(lái)影響套接字,其調(diào)用格式為:
#include<sys/types.h>
#include<sys/socket.h>
intgetsockopt(intfd,intlevel,intoptname,char*optval,int*optlen);
intsetsockopt(intfd,intlevel,intoptname,char*optval,int*optlen);
參數(shù)說(shuō)明:
fd:套接字描述符。
level:指明系統(tǒng)中由哪個(gè)程序解釋選擇項(xiàng)。例如,是普通套接字程序,TCP/IP程序還是XNS程序。圖2-5用TCP/IP協(xié)議的信道,可以通過(guò)getsockopt()和setsockopt()獲取和設(shè)置套接字、TCP、IP級(jí)別上的參數(shù)項(xiàng)。
optname:指定設(shè)置或查詢的選擇項(xiàng)。
optval:指向緩沖區(qū)的一個(gè)指針。setsockopt()從該緩沖區(qū)中取出值設(shè)置選擇項(xiàng),getsockopt()把某選擇項(xiàng)的值返回到該緩沖區(qū)中。
optlen:指緩沖區(qū)的長(zhǎng)度。對(duì)于setsockopt(),它指緩沖區(qū)的長(zhǎng)度;對(duì)于getsockopt(),由系統(tǒng)返回值填充,該值是指在optval緩沖區(qū)中存取的數(shù)據(jù)量。圖2-5套接字的選擇項(xiàng)
表2-4列出了getsockopt()與setsockopt()可以使用的所有選擇項(xiàng),這些選擇項(xiàng)大致可以分為兩類:一類為允許或禁止某種特性的選擇項(xiàng),稱為標(biāo)志項(xiàng);另一類為提取和返回允許設(shè)置和檢測(cè)的選擇項(xiàng)的值,稱為值項(xiàng)。標(biāo)志欄目表明是否是標(biāo)志項(xiàng)。如果不含有“.”,則表示選擇項(xiàng)用于用戶進(jìn)程和系統(tǒng)之間傳遞指定的參數(shù)。當(dāng)調(diào)用getsockopt()獲取標(biāo)志項(xiàng)時(shí),optval返回一個(gè)整數(shù),返回值為0表示該選擇項(xiàng)被禁止,非0表示允許使用該選擇項(xiàng)。當(dāng)調(diào)用setsockopt()時(shí),optval為0禁止該選擇項(xiàng),非0允許該選擇項(xiàng)。表2-4setsockopt和getsockopt的選項(xiàng)表
下面的程序可以加深我們對(duì)getsockopt()及setsockopt()的理解:
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netinet/tcp.h>
/*
設(shè)置套接字級(jí)的參數(shù)
*/ /*
設(shè)置TCP級(jí)的參數(shù),它只允許信道上使用
TCP/IP協(xié)議集合
*/ intmain() {intfd;
intmaxseg,buff,optlen;
/*
產(chǎn)生套接字
*/
if((fd=socket(AF_INET,SOCK_STREAM,0))<0)perror("Can'tcreatsocket."); /*
取
TCP最大段長(zhǎng)度
*/ optlen=sizeof(maxseg); if(getsockopt(fd,IPPROTO_TCP, TCP_MAXSEG,(char*)&maxseg,&optlen)<0) perror("getsockoptTCP_MAXSEGiserror"); printf("TCPmaxseg=%d\n",maxseg); /*
設(shè)置發(fā)送緩沖區(qū)長(zhǎng)度.*/buff=1024*16;if(setsockopt(fd,SOL_SOCKET,SO_SNDBUF,(char*)&buff,sizeof(buff))<0)perror("setsockoptSO_SNDBUFiserror.");optlen=sizeof(buff);if(getsockopt(fd,SOL_SOCKET,SO_SNDBUF,(char*)&buff,&optlen)<0)perror("getsockoptSOL_SNDBUFiserror.");printf("thesizeofsendbuffer=%d\n",buff);}
運(yùn)行結(jié)果為:
TCPmaxseg=512
thesizeofsendbuffer=16384
10.fcntl()
fcntl()和ioctl()是兩個(gè)能改變套接字屬性的系統(tǒng)調(diào)用。fcntl()影響由套接字描述符fd參數(shù)所引用的打開(kāi)文件,其調(diào)用格式為:
#include<fcntl.h>
#include<sys/types.h>
intfcntl(intfd,intcmd,intarg);
參數(shù)說(shuō)明:
fd:套接字文件描述符。 cmd:指定要執(zhí)行的操作。對(duì)套接字而言,它指F_GETOWN、F_SETOWN、F_GETFL及F_SETFL。其中,F(xiàn)_GETOWN:返回與套接字相關(guān)的進(jìn)程標(biāo)識(shí)(返值大于0)或進(jìn)程標(biāo)識(shí)(返回值小于0,但除-1以外)。前者指只要求一個(gè)進(jìn)程接收信號(hào),后者要求進(jìn)程組中每個(gè)進(jìn)程都接收信號(hào)。F_SETOWN:命令F_SETOWN用來(lái)設(shè)置進(jìn)程ID和進(jìn)程組ID,它們將接收與描述符fd相關(guān)的信號(hào)SIGIO或SIGURG。每個(gè)套接字均有一個(gè)相關(guān)的進(jìn)程組號(hào),套接字進(jìn)程組號(hào)初始化為0,可以通過(guò)F_SETOWN命令的系統(tǒng)調(diào)用fcntl設(shè)置。對(duì)于F_SETOWN命令,其arg值可正可負(fù),正是指進(jìn)程標(biāo)識(shí)數(shù),負(fù)是指定進(jìn)程組標(biāo)識(shí)。F_SETFL:該命令設(shè)置文件的標(biāo)志位。F_GETFL:該命令檢查文件的標(biāo)志位。
用F_SETFL和F_GETFL命令時(shí),下面給出兩種arg設(shè)置。
FNDELAY:指定套接字為非阻塞模型。對(duì)在非阻塞套接字上不能馬上執(zhí)行的I/O請(qǐng)求會(huì)立即返回到調(diào)用者,且將errno置為EWOULDBLOCK。該選擇項(xiàng)對(duì)以下的系統(tǒng)調(diào)用有影響:accept、connect、read、readv、recv、recvfrom、recvmsg、send、sendto、sendmsg、write及writev。
對(duì)于無(wú)連接套接字上的connect不會(huì)阻塞,因?yàn)榇藭r(shí)系統(tǒng)只記錄以后輸出時(shí)要用到的對(duì)方同等層地址。對(duì)于面向連接的套接字,connect可能需要一段時(shí)間,因?yàn)樗话阋c對(duì)方交換實(shí)際信息。這樣非阻塞套接字立即從connect系統(tǒng)調(diào)用返回,且errno被置為EINPROGRESS。send、sendto、sendmsg、write及writev在某些條件下可以在非阻塞套接字上進(jìn)行部分寫(xiě)入。
FASYNC:允許接收異步
I/O信號(hào),在數(shù)據(jù)準(zhǔn)備好可讀時(shí),向該套接字相應(yīng)的進(jìn)程組發(fā)SIGIO信號(hào)。 11.ioctl() ioctl()處理各種設(shè)備的選擇項(xiàng),主要用來(lái)控制I/O操作,其調(diào)用格式為: #include<sys/ioctl.h> intioctl(intfd,intrequest,...);
參數(shù)說(shuō)明:
fd:套接字文件描述符。
request:指定操作類型。一般request的設(shè)置可以為:
SIOCATMARK /*
帶int參數(shù),檢測(cè)是否達(dá)到帶外標(biāo)記
*/ FIOASYNC /*
帶int參數(shù),異步輸入輸出標(biāo)志
*/ FIONREAD /*
帶int參數(shù),緩沖區(qū)有多少數(shù)據(jù)可讀
*/
當(dāng)參數(shù)request的值為FIONREAD時(shí),第三個(gè)參數(shù)所指的整數(shù)值返回當(dāng)前套接字接收緩沖區(qū)中可讀數(shù)據(jù)的字節(jié)數(shù)。下面給出ioctl的適用類型。
ioctl的適用類型
常量名稱
要包含的頭文件 磁盤(pán)卷標(biāo)
DIOxxx <disklabel.h>
文件
I/O FIOxxx <ioctl.h>
磁帶
I/O MTIOxxx <mtio.h>
套接字
I/O SIOxxx <ioctl.h>
終端
I/O TIOxxx <termios.h>
表2-5指出了函數(shù)ioctl用于網(wǎng)絡(luò)的各選擇項(xiàng)以及參數(shù)必須指向的數(shù)據(jù)類型。表2-5ioctl用于網(wǎng)絡(luò)的控制選項(xiàng)
2.5多路復(fù)用
多路復(fù)用通過(guò)設(shè)置文件描述符集,將多個(gè)套接字組成一個(gè)集合,然后使用select()函數(shù)對(duì)集合進(jìn)行監(jiān)控,集合中任何一個(gè)(或幾個(gè))描述符就緒時(shí),進(jìn)程就可以作相應(yīng)的I/O處理。文件描述符集分為讀、寫(xiě)和異常三個(gè)類型,其中異常描述符集主要應(yīng)用于帶外數(shù)據(jù)的處理。select()和文件描述符操作函數(shù)的定義如下:
intselect(intmaxfd,structfd_set*rdset,structfd_set*wrset,structfd_set*exset,structtimeval*timeout); voidFD_SET(intfd,fd_set*fdset) /*
將fd加入到fdset*/ voidFD_CLR(intfd,fd_set*fdset) /*
將fd從fdset里面清除
*/ voidFD_ZERO(fd_set*fdset) /*
從fdset中清除所有的文件描述符
*/ intFD_ISSET(intfd,fd_set*fdset) /*
判斷fd是否在fdset集合中
*/ select()可以設(shè)置超時(shí),使長(zhǎng)期沒(méi)有文件描述符就緒時(shí),進(jìn)程可以跳出阻塞狀態(tài)。select()的第一個(gè)參數(shù)maxfd是集合中最大的文件描述符加1,例如,一個(gè)包含3個(gè)套接字描述符的集合{12,23,30},那么
maxfd就應(yīng)該是30+1=31。
在調(diào)用select()時(shí),進(jìn)程會(huì)一直阻塞到以下的一種情況發(fā)生: (1)有文件可以讀,包括出現(xiàn)錯(cuò)誤。
(2)有文件可以寫(xiě),包括出現(xiàn)錯(cuò)誤。
(3)超時(shí)所設(shè)置的時(shí)間到。
(4)被信號(hào)中斷。 下面例子中,客戶機(jī)與多個(gè)服務(wù)器建立連接,然后設(shè)置讀描述符集合,調(diào)用函數(shù)將進(jìn)程阻塞。當(dāng)任一個(gè)服務(wù)器的數(shù)據(jù)到達(dá)時(shí),進(jìn)程就被喚醒,檢查集合中哪個(gè)描述符就緒,然后作相應(yīng)處理。代碼片斷如下: intmain(intargc,char*argv[]) { intsockfd[NUMBER]; /*NUMBER為需要建立的套接字?jǐn)?shù)量
*/ structsockaddr_inservaddr[NUMBER]; fd_setrfds; charbuf[1024]; inti;
for(i=0;i<NUMBER;i++) { sockfd[i]=socket(AF_INET,SOCK_STREAM,0);} if(sockfd[i]<0) exit(1); } ... /*
填充NUMBER個(gè)地址結(jié)構(gòu)
*/ ... /*
建立NUMBER個(gè)連接
*/ intnOK[NUMBER]; for(i=0;i<NUMBER;i++){nOK[i]=0;} intnEnd=NUMBER; while(nEnd!=0) { for(i=0;i<NUMBER;i++) if(nOK[i]==0) /*
只檢查未被處理的套接字
*/FD_SET(sockfd[i],&rds); n=select(theMax(NUMBER,sockfd)+1,&rds,NULL,NULL,NULL); if(n<0&&errno==EINTR) continue; for(i=0;i<NUMBER;i++){nOK[i]=0;} {if(FD_ISSET(sockfd[i],&rds)){n=read(sockfd1,buf,1024);if(n<=0&&errno!=EINTR) { perror("AnError."); /*
發(fā)生錯(cuò)誤,這個(gè)套接字不再工作
*/ nOK[i]=1;nEnd--; } elseif(n>0) { process(buf,...);
/*
數(shù)據(jù)處理
*/ nOK[i]=1;nEnd--; }}}}for(i=0;i<NUMBER;i++){close(sockfd[i]);}}2.6網(wǎng)絡(luò)字節(jié)傳輸順序及主機(jī)字節(jié)順序 2.6.1網(wǎng)絡(luò)字節(jié)順序與主機(jī)字節(jié)順序 每一臺(tái)機(jī)器內(nèi)部對(duì)變量的字節(jié)存儲(chǔ)順序不同,而網(wǎng)絡(luò)傳輸?shù)臄?shù)據(jù)是一定要統(tǒng)一順序的。所以對(duì)內(nèi)部字節(jié)表示順序與網(wǎng)絡(luò)字節(jié)順序不同的機(jī)器,一定要對(duì)數(shù)據(jù)進(jìn)行轉(zhuǎn)換,從程序的可移植性要求來(lái)講,就算本機(jī)的內(nèi)部字節(jié)表示順序與網(wǎng)絡(luò)字節(jié)順序相同,也應(yīng)該在傳輸數(shù)據(jù)以前先調(diào)用數(shù)據(jù)轉(zhuǎn)換函數(shù),以便程序移植到其他機(jī)器后能正確執(zhí)行。真正轉(zhuǎn)換還是不轉(zhuǎn)換是由系統(tǒng)函數(shù)自己來(lái)決定的。
對(duì)于不同的主機(jī):
Intel芯片:低字節(jié)在前,高字節(jié)在后,稱little-endian;
RISC芯片:高字節(jié)在前,低字節(jié)在后,稱big-endian,Internet采用此模式; 因此,不同機(jī)器表示數(shù)據(jù)的字節(jié)順序是不同的。有關(guān)的轉(zhuǎn)換函數(shù)為:
(1)?unsignedshortinthtons(unsignedshortinthostshort)。htons()表示“HosttoNetworkShort”,將主機(jī)字節(jié)順序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)順序,返回?zé)o符號(hào)短型處理結(jié)果(4B)。
(2)?unsignedlonginthtonl(unsignedlonginthostlong)。htonl()表示“HosttoNetworkLong”,將主機(jī)字節(jié)順序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)順序,返回?zé)o符號(hào)長(zhǎng)型處理結(jié)果(8B)。 (3)?unsignedshortintntohs(unsigned
short
int
netshort)。ntohs()表示“NetworktoHostShort”,將網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換成主機(jī)字節(jié)順序,返回?zé)o符號(hào)
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 勤雜工合同范例
- 合伙種葡萄合同范本
- 合伙開(kāi)店股合同范例
- 醫(yī)療勞動(dòng)合同范本
- 合同范本 模板
- 合伙經(jīng)營(yíng)酒吧合同范本
- 鄉(xiāng)鎮(zhèn)山林承租合同范本
- 半價(jià)打包餐飲服務(wù)合同范本
- ppp項(xiàng)目政府合同范本
- 雙方合作開(kāi)發(fā)合同范例
- 建(構(gòu))筑物消防員初級(jí)技能培訓(xùn)課件
- 2025-2030年中國(guó)天線行業(yè)市場(chǎng)需求狀況規(guī)劃研究報(bào)告
- 2024年南京旅游職業(yè)學(xué)院高職單招職業(yè)技能測(cè)驗(yàn)歷年參考題庫(kù)(頻考版)含答案解析
- 如何提升自我管理能力
- 2025年潛江市城市建設(shè)發(fā)展集團(tuán)招聘工作人員【52人】高頻重點(diǎn)提升(共500題)附帶答案詳解
- 人教版(新)九年級(jí)下冊(cè)化學(xué)全冊(cè)教案教學(xué)設(shè)計(jì)及教學(xué)反思
- 2024年05月富德生命人壽保險(xiǎn)股份有限公司招考筆試歷年參考題庫(kù)附帶答案詳解
- 部隊(duì)安全手機(jī)保密課件
- 光伏電站安全培訓(xùn)課件
- 小學(xué)生勤儉節(jié)約課件
- 2025年上半年重慶市渝北區(qū)大灣鎮(zhèn)招錄村綜合服務(wù)專干13人重點(diǎn)基礎(chǔ)提升(共500題)附帶答案詳解
評(píng)論
0/150
提交評(píng)論