第六章 UNIX的網(wǎng)絡(luò)通信初步_第1頁(yè)
第六章 UNIX的網(wǎng)絡(luò)通信初步_第2頁(yè)
第六章 UNIX的網(wǎng)絡(luò)通信初步_第3頁(yè)
第六章 UNIX的網(wǎng)絡(luò)通信初步_第4頁(yè)
第六章 UNIX的網(wǎng)絡(luò)通信初步_第5頁(yè)
已閱讀5頁(yè),還剩102頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、第六章第六章 unix的網(wǎng)絡(luò)通信初步的網(wǎng)絡(luò)通信初步 (1) unix操作系統(tǒng)為進(jìn)程通信提供了相應(yīng)設(shè)施,如管道(pipe)、命名管道(named pipe)和軟中斷信號(hào)(signal),消息(message)、共享存儲(chǔ)區(qū)(shared memory)和信號(hào)量(semaphore)等,但這些都只限于用在本機(jī)進(jìn)程間的通信。 (2) 為了實(shí)現(xiàn)計(jì)算機(jī)全面聯(lián)網(wǎng)與信息的異地處理,需要為用戶構(gòu)建client/server 應(yīng)用的通訊結(jié)構(gòu),通過網(wǎng)絡(luò)接口編程,以解決不同主機(jī)進(jìn)程間的通信問題。6.1 網(wǎng)絡(luò)接口網(wǎng)絡(luò)接口 在unix系統(tǒng)中,網(wǎng)絡(luò)接口有兩類:一類是源自bsd unix的sockets(套接口),另一類是u

2、nix system v 的tli(transmission layer interface)。tli是根據(jù)工業(yè)標(biāo)準(zhǔn)“iso傳輸服務(wù)定義(iso 8072)”實(shí)現(xiàn)的,由于svr3只包括了流以及tli構(gòu)建模塊而并沒有任何的如tcp/ip之類的協(xié)議,因此tli具有與協(xié)議無關(guān)性,關(guān)鍵技術(shù)是定義了一組對(duì)許多傳輸協(xié)議公共的服務(wù)。目前tli的修正版xtl在unix系統(tǒng)中仍然得到廣泛的使用。socket api是基于各種傳輸協(xié)議之上的,目前已經(jīng)成為網(wǎng)絡(luò)編程的既成事實(shí)標(biāo)準(zhǔn)。 基于sockets api的通用性,本章只討論sockets api的應(yīng)用。 目前最通用的提供遠(yuǎn)程進(jìn)程間通信的api是伯克利套接字(be

3、rkeley socket)接口。 所謂的套接字是一種抽象數(shù)據(jù)結(jié)構(gòu),用以創(chuàng)建一條在沒有相關(guān)聯(lián)的進(jìn)程間發(fā)送、接收消息的通道(連接點(diǎn))。這些進(jìn)程在通信前各自建立一個(gè)socket,并通過對(duì)socket的讀寫操作實(shí)現(xiàn)通信功能。 當(dāng)使用基于套接字的連接時(shí),服務(wù)器端進(jìn)程創(chuàng)建一個(gè)套接字,并把它映射到一個(gè)本地地址上,然后等待(監(jiān)聽)客戶端的請(qǐng)求??蛻舳诉M(jìn)程也創(chuàng)建自己的套接字,并確定服務(wù)器端的具體位置(比如主機(jī)名,端口號(hào)等)。依靠傳輸連接方式的應(yīng)答,客戶端進(jìn)程就可以開始發(fā)送和接收數(shù)據(jù),而不用管是否接收到服務(wù)器進(jìn)程的正式確認(rèn)(應(yīng)答)。 每個(gè)套接字都有其類型和一個(gè)與之相連的進(jìn)程。當(dāng)應(yīng)用程序創(chuàng)建套接字時(shí),套接字系統(tǒng)調(diào)

4、用返回句柄,即所謂套接字描述字,它和文件描述字是有所區(qū)別的:當(dāng)文件描述字,由open命令創(chuàng)建時(shí),它被耦合到特定的文件或設(shè)備;當(dāng)套接字描述字由socket命令創(chuàng)建時(shí),它并不被耦合到任何位置。當(dāng)套接字用作面向連接的網(wǎng)絡(luò)傳輸接口時(shí),應(yīng)用程序可用bind命令將套接字明確地耦合到一個(gè)地址。當(dāng)套接字用作無連接的網(wǎng)絡(luò)傳輸接口時(shí),應(yīng)用程序可以在用sendto命令發(fā)送數(shù)據(jù)報(bào)時(shí)動(dòng)態(tài)地提供地址。 6.1.1 套接口的類型套接口的類型 unix 提供下列四種類型的socket: 數(shù)據(jù)流套接字(sock_stream),它提供雙向的、面向連接的、可靠的、有序的并且不重復(fù)的無記錄邊界數(shù)據(jù)流。一對(duì)相連的流socket提供幾

5、乎類似于管道的接口。流式socket針對(duì)于tcp服務(wù)應(yīng)用,如文件傳送協(xié)議(ftp)。 數(shù)據(jù)流套接字采用tcp協(xié)議,這是個(gè)有連接的協(xié)議,在數(shù)據(jù)正式傳輸前必須建立連接,此連接是個(gè)穩(wěn)定的雙向線路,可以保證提供無錯(cuò)誤的傳送管道,因?yàn)橹灰獍趥魉瓦^程發(fā)生錯(cuò)誤損毀、次序錯(cuò)亂或送錯(cuò),tcp將會(huì)察覺問題并要求重新發(fā)送數(shù)據(jù),因此適合在需要大量的數(shù)據(jù)傳輸并要求完全正確的狀況時(shí)使用。 6.1.1 套接口的類型套接口的類型 數(shù)據(jù)報(bào)套接字(sock_dgram),它也支持雙向數(shù)據(jù)流,但數(shù)據(jù)以獨(dú)立包形式被發(fā)送,無可靠性保證、無序、數(shù)據(jù)可能丟失或重復(fù)。數(shù)據(jù)報(bào)socket提供一個(gè)無連接服務(wù),對(duì)應(yīng)于無連接的udp服務(wù)應(yīng)用,如

6、網(wǎng)絡(luò)文件系統(tǒng)(nfs)、組播通信。 數(shù)據(jù)報(bào)套接字采用udp協(xié)議,這是個(gè)無連接的協(xié)議,發(fā)送主機(jī)直接將封包送至目的主機(jī),無需事先建立連接。因?yàn)楸苊饬私⑦B接所需的高代價(jià),采用數(shù)據(jù)報(bào)方式效率較高,但數(shù)據(jù)報(bào)方式自身不能處理數(shù)據(jù)傳輸過程出現(xiàn)的錯(cuò)誤,因此使用數(shù)據(jù)報(bào)方式的應(yīng)用程序必須自己處理這些問題。一般在比較簡(jiǎn)單的網(wǎng)絡(luò)應(yīng)用程序中使用數(shù)據(jù)報(bào)方式。 6.1.1 套接口的類型套接口的類型 原始套接字(sock_raw),它提供對(duì)支持socket概念的基本通信協(xié)議的訪問。該接口允許用戶訪問支持套接字抽象的底層通信協(xié)議,如ip、icmp直接訪問,常用于檢驗(yàn)新的協(xié)議實(shí)現(xiàn)或訪問現(xiàn)有服務(wù)中配置的新設(shè)備。 順序報(bào)套接字:這

