版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、第八章 Socket程序設(shè)計(jì),1 基本概念,網(wǎng)間進(jìn)程通信 網(wǎng)間進(jìn)程通信要解決的是不同主機(jī)進(jìn)程間的相互通信問(wèn)題(可把同機(jī)進(jìn)程通信看作是其中的特例)。為此,首先要解決的是網(wǎng)間進(jìn)程標(biāo)識(shí)問(wèn)題。同一主機(jī)上,不同進(jìn)程可用進(jìn)程號(hào)(process ID)唯一標(biāo)識(shí)。但在網(wǎng)絡(luò)環(huán)境下,各主機(jī)獨(dú)立分配的進(jìn)程號(hào)不能唯一標(biāo)識(shí)該進(jìn)程。 其次,操作系統(tǒng)支持的網(wǎng)絡(luò)協(xié)議眾多,不同協(xié)議的工作方式不同,地址格式也不同。因此,網(wǎng)間進(jìn)程通信還要解決多重協(xié)議的識(shí)別問(wèn)題。,1 基本概念,端口端口是一種抽象的軟件結(jié)構(gòu)(包括一些數(shù)據(jù)結(jié)構(gòu)和I/O緩沖區(qū))。應(yīng)用程序(即進(jìn)程)通過(guò)系統(tǒng)調(diào)用與某端口建立連接(binding)后,傳輸層傳給該端口的數(shù)據(jù)都
2、被相應(yīng)進(jìn)程所接收,相應(yīng)進(jìn)程發(fā)給傳輸層的數(shù)據(jù)都通過(guò)該端口輸出。在TCP/IP協(xié)議的實(shí)現(xiàn)中,端口操作類似于一般的I/O操作,進(jìn)程獲取一個(gè)端口,相當(dāng)于獲取本地唯一的I/O文件,可以用一般的讀寫原語(yǔ)訪問(wèn)。,1 基本概念,端口 端口號(hào)的分配有兩種基本分配方式:全局分配;本地分配 TCP/IP端口號(hào)的分配中綜合了上述兩種方式。TCP/IP將端口號(hào)分為兩部分,少量的作為保留端口,以全局方式分配給服務(wù)進(jìn)程。因此,每一個(gè)標(biāo)準(zhǔn)服務(wù)器都擁有一個(gè)全局公認(rèn)的端口(即周知口,well-known port),即使在不同機(jī)器上,其端口號(hào)也相同。剩余的為自由端口,以本地方式進(jìn)行分配。,1 基本概念,地址 網(wǎng)絡(luò)通信中通信的兩個(gè)
3、進(jìn)程分別在不同的機(jī)器上。在互連網(wǎng)絡(luò)中,兩臺(tái)機(jī)器可能位于不同的網(wǎng)絡(luò),這些網(wǎng)絡(luò)通過(guò)網(wǎng)絡(luò)互連設(shè)備(網(wǎng)關(guān),網(wǎng)橋,路由器等)連接。因此需要三級(jí)尋址:1. 某一主機(jī)可與多個(gè)網(wǎng)絡(luò)相連,必須指定一特定網(wǎng)絡(luò)地址;2. 網(wǎng)絡(luò)上每一臺(tái)主機(jī)應(yīng)有其唯一的地址;3. 每一主機(jī)上的每一進(jìn)程應(yīng)有在該主機(jī)上的唯一標(biāo)識(shí)符。 通常主機(jī)地址由網(wǎng)絡(luò)ID和主機(jī)ID組成,在TCP/IP協(xié)議中用32位整數(shù)值表示;TCP和UDP均使用16位端口號(hào)標(biāo)識(shí)用戶進(jìn)程。,基本概念,網(wǎng)絡(luò)字節(jié)順序 不同的計(jì)算機(jī)存放多字節(jié)值的順序不同,因此為保證數(shù)據(jù)的正確性,在網(wǎng)絡(luò)協(xié)議中須指定網(wǎng)絡(luò)字節(jié)順序。TCP/IP協(xié)議使用16位整數(shù)和32位整數(shù)的高價(jià)先存格式。 連接 兩
4、個(gè)進(jìn)程間的通信鏈路稱為連接。連接在內(nèi)部表現(xiàn)為一些緩沖區(qū)和一組協(xié)議機(jī)制,在外部表現(xiàn)出比無(wú)連接高的可靠性。,基本概念,半相關(guān) 網(wǎng)絡(luò)中用一個(gè)三元組可以在全局唯一標(biāo)志一個(gè)進(jìn)程: (協(xié)議,本地地址,本地端口號(hào)) 這樣一個(gè)三元組,叫做一個(gè)半相關(guān)(half-association),它指定連接的每半部分。 全相關(guān) 一個(gè)完整的網(wǎng)間進(jìn)程通信需要由兩個(gè)進(jìn)程組成,并且只能使用同一種高層協(xié)議。因此一個(gè)完整的網(wǎng)間通信需要一個(gè)五元組來(lái)標(biāo)識(shí): (協(xié)議,本地地址,本地端口號(hào),遠(yuǎn)地地址,遠(yuǎn)地端口號(hào)) 這樣一個(gè)五元組,叫做一個(gè)相關(guān)(association),即兩個(gè)協(xié)議相同的半相關(guān)才能組合成一個(gè)合適的相關(guān),或完全指定組成一連接。,
5、Socket 概述,網(wǎng)絡(luò)協(xié)議規(guī)定了兩臺(tái)計(jì)算機(jī)之間進(jìn)行數(shù)據(jù)交換的共同規(guī)則,包括交換數(shù)據(jù)的格式和動(dòng)作序列。 UNIX中傳輸層和傳輸層以下的協(xié)議在操作系統(tǒng)內(nèi)核中實(shí)現(xiàn),那么就必須規(guī)定一種應(yīng)用程序使用內(nèi)核的這些網(wǎng)絡(luò)功能的方法。 UNIX訪問(wèn)網(wǎng)絡(luò)也用文件描述符引用一個(gè)特殊文件的方法。 目前,應(yīng)用程序與網(wǎng)絡(luò)之間接口有socket和TLI(Tansport Layer Interface),8,TCP和UDP,TCP/IP對(duì)應(yīng)用程序提供的服務(wù)主要有兩種:一種是面向連接的可靠的數(shù)據(jù)流傳輸TCP,另一種是無(wú)連接不可靠數(shù)據(jù)報(bào)傳輸U(kuò)DP。 應(yīng)用程序員在使用TCP/IP網(wǎng)絡(luò)編寫通信程序之前,應(yīng)當(dāng)首先在TCP和UDP協(xié)議
6、之間作出選擇,它們決定了由系統(tǒng)提供的通信可靠性。,9,TCP和UDP,TCP提供了完全可靠的通信服務(wù),它能夠自動(dòng)地重傳;計(jì)算校驗(yàn)和以保證數(shù)據(jù)的正確性;TCP協(xié)議保證數(shù)據(jù)在接收端按在發(fā)送端發(fā)送的次序接收,不會(huì)出現(xiàn)后發(fā)送的數(shù)據(jù)先到達(dá)的情況;能自動(dòng)地扔掉那些重復(fù)傳輸?shù)臄?shù)據(jù);它提供了流量控制機(jī)制,保證發(fā)送者發(fā)送的數(shù)據(jù)不要太快以至于接收者來(lái)不及處理它們,甚至還考慮了不以過(guò)快的速度發(fā)送數(shù)據(jù)以防整個(gè)網(wǎng)絡(luò)擁堵。當(dāng)由于某種原因TCP通信無(wú)法進(jìn)行時(shí),會(huì)通過(guò)一定的手段通知應(yīng)用程序。 使用UDP,所有上述TCP的可靠機(jī)制都沒(méi)有。由應(yīng)用程序想辦法解決可靠性問(wèn)題,這些問(wèn)題包括:錯(cuò)報(bào)、重報(bào)、丟報(bào)、亂序和流量控制。,10,T
7、CP和UDP,選擇UDP協(xié)議的程序,應(yīng)當(dāng)進(jìn)行周密的測(cè)試。否則,一個(gè)在時(shí)延短誤碼率低的本地局域網(wǎng)上調(diào)試好的程序在時(shí)延長(zhǎng)差錯(cuò)率高,或者時(shí)延和誤碼率經(jīng)常變化的廣域網(wǎng)上可能會(huì)出現(xiàn)問(wèn)題?;蛘?,在數(shù)據(jù)量較小時(shí),運(yùn)行正常,但數(shù)據(jù)量增大時(shí)運(yùn)行不穩(wěn)定。 選擇UDP協(xié)議通信的程序,必須利用一些技術(shù),以保證數(shù)據(jù)的可靠傳輸,或者是高層的軟件在UDP不可靠的服務(wù)面前也能夠正常工作。相對(duì)來(lái)說(shuō),要比直接使用TCP難度更大。使用UDP協(xié)議的典型應(yīng)用有域名服務(wù)DNS和簡(jiǎn)單文件傳送協(xié)議TFTP,簡(jiǎn)單網(wǎng)管協(xié)議SNMP,以及路由信息協(xié)議RIP。需要使用廣播(broadcast)或者組播(multicast)功能時(shí),只能選擇UDP。,
8、11,TCP通信,socket給出的編程接口,無(wú)論使用TCP還是UDP協(xié)議,都是一種client/server風(fēng)格的軟件結(jié)構(gòu)。 事實(shí)上,UNIX設(shè)計(jì)的socket機(jī)制,不僅僅是面向TCP/IP通信協(xié)議的,而是面向所有的網(wǎng)絡(luò)通信,包括其他的通信協(xié)議棧。 client/server結(jié)構(gòu)的協(xié)議軟件包括客戶端軟件和服務(wù)器端軟件。,12,TCP通信,文件傳送協(xié)議FTP為例,UNIX提供的ftp命令就是客戶端軟件,在提供文件傳送服務(wù)的遠(yuǎn)程計(jì)算機(jī)上,運(yùn)行服務(wù)器端軟件。 這些服務(wù)器端的軟件,在UNIX中由守護(hù)進(jìn)程inetd控制,當(dāng)TCP連接到達(dá)時(shí),inetd創(chuàng)建ftpd服務(wù)進(jìn)程負(fù)責(zé)與客戶端軟件的ftp通信,以
9、完成文件傳送操作,文件傳送結(jié)束后,ftpd進(jìn)程結(jié)束。,13,TCP通信,為了進(jìn)行類比,先看一下文件操作的模式。訪問(wèn)文件有一組函數(shù),而且這些函數(shù)調(diào)用的先后順序也有一定的規(guī)則,先open()得到文件描述符fd,然后可以執(zhí)行read()和write()訪問(wèn)文件內(nèi)容,另有一些可以施加在fd上的函數(shù),例如:lseek()定位文件指針,fstat()獲得文件的狀態(tài)等等。 使用管道時(shí),就不再用open()獲得文件描述符,而是用pipe()一次獲得兩個(gè)文件描述符。,14,TCP通信,使用socket的情況類似,先用socket()創(chuàng)建一個(gè)文件描述符,在這個(gè)文件描述符上,先施行一些connect(),bind(
10、),listen()等操作控制建立TCP連接,然后才能使用read()和write()收發(fā)數(shù)據(jù)。 通信過(guò)程中,可以使用fcntl(),setsockopt()和getpeername()等函數(shù),獲得一些通信的狀態(tài),或者設(shè)置一些與通信有關(guān)的參數(shù)。 最終,用close()關(guān)閉連接。這些函數(shù)調(diào)用的先后順序,也遵循一定的規(guī)則。,15,TCP通信,TCP客戶端程序 客戶端程序首先和遠(yuǎn)程的服務(wù)器端程序建立連接,然后,從標(biāo)準(zhǔn)輸入讀取一個(gè)字符串,將該字符串發(fā)送到服務(wù)器。 client.c基本上分幾部分:創(chuàng)建socket文件描述符,建立連接,發(fā)送數(shù)據(jù),最后關(guān)閉文件描述符,也就關(guān)閉了網(wǎng)絡(luò)連接。 connect調(diào)用
11、,建立一個(gè)TCP連接。執(zhí)行connect調(diào)用,內(nèi)核的TCP協(xié)議軟件開(kāi)始3次握手,并等待對(duì)方計(jì)算機(jī)的應(yīng)答。,16,TCP通信,TCP服務(wù)器程序 服務(wù)器端程序也一樣需要用socket()調(diào)用創(chuàng)建一個(gè)文件描述符,然后,執(zhí)行bind()調(diào)用。 執(zhí)行了socket()調(diào)用之后,內(nèi)核中為這一文件描述符記錄的本地端點(diǎn)名和遠(yuǎn)地端點(diǎn)名都為空,IP地址和端口號(hào)都是0。服務(wù)器端的程序,必須把本地端點(diǎn)設(shè)置為一個(gè)約定好的TCP端口。 系統(tǒng)調(diào)用bind()完成這項(xiàng)任務(wù),bind調(diào)用的目的是指定本地的一個(gè)“端點(diǎn)名”。這里的程序本地端點(diǎn)名中的端口號(hào),使用常數(shù)PORT_NO,需要事先轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)順序。,17,TCP通信,TC
12、P服務(wù)器程序 listen()調(diào)用是通知內(nèi)核,以后凡是到達(dá)本地主機(jī)的TCP連接,如果端點(diǎn)名和這個(gè)文件描述符的本地端點(diǎn)名相同,就轉(zhuǎn)交到這個(gè)文件描述符上。 一般面向連接的通信的服務(wù)器端程序需要listen(),而且listen()調(diào)用安排在程序的bind()調(diào)用之后。 accept()在文件描述符admin_sock上等待一個(gè)遠(yuǎn)程主機(jī)發(fā)送來(lái)的連接請(qǐng)求,,18,TCP通信,socket()系統(tǒng)調(diào)用創(chuàng)建一個(gè)socket,19,#include #include int socket (int domain, int type, int protocol); 成功返回socket文件描述符; 出錯(cuò)返回-
13、1,TCP通信,socket()系統(tǒng)調(diào)用創(chuàng)建一個(gè)socket,20,TCP通信,socket()系統(tǒng)調(diào)用創(chuàng)建一個(gè)socket protocol參數(shù)通常情況下為0,用來(lái)表明默認(rèn)的domain和socket類型。 SOCK_STREAM這種socket類型的話,默認(rèn)為AF_INET的IPPROTO_TCP ; SOCK_DGRAM這種socket類型的話,默認(rèn)為AF_INET的IPPROTO_UDP ;,21,TCP通信,connet()系統(tǒng)調(diào)用建立連接,22,#include #include int connect (int sockfd, const struct sockaddr *add
14、r, socklen_t len); 成功返回0; 出錯(cuò)返回-1,connect()的第一個(gè)參數(shù)決定了協(xié)議類型,有了“端點(diǎn)名”,connect()才知道連接的對(duì)方是誰(shuí)。為了描述一個(gè)連接的“端點(diǎn)名”,不同協(xié)議棧需要不同的描述方法。,5 命名套接字,示例:,#include void make_socket( unsigned short int port ) int i,sock; struct sockaddr_in address; sock = socket( AF_INET, SOCK_STREAM, 0); address.sin_family = AF_INET; address.s
15、in_port = htons( port ); address.sin_addr.s_addr = htonl( INADDR_ANY ); i = bind( sock, (struct sockaddr*) ,TCP通信,connet()系統(tǒng)調(diào)用建立連接,24,struct sockaddr short sin_family; /* AF_INET */ u_short sin_port; /* port number */ struct in_addr sin_addr; /* IP address */ char sin_zero8; /* unused */ ; struct in
16、_addr u_long s_addr; ;,sin_family必須為AF_INET,sin_port是TCP端口號(hào),sin_addr是IP地址。這兩個(gè)參數(shù)都需要按網(wǎng)絡(luò)字節(jié)順序存放,所以分別使用了htonl和htons。,TCP通信,connet()系統(tǒng)調(diào)用建立連接,25,對(duì)于TCP來(lái)說(shuō),指定了通信的對(duì)端,在本地和遠(yuǎn)地主機(jī)之間建立一個(gè)連接。所連接對(duì)端的端點(diǎn)名放在name緩沖區(qū)中,namelen為存放端點(diǎn)名的緩沖區(qū)的有效數(shù)據(jù)長(zhǎng)度。這個(gè)系統(tǒng)調(diào)用可能會(huì)導(dǎo)致進(jìn)程睡眠等待連接建立成功。 對(duì)于UDP來(lái)說(shuō),connect并不和遠(yuǎn)程主機(jī)之間建立任何連接,而是僅僅將通信對(duì)端的“端點(diǎn)名”記錄到內(nèi)核的sockfd
17、對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)中。當(dāng)然,這也不會(huì)導(dǎo)致進(jìn)程睡眠。隨后的write(sockfd,.)將使用這個(gè)sockfd的對(duì)端的端點(diǎn)名記錄的IP地址和端口號(hào),作為每個(gè)UDP報(bào)的目的地址。,TCP通信,bind()系統(tǒng)調(diào)用指定本地端口,26,#include #include int bind (int sockfd, const struct sockaddr *addr, socklen_t len); 成功返回0; 出錯(cuò)返回-1,bind用于指定通信的本地“端點(diǎn)名”。在服務(wù)器端的程序中,這個(gè)調(diào)用是必需的,指定一個(gè)約定的端口號(hào)作為本地的端口號(hào)。隨后的listen才使得內(nèi)核的TCP協(xié)議模塊了解哪些到達(dá)的連接該
18、轉(zhuǎn)交到這個(gè)sockfd。,TCP通信,bind()系統(tǒng)調(diào)用指定本地端口 bind()調(diào)用的目的是為文件描述符選定一個(gè)本地端點(diǎn)名,這個(gè)選定的端點(diǎn)名被內(nèi)核記錄下來(lái)。 bind()函數(shù)并不是服務(wù)器端程序?qū)S玫摹T诳蛻舳说某绦蛑?,包括TCP和 UDP,可以不指定本地端點(diǎn)名,執(zhí)行connect調(diào)用,或者對(duì)UDP來(lái)說(shuō)發(fā)送數(shù)據(jù)時(shí),如果本地端點(diǎn)名還是空,那么系統(tǒng)會(huì)自動(dòng)為本地端點(diǎn)分配一個(gè)端點(diǎn)名,包括IP地址和端點(diǎn)號(hào)。 客戶端程序在connect()前也可以用bind()強(qiáng)制選擇本地端點(diǎn)名,即本地IP地址和端口號(hào)。本地主機(jī)有多個(gè)IP地址時(shí),bind()可以為本地端點(diǎn)強(qiáng)制選定其中的一個(gè)地址,或者強(qiáng)制選定一個(gè)本地端口
19、號(hào)。,27,5 命名套接字,示例:,#include void make_socket( unsigned short int port ) int i,sock; struct sockaddr_in address; sock = socket( AF_INET, SOCK_STREAM, 0); address.sin_family = AF_INET; address.sin_port = htons( port ); address.sin_addr.s_addr = htonl( INADDR_ANY ); i = bind( sock, (struct sockaddr*) ,T
20、CP通信,listen()監(jiān)聽(tīng)到達(dá)的連接請(qǐng)求,29,#include #include int listen (int sockfd, int back_log); 成功返回0; 出錯(cuò)返回-1,TCP通信,listen()監(jiān)聽(tīng)到達(dá)的連接請(qǐng)求 一個(gè)已經(jīng)執(zhí)行過(guò)bind() 指定了本地“端點(diǎn)名”的文件描述符,執(zhí)行了listen()后才開(kāi)始監(jiān)聽(tīng)有沒(méi)有連接請(qǐng)求到達(dá)。 listen()只用在面向連接的網(wǎng)絡(luò)協(xié)議對(duì)應(yīng)的文件描述符。backlog指明最多可以排隊(duì)多少個(gè)連接請(qǐng)求。 每次accept()就從這個(gè)隊(duì)列里取走一個(gè)連接請(qǐng)求,創(chuàng)建新的文件描述符。 在accept()之后,下次accept()之前的時(shí)間段到達(dá)
21、的連接請(qǐng)求就排隊(duì)等候,backlog指定允許的最大排隊(duì)數(shù)。此參數(shù)常指定為5,是目前允許的最大值。,30,TCP通信,accept()系統(tǒng)調(diào)用指定本地端口,31,#include #include int accpet (int sockfd, const struct sockaddr *addr, socklen_t len); 成功返回文件描述符; 出錯(cuò)返回-1,在一個(gè)面向連接的服務(wù)器中,先用socket創(chuàng)建一個(gè)套接字,再用bind指定一個(gè)本地“端點(diǎn)名”,然后執(zhí)行l(wèi)isten后就開(kāi)始監(jiān)聽(tīng)。,TCP通信,accept()系統(tǒng)調(diào)用指定本地端口 accept()就是取得排隊(duì)在套接字上的隊(duì)列的第一
22、個(gè)連接請(qǐng)求,并建立起一個(gè)新的文件描述符,函數(shù)返回值就是這個(gè)文件描述符。 如果addr和len不為0,對(duì)端的端點(diǎn)名返回在addr中。len用于傳入存放端點(diǎn)名的緩沖區(qū)addr的大小,函數(shù)調(diào)用返回時(shí),len存放已記錄到addr中的遠(yuǎn)端主機(jī)的端點(diǎn)名的有效長(zhǎng)度。前面的程序例子中給出了這后兩個(gè)參數(shù)的用法。 如果accept()執(zhí)行時(shí),sockfd上尚沒(méi)有連接請(qǐng)求到達(dá),那么accept()調(diào)用被阻塞,進(jìn)程處于睡眠狀態(tài),等待連接請(qǐng)求的到達(dá)。這一系統(tǒng)調(diào)用可以被信號(hào)打斷。,32,3 套接字地址,(a) 主機(jī)的IP地址,/ IP地址的內(nèi)部表示: #include struct in_addr in_addr_t s
23、_addr; ,32位無(wú)符號(hào)整數(shù),3 套接字地址,(a) 主機(jī)的IP地址,字符串IP和32位IP地址轉(zhuǎn)換函數(shù),int inet_aton (char *name, struct in_addr *addr);,char* inet_ntoa (struct in_addr in);,函數(shù)里面 a 代表 ascii n 代表network.第一個(gè)函數(shù)表示將a.b.c.d的IP轉(zhuǎn)換為32位的IP,存儲(chǔ)在 inp指針里面.第二個(gè)是將32位IP轉(zhuǎn)換為a.b.c.d的格式.,3 套接字地址,(b) 主機(jī)名,為了方便記憶,計(jì)算機(jī)都有一個(gè)主機(jī)名,這樣就不用記復(fù)雜的數(shù)點(diǎn)形式的IP地址,在目前所有的操作系統(tǒng)中都
24、有一個(gè)hosts文件,用于記錄主機(jī)名和數(shù)點(diǎn)形式的IP地址的映射。,3 套接字地址,UNIX系統(tǒng)為處理主機(jī)信息,定義了 hostent 結(jié)構(gòu)描述主機(jī)名相關(guān)信息,include struct hostent char * h_name; /* 主機(jī)的正式名稱*/ char * h_aliases; /* 主機(jī)的別名 */ int h_addrtype; /* 主機(jī)的地址類型AF_INET*/ int h_length; /* 主機(jī)的地址長(zhǎng)度對(duì)于IP4 是4字節(jié)32位*/ char * h_addr_list; /* 主機(jī)的IP地址列表 */ #defineh_addrh_addr_list0 /*
25、 主機(jī)的第一個(gè)IP地址*/,3 套接字地址,UNIX系統(tǒng)為獲得主機(jī)信息的相關(guān)函數(shù),#include struct hostent* gethostbyname( char *name); struct hostent* gethostbyaddr( void *addr, size_t length, int type );,gethostbyname可以將機(jī)器名(如 )轉(zhuǎn)換為一個(gè)結(jié)構(gòu)指針.在這個(gè)結(jié)構(gòu)里面儲(chǔ)存了域名的信息 gethostbyaddr可以將一個(gè)32位的IP地址(C0A80001)轉(zhuǎn)換為結(jié)構(gòu)指針. 這兩個(gè)函數(shù)失敗時(shí)返回NULL.,3 套接字地址,2.服務(wù)與端口,套接字地址 機(jī)器的I
26、P地址 端口號(hào),標(biāo)識(shí)一臺(tái)計(jì)算機(jī),區(qū)別同一臺(tái)計(jì)算機(jī)上的不同服務(wù)程序,3 套接字地址,2 服務(wù)與端口,UNIX系統(tǒng)中的端口分配,0, 不使用,11023, 知名端口(保留端口),限定為標(biāo)準(zhǔn)服務(wù)使用,如ftp 的21端口,http的80端口等,10245000,可以被任意的客戶端程序使用,500165535,為其他服務(wù)程序預(yù)留,3 套接字地址,UNIX系統(tǒng)為處理服務(wù)信息,定義了 servent 結(jié)構(gòu)描述服務(wù)相關(guān)信息,include struct servent char * s_name; /服務(wù)的正式名稱 char* s_aliases; /服務(wù)的可選別名 int s_port; /服務(wù)使用的端口
27、號(hào) char * s_proto; /與該服務(wù)一起使用的協(xié)議名 ,3 套接字地址,UNIX系統(tǒng)為獲得服務(wù)信息的相關(guān)函數(shù),#include struct servent* getservbyname(char *name,char* proto); Struct servent* getservbyport(int port, char* proto);,3 套接字地址,不同的計(jì)算機(jī)系統(tǒng)采用不同的字節(jié)序存儲(chǔ)數(shù)據(jù),Little-Endian字節(jié)序:將低字節(jié)存儲(chǔ)在起始地址,由Intel體系采用,Big-Endian字節(jié)序:將高字節(jié)存儲(chǔ)在起始地址,由Macintosh體系采用,3 套接字地址,主機(jī)字節(jié)
28、序和網(wǎng)絡(luò)字節(jié)序,主機(jī)字節(jié)序:將某給定系統(tǒng)所用的字節(jié)序稱為主機(jī)字節(jié)序,網(wǎng)絡(luò)字節(jié)序:網(wǎng)絡(luò)上傳輸?shù)臄?shù)據(jù)使用規(guī)定的字節(jié)序,即網(wǎng)絡(luò)字節(jié)序,采用big-endian,網(wǎng)絡(luò)字節(jié)次序,不同的計(jì)算機(jī)廠商在計(jì)算機(jī)內(nèi)部存儲(chǔ)整數(shù)的方法會(huì)有些不同。 例如:一個(gè)4B的整數(shù),占用內(nèi)存的連續(xù)的4個(gè)存儲(chǔ)單元,有的廠商將這個(gè)整數(shù)的低位字節(jié)放在最低地址處,x86系列CPU就是這種安排,這種安排叫Little Endian;而有的廠商正好相反,高位字節(jié)放在4B內(nèi)存的低地址處,如PowerPC和Sparc,它們的字節(jié)順序安排叫Big Endian。 網(wǎng)絡(luò)通信時(shí),總是從內(nèi)存的低地址開(kāi)始傳輸連續(xù)的若干字節(jié),因此,網(wǎng)絡(luò)軟件為了保證各計(jì)算機(jī)之
29、間的互聯(lián)性,要求所有的數(shù)據(jù)按統(tǒng)一的字節(jié)順序傳輸,這就是規(guī)定的網(wǎng)絡(luò)字節(jié)順序。網(wǎng)絡(luò)字節(jié)順序的規(guī)定與x86的主機(jī)字節(jié)順序相反。,44,網(wǎng)絡(luò)字節(jié)次序,在UNIX中提供了htons,htonl兩個(gè)庫(kù)函數(shù),分別將短整數(shù)和長(zhǎng)整數(shù)從主機(jī)字節(jié)次序轉(zhuǎn)換到網(wǎng)絡(luò)字節(jié)次序。 相應(yīng)地,ntohs()和ntohl()把網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換到主機(jī)字節(jié)順序。在socket的網(wǎng)絡(luò)系統(tǒng)調(diào)用和庫(kù)函數(shù)的結(jié)構(gòu)體參數(shù)中的整數(shù),一般也要求按網(wǎng)絡(luò)字節(jié)順序排列。 為了源程序的可移植性,即使所用的UNIX中主機(jī)字節(jié)順序與網(wǎng)絡(luò)字節(jié)順序正好吻合,也不要省略掉所必須的htons()、htonl()、ntohs()和ntohl()。,45,read/writ
30、e與TCP通信的時(shí)序,當(dāng)調(diào)用read時(shí),如果TCP連接上事先已經(jīng)有到達(dá)了的數(shù)據(jù),提前到達(dá)的數(shù)據(jù)會(huì)被放在內(nèi)核里這個(gè)TCP連接對(duì)應(yīng)的接收緩沖區(qū)內(nèi),那么read立即返回,返回值為實(shí)際的字節(jié)數(shù);否則,如果TCP連接上沒(méi)有數(shù)據(jù)到達(dá),那么read就被阻塞,進(jìn)程處于睡眠狀態(tài),直到收到數(shù)據(jù)為止。 當(dāng)調(diào)用write時(shí),如果TCP連接上發(fā)送忙,那么write就被阻塞,進(jìn)程處于睡眠狀態(tài),直到能夠把數(shù)據(jù)傳給發(fā)送緩沖區(qū)為止。write調(diào)用沒(méi)有返回1,系統(tǒng)調(diào)用正確返回后,并不能保證數(shù)據(jù)已經(jīng)正確地發(fā)送到了對(duì)方。 下面以一個(gè)具體的TCP通信實(shí)例描述write和read在什么時(shí)刻返回。,46,read/write與TCP通信的
31、時(shí)序,主機(jī)A通過(guò)TCP連接向主機(jī)B發(fā)送數(shù)據(jù),用write();主機(jī)B接收數(shù)據(jù)用read()。最左側(cè)為時(shí)間編號(hào)。 t0: 主機(jī)B開(kāi)始read(),read處于阻塞狀態(tài)。 t1: 主機(jī)A調(diào)用write(),主機(jī)A發(fā)送緩沖區(qū)有空閑,將數(shù)據(jù)復(fù)制至發(fā)送緩沖區(qū)。 t2: 主機(jī)A將數(shù)據(jù)發(fā)往主機(jī)B。 t3: 主機(jī)B收到數(shù)據(jù)后,驗(yàn)證校驗(yàn)和正確,則進(jìn)程被叫醒,read()返回讀到的數(shù)據(jù)。 t4: 主機(jī)B向主機(jī)A發(fā)ACK,ACK途中丟失。 t5: 主機(jī)A超時(shí)自動(dòng)重發(fā)數(shù)據(jù)。 t6: 主機(jī)B收到重復(fù)的數(shù)據(jù)后扔掉,回送ACK。 t7: 主機(jī)A收到ACK,將發(fā)送緩沖區(qū)的數(shù)據(jù)清除。,47,read/write與TCP通信的時(shí)
32、序,對(duì)于read()調(diào)用來(lái)說(shuō),在調(diào)用參數(shù)中指明了期望讀到的字節(jié)數(shù)。那么,當(dāng)系統(tǒng)收不到數(shù)據(jù)時(shí),read()會(huì)等待,而不是立刻返回0,只要收到了數(shù)據(jù),read()就立即返回,返回值為實(shí)際收到的字節(jié)數(shù)。 即使實(shí)際收到的字節(jié)數(shù)遠(yuǎn)遠(yuǎn)小于它所期望讀到的字節(jié)數(shù)nbyte,read()也不再等下去。 系統(tǒng)實(shí)際已收到的字節(jié)數(shù)大于nbyte時(shí),read()讀取nbyte字節(jié),剩下的數(shù)據(jù),下次read()時(shí)讀取。如果等待過(guò)程中發(fā)生了通信意外,那么read()會(huì)以1返回。 如果在read/write之前,sockfd上就已經(jīng)出現(xiàn)了錯(cuò)誤,例如,通信中斷,那么,read()和write()都會(huì)馬上以1立刻返回。,48,6
33、 套接字通信模式,流套接字通信模式,7 流套接字操作,send 和 recv,#include int send( int sockfd,void* buffer, size_t length, int flag ); int recv( int sockfd, void* buffer, size_t length, int flag );,sockfd:本地套接字描述符. buffer:保存有發(fā)送數(shù)據(jù)的緩沖區(qū)的指針,長(zhǎng)度有l(wèi)ength指定 flags:. 傳輸控制方式(0,MSG_DONTROUTE/MSG_OOB/MSG_PEEK/MSG_WAITALL),7 流套接字操作,流套接字操作
34、示例:客戶程序向服務(wù)程序請(qǐng)求得到日期和時(shí)間信息,/客戶端程序client.c ,將用到子函數(shù)socket_connect #include void main() int sockfd, n; char recvbuffer128; sockfd = socket_connect ( “”, “13”); while( (n= recv(sockfd,recvbuffer, 127, 0) 0 ) recvbuffern = 0; printf(“%s”, recvbuffer); close( sockfd ); ,7 流套接字操作,流套接字操作示例:客戶程序向服務(wù)程
35、序請(qǐng)求得到日期和時(shí)間信息,/服務(wù)端程序server.c , 將用到子函數(shù)make_socket #include #define MAX_LISTEN 30 void main() int listen_fd, connected_fd; char buffer128; time_t ticks; struct sockaddr_in client_addr; listen_fd = make_socket(13); listen( listen_fd, MAX_LISTEN );,read/write與TCP通信故障,TCP在成功地建立好了連接后,在兩臺(tái)主機(jī)之間保持這條連接。 在連接保持期間
36、,如果用戶沒(méi)有數(shù)據(jù)在這條連接上發(fā)送,那么系統(tǒng)會(huì)周期性地發(fā)送探測(cè)數(shù)據(jù),這就是所謂的keepalive。 由于TCP連接是“端到端”的,中間需要穿越網(wǎng)絡(luò),有很多轉(zhuǎn)發(fā)節(jié)點(diǎn),這和直接用電路連接的“點(diǎn)到點(diǎn)”情況不同?!包c(diǎn)到點(diǎn)”之間如果沒(méi)有數(shù)據(jù)傳輸,線路就空閑,但是,TCP的“端到端”之間沒(méi)有數(shù)據(jù)傳輸,“端到端”的網(wǎng)絡(luò)沿途所有節(jié)點(diǎn)的線路不見(jiàn)得空閑。所以,這種周期性的探測(cè)數(shù)據(jù)不應(yīng)當(dāng)太頻繁,以免無(wú)謂地增加整個(gè)網(wǎng)絡(luò)的流量。一般默認(rèn)設(shè)置為兩個(gè)小時(shí)。,53,read/write與TCP通信故障,TCP連接建立好之后,一段時(shí)間內(nèi)沒(méi)有數(shù)據(jù)傳送。這時(shí),如果網(wǎng)絡(luò)已經(jīng)發(fā)生了故障,TCP兩小時(shí)一次的keepalive不能迅速察
37、覺(jué),TCP兩端的進(jìn)程也無(wú)法知道。這時(shí),如果調(diào)用write(),write()在將數(shù)據(jù)放到發(fā)送緩沖區(qū)后就成功地返回,返回碼不等于1。隨后立即還有write(),只要發(fā)送緩沖區(qū)有空閑,就能成功地返回。 內(nèi)核中的TCP協(xié)議軟件負(fù)責(zé)將數(shù)據(jù)發(fā)送到對(duì)方主機(jī),由于網(wǎng)絡(luò)發(fā)生了永久性故障,那么將不可能收到對(duì)方的ACK。TCP就試著重發(fā),經(jīng)多次重發(fā)仍沒(méi)有對(duì)方回送的證實(shí)后,才斷定故障發(fā)生。這樣,隨后再對(duì)這一連接進(jìn)行的read()和write()才會(huì)失敗,立刻返回1。,54,read/write與TCP通信故障,從write第一次試著發(fā)送數(shù)據(jù)到內(nèi)核的TCP協(xié)議軟件多次重發(fā)后認(rèn)定出了故障,這段時(shí)間間隔常常為十幾分鐘。有
38、時(shí)在主機(jī)向網(wǎng)絡(luò)發(fā)送數(shù)據(jù)時(shí),網(wǎng)絡(luò)中的設(shè)備會(huì)主動(dòng)以ICMP報(bào)文向主機(jī)報(bào)告“目的不可達(dá)”,這時(shí)TCP會(huì)放棄重試,立即斷定網(wǎng)絡(luò)出了故障,那么,從write()第一次試著發(fā)送到斷定出了故障的時(shí)間間隔值就小得多。 在斷定出了故障后,隨后的write/read或正在進(jìn)行中的write/read都會(huì)失敗,以1返回。 其他原因也有可能導(dǎo)致TCP傳輸失敗,例如:兩臺(tái)主機(jī)之間建立好了一條TCP連接之后,其中一臺(tái)來(lái)不及關(guān)閉所有TCP連接就突然掉電,不再啟動(dòng)。如果它正在以read()等待已停止運(yùn)行的主機(jī)送來(lái)數(shù)據(jù),那么它就會(huì)執(zhí)著地等下去,直到keepalive發(fā)現(xiàn)了連接斷開(kāi)。,55,read/write與TCP通信故障,
39、在無(wú)法確認(rèn)數(shù)據(jù)正確到達(dá)對(duì)方的前提下,write調(diào)用就返回,返回碼不等于1,但是,數(shù)據(jù)可能僅僅復(fù)制到了內(nèi)核中的緩沖區(qū)。這似乎與TCP承諾的無(wú)差錯(cuò)可靠的數(shù)據(jù)流傳輸相違背,其實(shí)不然,因?yàn)橹灰菑腡CP接收到的,就一定是正確的,中間絕沒(méi)有丟失數(shù)據(jù)。 在UNIX中的TCP連接上使用write()和read()能帶給程序員的服務(wù)質(zhì)量是“發(fā)出去的數(shù)據(jù)不能保證一定會(huì)到達(dá)對(duì)方,但是收到的數(shù)據(jù)就一定是正確的”。如果發(fā)送方需要知道對(duì)方是否確實(shí)收到了數(shù)據(jù),那么,接收方就該通過(guò)TCP給一個(gè)回執(zhí)。由于TCP是可靠傳輸,完全可以一批數(shù)據(jù)傳輸完成后給一個(gè)回執(zhí)。 類似的問(wèn)題是,對(duì)方計(jì)算機(jī)一樣也不知道它給出的回執(zhí)是否確實(shí)能到達(dá)受信方。,56,read/write與TCP通信故障,在TCP連接一側(cè)的一次write可能會(huì)導(dǎo)致對(duì)方的多次read,也有可能多次write,只能導(dǎo)致對(duì)方的一次read,因此,靠發(fā)送端的一次write導(dǎo)致接收端的一次read這樣一個(gè)對(duì)應(yīng)關(guān)系來(lái)保持?jǐn)?shù)據(jù)記錄的邊界是靠不住的。 TCP僅僅承諾在連接的兩端提供了一個(gè)透明的可靠的字節(jié)流傳輸,為了提高傳輸或者重傳的效率,TCP可能會(huì)把應(yīng)用程序多次write的一段段數(shù)據(jù),粘接在一起作為一個(gè)數(shù)據(jù)包,
溫馨提示
- 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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 第6課 工業(yè)化國(guó)家的社會(huì)變化(備課筆記系列講義)歷史統(tǒng)編版九年級(jí)下冊(cè)
- 2025年高職(新能源汽車檢測(cè)與維修技術(shù))檢測(cè)技術(shù)階段測(cè)試題及答案
- 2025年中職應(yīng)用馬其頓語(yǔ)(日常馬語(yǔ)交流)試題及答案
- 2025年大學(xué)二年級(jí)(管理學(xué))應(yīng)用管理綜合測(cè)試題及答案
- 2025年高職高爾夫服務(wù)與管理(服務(wù)應(yīng)用)試題及答案
- 2025年大學(xué)化工類(化工性能測(cè)試)試題及答案
- 2025年大學(xué)作物生產(chǎn)與經(jīng)營(yíng)管理(作物生產(chǎn)系統(tǒng))試題及答案
- 2025年中職廣播電視編導(dǎo)(廣播電視教育心理學(xué))試題及答案
- 2025年高職(生態(tài)農(nóng)業(yè)技術(shù))有機(jī)農(nóng)業(yè)種植測(cè)試題及答案
- 2025年中職幼兒教育學(xué)(幼兒教育基礎(chǔ))試題及答案
- 朱子治家格言(朱子家訓(xùn))課件
- 20S517 排水管道出水口
- vpap iv st說(shuō)明總體操作界面
- 2023人事年度工作計(jì)劃七篇
- LY/T 1692-2007轉(zhuǎn)基因森林植物及其產(chǎn)品安全性評(píng)價(jià)技術(shù)規(guī)程
- 初中一年級(jí)(7年級(jí))上學(xué)期生物部分單元知識(shí)點(diǎn)
- 長(zhǎng)興中學(xué)提前招生試卷
- 2022年基礎(chǔ)教育國(guó)家級(jí)教學(xué)成果獎(jiǎng)評(píng)審工作安排
- 生物統(tǒng)計(jì)學(xué)(課堂PPT)
- 突發(fā)公共衛(wèi)生事件應(yīng)急處理
- 腫瘤內(nèi)科中級(jí)分章試題精選
評(píng)論
0/150
提交評(píng)論