7、種類型的套接字類似數(shù)據(jù)流套接字,不同的是其傳送的數(shù)據(jù)具有記錄邊界。6.1.2 套接口支持的協(xié)議套接口支持的協(xié)議 1套接字協(xié)議簇套接字協(xié)議簇(family) (1)af-unix :unix domain協(xié)議,在該域中創(chuàng)建的套接字只能為同在一個(gè)主機(jī)的進(jìn)程所用; (2)af-inet:internet協(xié)議,這個(gè)域里的套接字允許在不同主機(jī)上的不相關(guān)的進(jìn)程間進(jìn)行通訊; 2套接字協(xié)議套接字協(xié)議(protocol) (1)tcp協(xié)議(傳輸控制協(xié)議):負(fù)責(zé)保證兩臺(tái)主機(jī)之間傳輸?shù)姆纸M到達(dá)目的地,保證分組以正確的順序(確切的說,分組按正確的順序重新編排)并無差錯(cuò)地到達(dá)目的地。當(dāng)分組在兩臺(tái)主機(jī)間的路徑上丟失時(shí),t

8、cp協(xié)議確保重傳丟失的分組; (2)udp協(xié)議(用戶數(shù)據(jù)報(bào)協(xié)議):類似tcp協(xié)議,但是它是不可靠的。udp不對(duì)分組進(jìn)行檢查、重新排序和重傳; 6.1.2 套接口支持的協(xié)議套接口支持的協(xié)議af_inetaf_inet6af_localaf_routeaf_key流套接口tcptcpy數(shù)據(jù)報(bào)套接口udpudpy原始套接口ipv4ipv6yy6.1.3 套接口地址結(jié)構(gòu)套接口地址結(jié)構(gòu) 大多數(shù)套接口函數(shù)都需要一個(gè)指向套接口地址結(jié)構(gòu)的指針作參數(shù),而在實(shí)際應(yīng)用中各協(xié)議的地址結(jié)構(gòu)是不同的,例如ipv4是32位的,而ipv6則是128的。由于早期定義的原因,unix的系統(tǒng)函數(shù)都只支持通用的地址結(jié)構(gòu)而無法區(qū)分特定

9、協(xié)議的地址結(jié)構(gòu),因此在調(diào)用這些函數(shù)時(shí)必須將指向具體協(xié)議的套接口地址結(jié)構(gòu)的指針類型轉(zhuǎn)換成指向通用套接口地址結(jié)構(gòu)的類型。如serv是ipv4的地址格式,可以通過(struct sockaddr *)&serv來轉(zhuǎn)換成通用地址格式。通用套接口地址結(jié)構(gòu):(在頭文件中定義)struct sockaddr u_char sa_len; /* 地址總長(zhǎng)度*/ u_short sa_family; /* 協(xié)議族,af_xxx */ char sa_data14; /* 具體協(xié)議地址 */ ; 其中:sa_family為套接字協(xié)議簇類型;sa_data中存儲(chǔ)具體的協(xié)議地址,不同的協(xié)議簇有不同的地址結(jié)構(gòu),

10、如tcp/ip協(xié)議的套接字地址結(jié)構(gòu)是在文件中定義的sockaddr_in結(jié)構(gòu),這個(gè)結(jié)構(gòu)定義如下:ipv4套接口地址結(jié)構(gòu):(在頭文件中定義)struct sockaddr u_char sin_len; /* 32位的ipv4地址總長(zhǎng)度*/ u_short sin_family; /* 協(xié)議族,af_inet */ u_short sin_port; /*協(xié)議端口號(hào)*/ char sin_zero8; /* 保留,置0 */ ; 要注意的是,套接口的地址結(jié)構(gòu)是按網(wǎng)絡(luò)字節(jié)順序而不是按主機(jī)存儲(chǔ)字節(jié)順序來存儲(chǔ)的(網(wǎng)絡(luò)字節(jié)是從高到低的順序,而主機(jī)是從低到高的順序),因此,在internet上傳輸數(shù)據(jù)時(shí)就

11、需要進(jìn)行轉(zhuǎn)換,否則就會(huì)出現(xiàn)數(shù)據(jù)不一致。下面是幾個(gè)常用的字節(jié)順序轉(zhuǎn)換函數(shù): htonl():把32位值從主機(jī)字節(jié)序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序 ntohl():把32位值從網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)換成主機(jī)字節(jié)序 通用套接口地址結(jié)構(gòu) ipv4套接口地址結(jié)構(gòu)長(zhǎng)度 af_iniet16位端口號(hào) sin_portsin_zero8長(zhǎng)度 af_iniet16位端口號(hào) sin_portsa_data166.2 網(wǎng)絡(luò)通信的系統(tǒng)函數(shù)網(wǎng)絡(luò)通信的系統(tǒng)函數(shù) 6.2.1 建立網(wǎng)絡(luò)連接的數(shù)據(jù)結(jié)構(gòu) 一個(gè)用戶為了執(zhí)行網(wǎng)絡(luò)i/o操作,它首先要調(diào)用函數(shù)socket ()。這個(gè)函數(shù)陷入內(nèi)核后執(zhí)行sys_socket()系統(tǒng)調(diào)用,后者為用戶創(chuàng)建一個(gè)名為so

12、cket的數(shù)據(jù)結(jié)構(gòu),并對(duì)其進(jìn)行一些簡(jiǎn)單的初始化工作,然后返回一個(gè)小的正整數(shù),代表這個(gè)socket結(jié)構(gòu)。這個(gè)小的正整數(shù)與文件描述符功能類似,所以把它稱作套接字描述符。6.2.1 建立網(wǎng)絡(luò)連接的數(shù)據(jù)結(jié)構(gòu)struct socket socket_state state; unsigned long flags; struct proto_ops *ops; struct inode *inode; struct fasync_struct *fasync_list; struct file *file; struct spck *sk; wait_queue_head_t wait; short t

13、ype; unsigned char passcred; unsigned char tli;其中:state描述該套接字的狀態(tài)信息; flags表示連接時(shí)的一些控制信息; ops指向oroto_ops 結(jié)構(gòu)類型的指針; inode是指向inode 結(jié)構(gòu)類型的指針; fasync_list存在異步進(jìn)行處理的不同文件的指針; sk指向socket結(jié)構(gòu)的指針; wait表示等待在該socket 結(jié)構(gòu)上的人物列表; type表示數(shù)據(jù)包的類型。6.2.2 網(wǎng)絡(luò)連接的建立和關(guān)閉網(wǎng)絡(luò)連接的建立和關(guān)閉 1 建立一個(gè)套接字描述符 函數(shù)格式如下: int socket(int family, int type,

14、 int protocol); socket( )調(diào)用中有三個(gè)參數(shù),第一個(gè)參數(shù)family是一個(gè)整數(shù)型的量,指定協(xié)議簇;第二個(gè)參數(shù)是type,表示套接字的類型;第三個(gè)參數(shù)protocol用來表示在指定協(xié)議族中使用哪種特定協(xié)議,大多情況下,該參數(shù)被設(shè)為0,讓系統(tǒng)自己去選擇基于協(xié)議族的協(xié)議。socket()調(diào)用成功則返回一個(gè)正整數(shù),即套接字描述符,用以標(biāo)識(shí)該套接字;如果調(diào)用失敗,會(huì)返回1,并設(shè)置全局變量errno為相應(yīng)的錯(cuò)誤類型。 sockek()函數(shù)的執(zhí)行流程socket( )sys_socketcall( )sys_socket( )socket_create( )socket_alloc(

15、)inet_creat()sk_alloc( )sock_alloc()用來創(chuàng)建一個(gè)socket 結(jié)構(gòu)sk_alloc( )用來創(chuàng)建一個(gè)sock 結(jié)構(gòu)系統(tǒng)調(diào)用接口sock_create(.) sock_alloc(); inet_creat(); 例1:創(chuàng)建一個(gè)套接字對(duì)#include #include #include #include #include main(void) int sock2, cpid, i; /* 套接字對(duì) */ static char bufbuf_sz; /* 消息的臨時(shí)緩沖區(qū) */ if(socketpair(pf_unix,sock_tream, 0, soc

16、k) 0) perror(“generation error ”); exit(1); switch(cpid=(int)fork() case -1: perror(“back fork ”); exit(2); case 0: /* 子進(jìn)程 */ close(sock1); for(i=0; i 10; i +=2) sleep(1); sprintf(buf, “c: %dn”,i); write(sock0, buf, sizeof(buf); read(sock0, buf , buf_sz); printf(“c %s”, buf); close(sock0); break; de

17、fault: /* 父進(jìn)程 */ close(sock0); for(i=0; i 10; i +=2) sleep(1); read(sock1, buf , buf_sz); printf(“p%s”, buf); sprintf(buf, “p: %dn”,i); write(sock1, buf, sizeof(buf); close(sock1); return 0;6.2.2 網(wǎng)絡(luò)連接的建立和關(guān)閉網(wǎng)絡(luò)連接的建立和關(guān)閉 2 指定本機(jī)地址及端口 bind() int bind(int sockfd, struct sockaddr * my_addr, int addrlen); /*

18、 0成功;-1出錯(cuò)*/ 其中:參數(shù)sockfd是調(diào)用socket()返回的套接字,參數(shù)my_addr是通用地址結(jié)構(gòu)指針,參數(shù)addrlen是該結(jié)構(gòu)的長(zhǎng)度,常被設(shè)置為sizeof(struct sockaddr)。該函數(shù)為套接字分配一個(gè)本地協(xié)議地址,對(duì)于ip協(xié)議來說是ip地址和tcp或udp端口號(hào)的組合。 bind( )調(diào)用在unix域中用來聯(lián)系套接字和它的名字(一個(gè)文件名),在因特網(wǎng)域用來將本地地址和套接字綁定在一起,包括ip地址和端口號(hào)。它是依據(jù)第二個(gè)參數(shù)的值的不同而不同的;套接字和本地地址的綁定采用組合的方式,如下表 程序類型ip地址端口號(hào)說明服務(wù)器inaddr-any非0指定服務(wù)器公認(rèn)端

19、口,表示服務(wù)器愿意接收來自任何網(wǎng)絡(luò)設(shè)備的客戶機(jī)連接服務(wù)器本地ip非0指定服務(wù)器ip地址和公認(rèn)端口,表示它只接收來自于這個(gè)ip地址的特定網(wǎng)絡(luò)設(shè)備接口的客戶機(jī)連接。多用于有多個(gè)網(wǎng)絡(luò)設(shè)備接口,限制服務(wù)器的接收范圍客戶機(jī)inaddr-any非0指定客戶機(jī)的連接端口,一般使用保留端口號(hào)客戶機(jī)本地ip非0指定客戶機(jī)的ip地址和連接端口號(hào),表示客戶機(jī)使用指定的網(wǎng)絡(luò)設(shè)備接口和端口號(hào)進(jìn)行通信客戶機(jī)本地ip0指定客戶機(jī)的ip地址,表示客戶機(jī)和指定的網(wǎng)絡(luò)設(shè)備接口通信,系統(tǒng)自動(dòng)為客戶機(jī)選擇一個(gè)未使用的端口號(hào)注:inaddr-any在unix系統(tǒng)中被映射為0的常量6.2.2 網(wǎng)絡(luò)連接的建立與關(guān)閉網(wǎng)絡(luò)連接的建立與關(guān)閉 使

20、用bind函數(shù)時(shí),可以用下面的賦值實(shí)現(xiàn)自動(dòng)獲得本機(jī)ip地址和隨機(jī)獲取一個(gè)沒有被占用的端口號(hào): my_addr.sin_port = 0; /* 系統(tǒng)隨機(jī)選擇一個(gè)未被使用的端口號(hào) */ my_addr.sin_addr.s_addr = inaddr_any; /* 填入本機(jī)ip地址 */ 6.2.2 網(wǎng)絡(luò)連接的建立與關(guān)閉網(wǎng)絡(luò)連接的建立與關(guān)閉 3 客戶啟動(dòng)連接connect() int connect(int sockfd, struct sockaddr *serv_addr, int addrlen); /*返回:0成功;-1出錯(cuò)*/ sockfd是調(diào)用socket()返回的套接字;serv

21、_addr是包含遠(yuǎn)端服務(wù)器ip地址和端口號(hào)的通用地址結(jié)構(gòu)的指針;addrlen是遠(yuǎn)端地址結(jié)構(gòu)的長(zhǎng)度。tcp客戶用connect函數(shù)建立一個(gè)與遠(yuǎn)端tcp服務(wù)器的連接,正是connect激發(fā)了tcp三次握手的連接過程。 由于協(xié)議族總被包含在套接字地址結(jié)構(gòu)的前兩個(gè)字節(jié)中,并通過socket()調(diào)用與某個(gè)協(xié)議族相關(guān)。因此bind()和connect()無須協(xié)議作為參數(shù)。 connect( )用戶空間核心空間sys_socketcall( )sys_connect( )inet_stream_coonect( )tcp_v4_connect( )ip_route_output( )ip_route_ou

22、tput_key( )ip_route_connect( )ip_output( )ip_route_optput_slow( )系統(tǒng)調(diào)用傳輸層(tcp協(xié)議)網(wǎng)絡(luò)層(ipv4或ipv6)6.2.2 網(wǎng)絡(luò)連接的建立與關(guān)閉網(wǎng)絡(luò)連接的建立與關(guān)閉 4 監(jiān)聽連接listen() int listen(int sockfd, int backlog); /*返回:0成功;-1出錯(cuò)*/ sockfd是調(diào)用socket()建立的套接字,它是一個(gè)socket調(diào)用成功后的返回值;backlog是指定在請(qǐng)求隊(duì)列中允許的最大請(qǐng)求數(shù),一般大于5的均設(shè)為5 。 進(jìn)入的連接請(qǐng)求將在隊(duì)列中等待accept()調(diào)用建立與客戶的

23、連接。該函數(shù)僅被tcp服務(wù)器調(diào)用,它總是使套接口處于被動(dòng)的監(jiān)聽模式,并為該套接口建立一個(gè)輸入數(shù)據(jù)隊(duì)列,將到達(dá)的服務(wù)請(qǐng)求保存在此隊(duì)列中,直到程序處理它們。如果一個(gè)服務(wù)請(qǐng)求到來時(shí),輸入隊(duì)列已滿,該套接口將拒絕連接請(qǐng)求并顯示出錯(cuò)信息。 listen( )調(diào)用將一個(gè)尚未連接的主動(dòng)套接字轉(zhuǎn)換成為一個(gè)被動(dòng)套接字,使其可以接收連接請(qǐng)求,因?yàn)橛蓅ocket( )調(diào)用所創(chuàng)建的套接字(主動(dòng)套接字)只可以用來進(jìn)行主動(dòng)連接,不能接收連接的請(qǐng)求。 5 服務(wù)器接受連接accept() int accept(int sockfd, struct sockaddr *addr, int *addrlen); /*返回:0成功

24、;-1出錯(cuò)*/ accept( )調(diào)用從傾聽套接字的連接隊(duì)列中接收第一個(gè)連接,生成一個(gè)新的套接字來完成客戶機(jī)的要求,原來的套接字繼續(xù)監(jiān)視網(wǎng)絡(luò),等待用戶的連接。 accept( )調(diào)用的第一個(gè)參數(shù)sockfd是用來標(biāo)識(shí)從哪個(gè)套接字中接收連接的 ,addr是一個(gè)指向客戶方套接字地址結(jié)構(gòu)的變量指針,該變量用來接收提出連接請(qǐng)求服務(wù)的客戶的協(xié)議地址,指明某臺(tái)主機(jī)從某個(gè)端口發(fā)出該請(qǐng)求;addr的確切格式由套接字創(chuàng)建時(shí)建立的地址族決定。addrten通常為一個(gè)指向值為sizeof(struct socka ddr) 的整型指針變量,指明客戶方套接字地址結(jié)構(gòu)的長(zhǎng)度(字節(jié)數(shù))。 accept()只用于tcp服務(wù)

25、器,且在調(diào)用前應(yīng)該先調(diào)用過listen()。當(dāng)listen()偵聽到有連接請(qǐng)求到達(dá)時(shí),accept()調(diào)用將請(qǐng)求連接隊(duì)列上的第一個(gè)客戶的套接字地址及長(zhǎng)度放入addr和addrlen,并與該客戶在sockfd建立連接。 6.2.2 網(wǎng)絡(luò)連接的建立與關(guān)閉網(wǎng)絡(luò)連接的建立與關(guān)閉 6 6 關(guān)閉套接字關(guān)閉套接字close()close() close(int sockfd); 參數(shù) sockfd是待關(guān)閉的套接字。close()是標(biāo)準(zhǔn)的關(guān)閉函數(shù),在tcp服務(wù)中激發(fā)該套接字的連接關(guān)閉。函數(shù)功能只是對(duì)該套接字作“關(guān)閉標(biāo)識(shí)”表明不可用,而連接的另一方還在試圖發(fā)送排隊(duì)的數(shù)據(jù)。只有當(dāng)對(duì)方發(fā)現(xiàn)通信的套接字已不可用,自己

26、也調(diào)用close()關(guān)閉本機(jī)的套接字,才真正地結(jié)束數(shù)據(jù)的發(fā)送。 6.2.3 發(fā)送數(shù)據(jù)發(fā)送數(shù)據(jù) unix內(nèi)核為用戶提供的發(fā)送數(shù)據(jù)的系統(tǒng)調(diào)用有5個(gè),它們分別是: 1) write():與文件系統(tǒng)中的write()完全一致; 2) writev():與write功能相似,所不同的是writev可以在一次函數(shù)調(diào)用中寫多個(gè)緩沖區(qū)(集中寫) 3) send():面向連接的發(fā)送數(shù)據(jù)過程(tcp); 4) sendto():面向無連接的發(fā)送數(shù)據(jù)過程(udp) 5) sendmsg():直接使用msghdr 結(jié)構(gòu)發(fā)送數(shù)據(jù)。在功能上可以代替以上四個(gè)輸出函數(shù)。1 1 發(fā)送數(shù)據(jù)的系統(tǒng)調(diào)用接口發(fā)送數(shù)據(jù)的系統(tǒng)調(diào)用接口wr

27、ite()writev()send()sendto()sendmsg()sys_write()sys_writev()sys_socketcall()sock_write()sock_write()sys_send()sys_sendmsg()sys_sendto()sock_sendmsg()inet_sendmsg()系統(tǒng)調(diào)用應(yīng)用層bsd 套接字接口inet 套接字(傳輸層)2. 2. 從從inteinte協(xié)議層到協(xié)議層到ipip層層inet_sendmsg()tcp_sendmsg()tcp_sendmsg()tcp_transmit_skb()ip_queue_xmit()網(wǎng)絡(luò)層bsd

28、 套接字接口inet 套接字(傳輸層)2. ip2. ip層到硬件層的數(shù)據(jù)發(fā)送過程層到硬件層的數(shù)據(jù)發(fā)送過程 ip_queue_xmit2()ip_output()ip_finish_output()ip_queue_xmit()net/core/dev.cip層dev_queue_xmit()neigh_resolve_output_()ip_finish_output2()net/ipv4/ip_et/ipv4/ip_et/ipv4/ip_et/ipv4/ip_et/ipv4/ip_et/core/neighbour.c數(shù)據(jù)鏈路與硬件層4. 4. 硬件層的數(shù)據(jù)發(fā)送過程硬件層的數(shù)據(jù)發(fā)送過程de

29、v_queue_xmit()ei_start_xmit()qdisc_run()ei_start_xmit()物理設(shè)備硬件層net/pkt_sched.h硬件上有隊(duì)列嗎?以太網(wǎng)卡(例如ne2000)6.2.4 接收數(shù)據(jù)接收數(shù)據(jù)unix內(nèi)核為用戶提供的接收數(shù)據(jù)的系統(tǒng)調(diào)用也是5個(gè),它們分別是: 1) read():與文件系統(tǒng)中的read()完全一致; 2) readv():與read功能相似,所不同的是readv可以在一次函數(shù)調(diào)用中讀多個(gè)緩沖區(qū); 3) recv():面向連接的接收數(shù)據(jù)過程(tcp); 4) recvfrom():面向無連接的接收數(shù)據(jù)過程(udp) 5) recvmsg():直接使

30、用msghdr 結(jié)構(gòu)來接收數(shù)據(jù)。在功能上可以代替以上四個(gè)輸出函數(shù)。1 1 接收數(shù)據(jù)的系統(tǒng)調(diào)用接口接收數(shù)據(jù)的系統(tǒng)調(diào)用接口read()readv()recv()recvfrom()recvmsg()sys_read()sys_readv()sys_socketcall()sock_read()sock_readv()sys_recv()sys_recvmsg()sys_recvfrom()sock_recvmsg()inet_recvmsg()系統(tǒng)調(diào)用應(yīng)用層bsd 套接字接口inet 套接字(傳輸層)2. 硬件層接收數(shù)據(jù)分析 netif_rx()ei_recieve()ei_interrupt()

31、ip_rev()net/core/dev.cdrivers/net/8390.cdrivers/net/8309.c 硬件層net_rx_action()ip層物理設(shè)備ne網(wǎng)卡向隊(duì)列寫入數(shù)據(jù)backing接收數(shù)據(jù)隊(duì)列從隊(duì)列中讀出數(shù)據(jù)2. 硬件層接收數(shù)據(jù)分析3. 從ip層接收數(shù)據(jù) tcp_v4_rcv()ip_local_deliver_finish()ip_local_deliver()tcp_rev_established()inet層net_rx_action()ip_rev()ip_rcv_finish()net/ipv4/tcp_et/ipv4/ip_et/ipv4/ip_et/ipv

32、4/input.c 硬件層tcp_v4_do_rcv()receive_queue隊(duì)列 ip層4. 4. 從從inteinte層接收數(shù)據(jù)層接收數(shù)據(jù)6.3 套接字編程方法套接字編程方法 流套接字的服務(wù)器進(jìn)程和客戶機(jī)進(jìn)程在進(jìn)行通信前必須建立一條連接,其中,初始化連接的是客戶端進(jìn)程,接收連接的進(jìn)程是服務(wù)器端的進(jìn)程。建立連接和通信的主要步驟如下:(1)服務(wù)進(jìn)程首先調(diào)用socket( )創(chuàng)建一個(gè)流套接字;(2)服務(wù)進(jìn)程調(diào)用bind( )公開服務(wù)器地址,將服務(wù)器地址與套接字綁定在一起;(3)服務(wù)進(jìn)程調(diào)用listen( )將套接字轉(zhuǎn)換成傾聽套接字,此時(shí)該套接字可以接收來自客戶機(jī)的請(qǐng)求;(4)通過accept

33、( )阻塞服務(wù)進(jìn)程,此時(shí)該服務(wù)器進(jìn)入一個(gè)無限循環(huán),等待客戶進(jìn)程建立連接;(5)客戶進(jìn)程也通過socket( )創(chuàng)建一個(gè)流套接字,然后調(diào)用connect( )與服務(wù)進(jìn)程建立連接;(6)當(dāng)客戶進(jìn)程的連接請(qǐng)求到達(dá)服務(wù)器后,服務(wù)進(jìn)程進(jìn)程被喚醒,生成一個(gè)新的套接字,服務(wù)進(jìn)程用這個(gè)新的套接字按預(yù)先定義的協(xié)議調(diào)用read( )和write( )進(jìn)行通信,處理客戶進(jìn)程的要求;而服務(wù)進(jìn)程最早生成的套接字則繼續(xù)用于監(jiān)聽網(wǎng)絡(luò)上的服務(wù)請(qǐng)求;(7)處理完成后,服務(wù)進(jìn)程和客戶進(jìn)程調(diào)用close( )關(guān)閉這個(gè)連接和套接字;服務(wù)器socket()、bind()、listen()accept()read()write()rea

34、d()close()阻塞,等待客戶機(jī)連接請(qǐng)求 coonect()write()read()socket()close()客戶建立連接(tcp三路握手)發(fā)送數(shù)據(jù)請(qǐng)求接收數(shù)據(jù)通信結(jié)束、關(guān)閉連接 (1)服務(wù)器一方main(void) if(創(chuàng)建一個(gè)流套接字返回值0) 出錯(cuò)提示; 退出; if(命名套接字返回值0) 出錯(cuò)提示; 退出; if(監(jiān)聽連接請(qǐng)求返回值0) 出錯(cuò)提示; 退出; for( ; ; ) 新的套接描述符=取得第一個(gè)連接請(qǐng)求返回代碼; if(新的套接描述符0) 出錯(cuò)提示; 退出; 接收數(shù)據(jù)信息; 處理請(qǐng)求; 將應(yīng)答發(fā)送給客戶機(jī); 關(guān)閉套接字; (2)客戶一方 main(void) if

35、(創(chuàng)建一個(gè)流套接字返回值0) 出錯(cuò)提示; 退出; if(連接服務(wù)器返回值0) 出錯(cuò)提示; 退出; for( ; ; ) 發(fā)送數(shù)據(jù)信息; 接收服務(wù)器方應(yīng)答; 關(guān)閉套接字; 1 數(shù)據(jù)報(bào)套接字的服務(wù)器進(jìn)程和客戶機(jī)進(jìn)程在進(jìn)行通信前不用建立連接。通信的主要步驟如下: (1)服務(wù)進(jìn)程首先調(diào)用socket ( )創(chuàng)建一個(gè)數(shù)據(jù)報(bào)套接字; (2)服務(wù)進(jìn)程調(diào)用bind( )將服務(wù)器地址綁定在在這個(gè)套接字上; (3)通過recvfrom( )阻塞服務(wù)進(jìn)程,等待客戶進(jìn)程發(fā)來的請(qǐng)求; (4)客戶機(jī)首先調(diào)用socket( )創(chuàng)建一個(gè)數(shù)據(jù)報(bào)套接字; (5)客戶機(jī)進(jìn)程調(diào)用bind( )將客戶機(jī)地址綁定在在此套接字上; (6)

36、調(diào)用sendto( ),客戶機(jī)進(jìn)程向服務(wù)進(jìn)程發(fā)出請(qǐng)求; (7)服務(wù)進(jìn)程接到客戶機(jī)數(shù)據(jù)報(bào)后被喚醒,執(zhí)行完客戶機(jī)請(qǐng)求后調(diào)用sendto( )將處理結(jié)果返回給客戶機(jī); (8)客戶機(jī)調(diào)用recvfrom( )接收服務(wù)進(jìn)程返回的請(qǐng)求處理結(jié)果; (9)服務(wù)進(jìn)程和客戶進(jìn)程調(diào)用close( )撤消套接字; socket()服務(wù)bind()recvfrom()進(jìn)程請(qǐng)求sendto()socket()sendto()read()close()客戶recvfrom()數(shù)據(jù)(請(qǐng)求)數(shù)據(jù)(回答)阻塞直至收到來自客戶的數(shù)據(jù)報(bào) (1)服務(wù)器一方 main(void) if(創(chuàng)建一個(gè)數(shù)據(jù)報(bào)套接字返回值0) 出錯(cuò)提示; 退出;

37、 if(命名套接字返回值0) 出錯(cuò)提示; 退出; for( ; ; ) 接收客戶機(jī)數(shù)據(jù)報(bào)(請(qǐng)求); 處理請(qǐng)求; 將數(shù)據(jù)(結(jié)果)發(fā)送給客戶機(jī); 關(guān)閉套接字; (1)客戶端一方 main(void) if(創(chuàng)建一個(gè)數(shù)據(jù)報(bào)套接字返回值0) 出錯(cuò)提示; 退出; if(命名該套接字返回值0) 出錯(cuò)提示; 退出; for( ; ; ) 發(fā)送數(shù)據(jù)報(bào)(請(qǐng)求)給服務(wù)器; 接收服務(wù)器應(yīng)答; 關(guān)閉套接字; 6.5 6.5 基于客戶基于客戶/ /服務(wù)器模式的網(wǎng)絡(luò)編程服務(wù)器模式的網(wǎng)絡(luò)編程1 客戶/服務(wù)器的工作流程 (1) 必須使用標(biāo)準(zhǔn)函數(shù)庫(kù)實(shí)現(xiàn)系統(tǒng)調(diào)用,以陷入內(nèi)核獲得完成用戶任務(wù)所需要的系統(tǒng)軟、硬件資源; (2) 系統(tǒng)

38、調(diào)用都被內(nèi)核入口點(diǎn)system_call函數(shù)截獲,該函數(shù)根據(jù)所傳遞的參數(shù)確定應(yīng)該執(zhí)行哪些系統(tǒng)調(diào)用,并通過檢查系統(tǒng)調(diào)用表,以確定相應(yīng)的服務(wù)例程。最后把控制權(quán)轉(zhuǎn)給該服務(wù)例程; (3) 該服務(wù)過程立即和相關(guān)的內(nèi)核代碼模塊建立聯(lián)系。這些模塊可能進(jìn)一步需要和其他內(nèi)核模塊或者底層硬件通訊; (4) 當(dāng)系統(tǒng)調(diào)用結(jié)束后,結(jié)果按照相同的路徑反方向返回。核心把控制交給用戶程序; (5) 如果該實(shí)例有某些錯(cuò)誤的操作,如用戶堆棧溢出,處理器將引發(fā)一個(gè)異常通知內(nèi)核,有內(nèi)核執(zhí)行相應(yīng)的處理程序來處理異常事件??蛻舳朔?wù)器端1.解析域名生成服務(wù)器請(qǐng)求 2. 計(jì)算并顯示服務(wù)內(nèi)容和圖像buffet alloctedsocket

39、buffer客戶程序系統(tǒng)調(diào)用 connect() recv() send() fputs()tcp/ip協(xié)議內(nèi)核nic緩沖網(wǎng)卡1.解析域名生成服務(wù)器請(qǐng)求 2. 計(jì)算并顯示服務(wù)內(nèi)容和圖像buffet alloctedsocket buffertcp/ip協(xié)議nic緩沖accept() recv() send() open() read()文件系統(tǒng)ramordisk服務(wù)器程序內(nèi)核網(wǎng)卡httptcpip網(wǎng)絡(luò)協(xié)議internet操作系統(tǒng)支持web服務(wù)器的工作流程1 客戶/服務(wù)器的工作流程 在客戶端,如果運(yùn)行的是web測(cè)覽器,當(dāng)向web瀏覽器的“地址”窗口處輸入http:/.或者“www.”等形式的網(wǎng)址

40、并回車,就是由web瀏覽器向web服務(wù)器發(fā)出的一次客戶請(qǐng)求。該請(qǐng)求經(jīng)過解析后,通過系統(tǒng)調(diào)用由用戶態(tài)轉(zhuǎn)為核心態(tài)執(zhí)行。在核心態(tài)操作系統(tǒng)中的tcp/ip協(xié)議代碼和網(wǎng)卡驅(qū)動(dòng)程序控制網(wǎng)卡把請(qǐng)求發(fā)送到相應(yīng)的網(wǎng)絡(luò)上,等待web服務(wù)器響應(yīng)。當(dāng)服務(wù)響應(yīng)返回時(shí),由網(wǎng)卡接收,并通過內(nèi)核傳送給客戶程序。 在服務(wù)器端,內(nèi)核通過網(wǎng)卡從網(wǎng)絡(luò)上接收web請(qǐng)求,并通過系統(tǒng)調(diào)用傳遞給web服務(wù)器。web服務(wù)器根據(jù)此服務(wù)請(qǐng)求執(zhí)行相應(yīng)的服務(wù)過程,并由內(nèi)核把結(jié)果放到網(wǎng)絡(luò)上傳送給客戶。 web瀏覽器和web服務(wù)器使用url和url connection進(jìn)行網(wǎng)絡(luò)通信,這是一種較高層次的網(wǎng)絡(luò)通信,為的是訪問internet上的資源。通常,用

41、戶也常常需要編寫一個(gè)由操作系統(tǒng)提供的接口客戶/服務(wù)器應(yīng)用程序,由套接口實(shí)現(xiàn)的客戶服務(wù)器應(yīng)用程序是低級(jí)別的網(wǎng)絡(luò)通信。 因此,從程序員的觀點(diǎn)來看,操作系統(tǒng)所提供的系統(tǒng)調(diào)用定義了應(yīng)用程序和協(xié)議棧之間的接口。應(yīng)用程序無論使用哪種通信協(xié)議進(jìn)行網(wǎng)絡(luò)通信,都必須申請(qǐng)同操作系統(tǒng)交互才能得到服務(wù)。#include#include#include#include#include#include#include#includeint main(int argc,char *argv) int sockfd,numb, port = 8000; struct sockaddr_in s; struct hostent

42、 *host; char buf100; if(argc!=2) fprintf(stderr,usage:%s hostnamen,argv0); exit(1); if(!(host=gethostbyname(argv1) perror(error in resolving hostname); exit(1); sockfd=socket(af_inet,sock_stream,0); if(sockfdh_addr)-s_addr; s.sin_port=htons(port); if(connect(sockfd,(struct sockaddr*)&s,sizeof(s)

43、!=0) perror(connect); exit(1); printf(get connected!n); getchar(); strcpy(buf,give me the first file of the presidents office); if(send(sockfd,buf,strlen(buf),0)0) perror(send); exit(1); numb=recv(sockfd,buf,100,0); if(numb0) perror(recv); exit(1); bufnumb=0; printf(received=%sn,buf); close(sockfd);

44、 return 0; 在這個(gè)例子中,客戶程序向服務(wù)器發(fā)出請(qǐng)求,建立與服務(wù)器的 tcp連接,并發(fā)送服務(wù)請(qǐng)求字符串“give me the first file of presidents offic”。 在client1.c客戶程序中,先調(diào)用socket()函數(shù)創(chuàng)建一個(gè)套接口,然后調(diào)用connect()函數(shù)數(shù)建立與主機(jī)的連接。當(dāng)connect()函數(shù)返回時(shí),再通過調(diào)用send()函數(shù)向服務(wù)器發(fā)出請(qǐng)求或數(shù)據(jù)。接下來執(zhí)行recv()函數(shù)等待接收服務(wù)器發(fā)回的響應(yīng)。 下面的程序清單給出的是服務(wù)器程序server1.c。服務(wù)器程序創(chuàng)建一個(gè)永久套接口來偵聽服務(wù)請(qǐng)求。當(dāng)接收到客戶的服務(wù)請(qǐng)求時(shí),它將創(chuàng)建一個(gè)臨時(shí)

45、套接口,傳回一個(gè)字符串,然后將關(guān)閉臨時(shí)套接口,循環(huán)等待下一個(gè)客戶請(qǐng)求的到來。為了不影響前臺(tái)其他程序的運(yùn)行,可以把服務(wù)器程序?qū)懗梢粋€(gè)守護(hù)進(jìn)程(daemon),守護(hù)進(jìn)程啟動(dòng)后一直運(yùn)行在后臺(tái),監(jiān)聽客戶的請(qǐng)求,從不退出,除非顯式地將它關(guān)閉。#include#include#include#include#include#include#include#include#include #include int port=8000;void init_daemon(void) pid_t pid; int i; if (pid = fork()=-1) /*生成第1個(gè)子進(jìn)程*/ exit(1); /* f

46、ork失敗退出 */ if (pid 0) exit(0); /* 父進(jìn)程退出,使shell成為前臺(tái)進(jìn)程*/ setsid(); /* 第1子進(jìn)程成為新會(huì)話和新進(jìn)程組的領(lǐng)頭進(jìn)程的同時(shí)也失去控制終端*/ /* 第1子進(jìn)程執(zhí)行下面的操作 */ for(i=0;inofile;+i) close(i); /* 關(guān)閉已打開的文件描述符 */ chdir(/rundir); /*改變當(dāng)前運(yùn)行的目錄*/ umask(0); /*改變文件創(chuàng)建掩碼*/ return;int main(int ac,char* av) int sockfd, tsockfd, addr_size; char buf100; s

47、truct sockaddr_in s, p; char temp_buf256; init_daemon(); sockfd=socket(af_inet,sock_stream,0); if(sockfd=-1) perror(socket building failure); exit(1); bzero(&s, sizeof(s); s.sin_family = af_inet; s.sin_addr.s_addr = inaddr_any; s.sin_port = htons(port); if (bind(sockfd, (struct sockaddr *)&s

48、, sizeof(s) = -1) perror(bind); exit(1); if(listen(sockfd,10)=-1) perror(listen); exit(1); for(;) tsockfd=accept(sockfd,(struct sockaddr*)&p,&addr_size); if(tsockfd0)printf(ft:%dn,errno); perror(accept); exit(1); if(recv(tsockfd,buf,100,0)0) perror(recv); exit(1); strcpy(temp_buf,here is the

49、 first file of the presidents office.); if(send(tsockfd,temp_buf,strlen(temp_buf),0)0) perror(send); exit(1); close(tsockfd); printf(send:%s,temp_buf); 在這個(gè)服務(wù)器程序的例子中,首先執(zhí)行socket()系統(tǒng)調(diào)用,讓內(nèi)核創(chuàng)建一個(gè)永久的接口,以監(jiān)聽服務(wù)器請(qǐng)求。然后調(diào)用bind()函數(shù)將剛創(chuàng)建的套接口與眾所周知的端口號(hào)8000連在一起。接著調(diào)用listen()函數(shù)將永久套接口變成一個(gè)被動(dòng)套接口,開始監(jiān)聽來自客戶機(jī)的連接請(qǐng)求。當(dāng)函數(shù)返回時(shí),程序?qū)⑦M(jìn)入一

50、個(gè)無限循環(huán),并阻塞在accept()系統(tǒng)調(diào)用上,等待連接請(qǐng)求的接入。一旦有接入請(qǐng)求,accept()馬上返回一個(gè)臨時(shí)套接口供服務(wù)器讀取用戶的請(qǐng)求數(shù)據(jù)。然后將用戶需要的數(shù)據(jù)返回給客戶。讀取用戶數(shù)據(jù)的系統(tǒng)調(diào)用是 recv()函數(shù),發(fā)送數(shù)據(jù)的系統(tǒng)調(diào)用是send()函數(shù)。 網(wǎng)絡(luò)上數(shù)據(jù)的傳輸方向是雙向的。一個(gè)流向是把數(shù)據(jù)從圖l6a的用戶層傳輸?shù)骄W(wǎng)卡,另一個(gè)流向是把數(shù)據(jù)從網(wǎng)卡傳輸?shù)接脩魧?。無論流向哪一方,中間都要穿過內(nèi)核層。 下面給出編譯并運(yùn)行這兩個(gè)程序的操作過程: $gcc -o ciient1 clientl.c $gcc -o server1 server1.c 操作如下: $./server1 $

51、 ./client1 localhost get connected?。ɑ剀嚕?received=here is the first file of the presidents office3 客戶/服務(wù)器程序的編程實(shí)現(xiàn)流程實(shí)現(xiàn)流程 下圖是基本的面向連接的client/sever系統(tǒng)調(diào)用流程。 在客戶端編程的基本步驟是: 創(chuàng)建套接字;利用創(chuàng)建的套接字向服務(wù)器發(fā)出連接請(qǐng)求,啟動(dòng)tcp的三次握手;如果連接成功,雙方互操作開始。 服務(wù)器端通信軟件的步驟是:創(chuàng)建套接字;在建立的套接字中綁定本機(jī)ip及一個(gè)周知的端口號(hào);在該套接字進(jìn)行監(jiān)聽;如收到連接請(qǐng)求,則啟動(dòng)與客戶的連接;如果連接成功,雙方互操作開始

52、。 tcp服務(wù)器socket ( )數(shù)據(jù)(請(qǐng)求)服務(wù)請(qǐng)求tcp三次握手建立連接子進(jìn)程響應(yīng)服務(wù)bind ( )listen ( )accept ( )處理fork ( )派生子進(jìn)程send ( )tcp客戶socket ( )connect ( )recv ( )send ( )send ( )recv ( )close ( )父進(jìn)程數(shù)據(jù)(應(yīng)答)close ( )文件結(jié)束通知close ( )子進(jìn)程發(fā)現(xiàn)客戶已關(guān)閉3 tcp3 tcp連接的建立和終止過程連接的建立和終止過程 tcp傳輸控制協(xié)議提供客戶與服務(wù)器的連接。一個(gè)tcp客戶與一個(gè)tcp服務(wù)器可以建立一個(gè)可靠的連接,并通過這個(gè)連接通路與服務(wù)器

53、交換數(shù)據(jù),通信結(jié)束后終止連接。 (1) 建立tcp連接的過程 建立一個(gè)tcp連接,必須首先啟動(dòng)服務(wù)器。服務(wù)器程序通過調(diào)用函數(shù)socket()、bind()和listen(),事先做好接收客戶的連接請(qǐng)求的準(zhǔn)備。這個(gè)過程稱為被動(dòng)打開。稍后啟動(dòng)客戶機(jī)程序,它調(diào)用函數(shù)socket()創(chuàng)建一個(gè)用于連接的套接口,然后調(diào)用函數(shù)connect()執(zhí)行連接請(qǐng)求,從而引發(fā)了客戶機(jī)與服務(wù)器之間一個(gè)tcp連接的建立過程,調(diào)用函數(shù)connect()的過程稱為主動(dòng)打開。下圖給出了建立tcp連接的示意圖。 客戶機(jī)socket()accept()(阻塞)accept()(返回)socket()bind()listen()co

54、nnect()(阻塞)connect()(返回)read() (阻塞)服務(wù)器syn bsyn d ackb+1 ack d+1建立tcp連接的三次握手3 tcp3 tcp連接的建立和終止過程連接的建立和終止過程 建立一個(gè)tcp連接需要三路握手信號(hào): (l) 客戶機(jī)通過調(diào)用connect()執(zhí)行,首先向服務(wù)器發(fā)送一個(gè)syn(同步序列號(hào)標(biāo)志)分節(jié)。這里所說的分節(jié)是tcp傳遞給ip的數(shù)據(jù)單元。一個(gè)syn分節(jié)一般不攜帶數(shù)據(jù),它只含有一個(gè)ip頭部和一個(gè)tcp頭部,還含有一些tcp的選項(xiàng)。這些選項(xiàng)告訴服務(wù)器在本次連接中發(fā)送數(shù)據(jù)的初始序列號(hào)。圖1中給出的客戶機(jī)初始序列號(hào)是b。 (2) 服務(wù)器在收到客戶發(fā)來的

55、syn分節(jié)以后,必須給客戶機(jī)一個(gè)回答信號(hào),確認(rèn)客戶的syn分節(jié)。與此同時(shí),服務(wù)器也要向客戶機(jī)發(fā)送一個(gè)syn分節(jié),通知客戶機(jī)服務(wù)器將在這次連接中發(fā)送數(shù)據(jù)的初始序列號(hào)d。 (3) 在收到服務(wù)器發(fā)來的syn分節(jié)和回答信號(hào)后,客戶也必須回答確認(rèn)服務(wù)器的syn分節(jié)。 由于syn只占一個(gè)字節(jié)的序列號(hào)空間,所以客戶、服務(wù)器通信雙方的ack回答的確認(rèn)序列號(hào)都是由所接收到對(duì)方的序列號(hào)加1得到。2 tcp2 tcp連接的建立和終止過程連接的建立和終止過程 (2) 終止 tcp連接的過程用戶可以顯式地調(diào)用close()函數(shù)來關(guān)閉一個(gè)tcp連接。關(guān)閉一個(gè)tcp連接需要4個(gè)分節(jié)。 關(guān)閉連接也有主動(dòng)關(guān)閉和被動(dòng)關(guān)閉之分。先

56、調(diào)用close()函數(shù)的一端稱為主動(dòng)關(guān)閉。當(dāng)close執(zhí)行時(shí),發(fā)送一個(gè)fin(完成標(biāo)志)分節(jié),通知對(duì)方數(shù)據(jù)發(fā)送完畢。 fin是finish flap的縮寫,它也是tcp分節(jié)之一。 接收fin分節(jié)的另一端稱為被動(dòng)關(guān)閉。fin分節(jié)作為文件結(jié)束符由另一端的read()函數(shù)來接收。因?yàn)榻邮盏絝in分節(jié)就意味著另一端的進(jìn)程在此連接上再也不會(huì)接收到數(shù)據(jù)了,所以read()返回0。但這個(gè)fin分節(jié)是由被動(dòng)關(guān)閉一端的tcp來確認(rèn)。 read()函數(shù)返回后,應(yīng)用進(jìn)程也將調(diào)用close()函數(shù)來關(guān)閉相應(yīng)的套接口。close()函數(shù)執(zhí)行時(shí),也會(huì)向?qū)Ψ桨l(fā)送一個(gè)fin分節(jié),并由主動(dòng)關(guān)閉一端的tcp對(duì)其進(jìn)行確認(rèn)。圖2給出

57、關(guān)閉套接口時(shí)分組的交換過程,總共需要交換4個(gè)分組。客戶機(jī)close()close()read() (阻塞)服務(wù)器fin xack x-1 tcp關(guān)閉連接時(shí)的分組交換過程fin zack z-13 tcp3 tcp連接的建立和終止過程連接的建立和終止過程 從tcp連接的建立和終止過程可以看出,如果在tcp協(xié)議的一次通信中,只發(fā)送一個(gè)單一分節(jié)的請(qǐng)求和接收一個(gè)單一分節(jié)的應(yīng)答,則需要7個(gè)分節(jié)的額外開銷。如果使用udp,只有“請(qǐng)求”和“應(yīng)答”兩個(gè)分組需要交換,但是udp不提供可靠的數(shù)據(jù)包傳送,它把傳輸層的許多處理推給udp的應(yīng)用程序。盡管如此,還是有許多應(yīng)用程序使用udp協(xié)議,因?yàn)樗鼪]有tcp建立連接和

58、終止連接的額外開銷。 4 編制編制tcp客戶程序客戶程序 客戶機(jī)應(yīng)用程序有幾個(gè)特點(diǎn):首先,客戶程序一般不要求處理并發(fā)性,因?yàn)樵谕粫r(shí)刻一個(gè)客戶程序只能與一個(gè)服務(wù)器進(jìn)行交互。第二,客戶程序的執(zhí)行不需要特定的保護(hù),它和普通的應(yīng)用程序一樣執(zhí)行,不需要特權(quán)。鑒于以上兩點(diǎn),可知設(shè)計(jì)和編寫客戶程序比較容易。 (1) tcp(1) tcp客戶程序設(shè)計(jì)方法客戶程序設(shè)計(jì)方法 設(shè)計(jì)一個(gè)tcp客戶程序步驟如下: 1) 確定與之通信的服務(wù)器的ip地址和端口號(hào)。 2) 調(diào)用socket()函數(shù)創(chuàng)建一個(gè)tcp套接口。 3) 為這次連接分配一個(gè)在本地機(jī)器中的、未使用的協(xié)議端口號(hào)。 4) 調(diào)用connect()函數(shù)建立與服務(wù)

59、器的連接。 5) 用輸人和輸出函數(shù)進(jìn)行通信。 6) 調(diào)用close()關(guān)閉連接。 4 編制編制tcp客戶程序客戶程序 (2) tcp(2) tcp客戶程序的實(shí)現(xiàn)客戶程序的實(shí)現(xiàn) 以下給出了一個(gè)客戶程序的例子clientc,以便了解編制客戶程序應(yīng)該具有的基本語(yǔ)句和算法。在這個(gè)程序中,客戶建立與服務(wù)器的tcp連接并向服務(wù)器發(fā)送一條消息詢問當(dāng)前時(shí)間,然后調(diào)用read()函數(shù)等待讀取服務(wù)器送回的當(dāng)前時(shí)間和日期。 #include #include #include #include #include char * host_name = ; /* local host */int po

60、rt = 8000;void main(int argc, char *argv) char buf8192, message256; int sockfd, n; struct sockaddr_in pin; struct hostent *shost_name; if (shost_name = gethostbyname(host_name) = 0) /*域名轉(zhuǎn)換成主機(jī)地址*/ perror(error resolving local hostn); exit(1); /* 首先,客戶機(jī)必須知道服務(wù)器主機(jī)首先,客戶機(jī)必須知道服務(wù)器主機(jī)ip的地址,如果使用同一臺(tái)計(jì)算機(jī)作為的地址,如果使用同一臺(tái)計(jì)算機(jī)作為服務(wù)器運(yùn)行,則本地計(jì)算機(jī)的標(biāo)準(zhǔn)服務(wù)器運(yùn)行

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝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ù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 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ì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論