|
面向無連接的協(xié)議
直到這時,我們實際了忽略了套接口通信的大部分內(nèi)容。相反,我們關(guān)注于創(chuàng)建套接口,綁定地址以及關(guān)閉套接口?,F(xiàn)在我們要實際使用套接口了。 對于套接口有兩種基本的通信模式。他們是面向無連接的通信與面向連接的通信。 在這一章,我們將會了解下面內(nèi)容: 面向無連接通信與面向連接通信之間的區(qū)別 如何執(zhí)行無連接的輸入與輸出操作 如何編寫一個數(shù)據(jù)報服務(wù)器 如何編寫一個數(shù)據(jù)報客戶端 現(xiàn)在我們來關(guān)注一下面向無連接通信與面向連接通信之間的區(qū)別。 通信方法 正如我們所想到的,面向無連接的通信在通信開始之前并不需要建立連接。這就像一個拿著護(hù)音器在嘈雜中向一個人喊話一樣。對于每一次新的喊話,發(fā)送消息的人可以將他的話傳遞到另一個人,而不需要先前的同意。 相類似的,在我們創(chuàng)建一個無連接的套接口以后,我們可以向?qū)邮瘴覀兿⒌娜魏翁捉涌诎l(fā)送消息。此時并沒有建立連接,而每一個消息可以直接發(fā)送給一個不同的接收套接口。 理解其優(yōu)點 面向無連接的通信與面向連接的通信相比提供了一些優(yōu)點。這些優(yōu)點包括: 簡單:不需要建立連接 靈活:可以將消息發(fā)送到不同的帶有消息發(fā)送操作的接收端 高效:不要求連接的建立與關(guān)閉,而這需要向網(wǎng)絡(luò)添加大量的消息包 快速:因為不需要連接的建立也關(guān)閉,而只是發(fā)送消息本身 廣播功能:將一個消息發(fā)送到多個接收端的功能 一個面向無連接的協(xié)議的許多優(yōu)點都與高效與速度有關(guān)。為了建立連接,需要在數(shù)據(jù)交換之前在兩個端點之間交換大量的數(shù)據(jù)包。而一個已建立的通信頻道的關(guān)閉則需要額外的數(shù)據(jù)包交換。而這產(chǎn)生額外的時間花費。 無連接協(xié)議還有一個優(yōu)點就是廣播功能。他可以將一個消息發(fā)送到多個接收端。這高效的利用了網(wǎng)絡(luò)帶度。 在這一章,我們將會學(xué)習(xí)UDP協(xié)議(用戶數(shù)據(jù)報協(xié)議),這是一個面向無連接的協(xié)議。這個協(xié)議充分利用了我們前面所談到的所有優(yōu)點。 理解無連接通信的缺點 既然無連接的協(xié)議可以提供這些優(yōu)點,我們也許想要知道為什么不總是使用這種協(xié)議。任何事物,即使是無連接的通信也有他的缺點。 UDP協(xié)議是一個非常簡單的傳輸層協(xié)議,而且他是無連接的。對于UDP協(xié)議,存在著下面的缺點: 協(xié)議不可靠 沒有多個數(shù)據(jù)報的序列 消息尺寸存在限制 可 靠性問題對于大多數(shù)程序來說是最嚴(yán)重的限制。我們的程序可以將UDP數(shù)據(jù)報發(fā)送到網(wǎng)絡(luò),但是并不能保證接收端可以接收到。這個消息也許被丟棄,而沒有同一 網(wǎng)絡(luò)的接收端所接收。相應(yīng)的,由于網(wǎng)絡(luò)路徑中多個路由器中的一個不能接收沒有檢驗碼錯誤的消息,所發(fā)送的消息也會丟失。當(dāng)一個數(shù)據(jù)包接收出錯時,UDP包 只是簡單的丟棄并且永遠(yuǎn)的丟失。 其他的問題也會造成UDP數(shù)據(jù)包的丟失。如果接收主機(jī)或是路由器不能分配足夠的緩沖空間來存放UDP數(shù)據(jù)包,這些數(shù)據(jù)包也會丟失。因此,如果我們的UDP數(shù)所包需要傳輸很長的距離,這些數(shù)據(jù)包就很有可能沿途丟失。 在 我們決定為我們的程序使用UDP協(xié)議之前,還有另外一個問題需要考慮。如果我們成功的向我們的目的地發(fā)送了兩個或是多個數(shù)據(jù)報,很有可能這些數(shù)據(jù)報并不是 順序接收的。UDP協(xié)議使用IP協(xié)議來傳輸最終的數(shù)據(jù)報。IP數(shù)據(jù)包可以由每一個傳輸進(jìn)行不同的路由,而路由是依據(jù)當(dāng)前的網(wǎng)絡(luò)環(huán)境而變化的。這通常會造成 一些數(shù)據(jù)包在另一些數(shù)據(jù)包之前到達(dá)目的地。也就說UDP數(shù)據(jù)包會并不會按順序到達(dá)。 最后存在數(shù)據(jù)報尺寸的問題。理論上一個數(shù)據(jù)報的最大長 度要小于64KB。然而,許多UNIX主機(jī)所支持的最大長度僅為32KB。其他的UNIX內(nèi)核內(nèi)建的尺寸限制更小,只有8KB。最后,接收套接口程序會將 這個尺寸限制為接收緩沖區(qū)的尺寸。因為這個原因,一些程序?qū)DP的消息尺寸限制為512字節(jié)或是更小。 如果發(fā)送了一個大的UDP數(shù)據(jù) 包,則必須將這個數(shù)據(jù)包分解為一些小的IP片段,然后在接收端重新進(jìn)行組合。組合過程需要分配緩沖區(qū)來存放接收到的IP片段。這通常會指定一個超時時限, 直到整個數(shù)據(jù)包重新進(jìn)行組合。緩沖區(qū)的完整會造成重組的無約束,而這會造成我們的UDP數(shù)據(jù)包的丟失。UDP數(shù)據(jù)報在成功的到達(dá)目的地之前必須要經(jīng)歷些風(fēng) 險。 對于一些程序來說,這會將使得我們轉(zhuǎn)向更為可靠的面向連接的協(xié)議,例如TCP/IP。這些于其他的程序來說并不是嚴(yán)重的限制。當(dāng)然只有我們來決定UDP是否適用我們的程序。 執(zhí)行數(shù)據(jù)報輸入/輸出操作 在 前面的介紹中,我們看到了當(dāng)我們要從套接口中讀取或是向套接口中寫入時,我們使用了read(2)和write(2)函數(shù)。在當(dāng)時我們并沒有指現(xiàn), socketpair(2)函數(shù)使用了面向連接的協(xié)議來創(chuàng)建一對套接口。相應(yīng)的,我們可以使用我們所熟悉的read(2)與write(2)函數(shù)來在這些 套接口上執(zhí)行I/O操作。 然而,當(dāng)要發(fā)送與接收數(shù)據(jù)報時,需要一對不同的函數(shù)。這是因為每一個要發(fā)送的消息實際上要發(fā)送到一個不同的目的 地址。發(fā)送數(shù)據(jù)報的函數(shù)允許我們指定目的接收端的地址。同樣,當(dāng)我們要接收一個數(shù)據(jù)報時,我們也需要指明他的來源。這些新函數(shù)必須為我們提供一個合適的方 法來確定發(fā)送者的地址。 簡介sendto(2)函數(shù) sendto(2)函數(shù)允許我們寫入一個數(shù)據(jù)報,同時指定目的接收端的地址。函數(shù)概要如下: #include <sys/types.h> #include <sys/socket.h> int sendto(int s, const void *msg, int len, unsigned flags, const struct sockaddr *to, int tolen); 這些參數(shù)描述如下: 1 第一個參數(shù)s為套接口號。我們可以從socket函數(shù)得到這個值。 2 參數(shù)msg為指向存放我們將要發(fā)送的消息的緩沖區(qū)的指針 3 參數(shù)len為字節(jié)形式長度 4 flags允放我們指定一些選項位。在許多情況下,我們只需指定為0 5 參數(shù)to為指向我們已經(jīng)建立的通用套接口的指針。這是數(shù)據(jù)報接收端的地址 6 參數(shù)tolen為參數(shù)to地址的長度 當(dāng)sendto函數(shù)執(zhí)行成功時,會返回寫入的字節(jié)數(shù)。當(dāng)發(fā)生錯誤時,則會返回-1,同時錯誤號會記錄在errno中。 參數(shù)to指定了數(shù)據(jù)報將要發(fā)送到的地址。參數(shù)to必須指向一個可用的套接口地址,而參數(shù)tolen應(yīng)包含地址的正確長度。我們已經(jīng)在前面的章節(jié)中成為格式化地址的專家,所以在這里我們應(yīng)感到得心應(yīng)手了。 flags可用值如下所示,當(dāng)然在大多數(shù)時候我們都將其指定為0。 標(biāo)志 十六進(jìn)制 意義 0 0x0000 普通-沒有選項 MSG_OOB 0x0001 處理超過邊界的數(shù)據(jù) MSG_DONTROUTE 0x0004 旁路路由,使用直接接口 MSG_DONTWAIT 0x0040 不緩沖,直接寫入 MSG_NOSIGNAL 0x4000 當(dāng)端點斷開時不發(fā)送信號 簡介recvfrom(2)函數(shù) 與sendto函數(shù)相對就是recvfrom函數(shù)。這個函數(shù)與read函數(shù)的不同就在于他允許我們同時指定我們要接收的數(shù)據(jù)報的發(fā)送者的地址。函數(shù)概要如下: #include <sys/types.h> #include <sys/socket.h> int recvfrom(int s, void *buf, int len, unsigned flags, struct sockaddr *from, int *fromlen); 參數(shù)描述如下: 1 套接口s指定要從中接收數(shù)據(jù)報的套接口 2 buf指向開始接收數(shù)據(jù)報的緩沖區(qū) 3 最大長度len指定了buf的長度 4 選項標(biāo)志flags 5 指向接收套接口的地址緩沖,這將會接收發(fā)送者的地址 6 指向最大長度fromlen的指針 與正常的read函數(shù)相類似,接收緩沖區(qū)buf必須足夠大來接收數(shù)據(jù)報。最大長度是通過len指定的。 如果函數(shù)執(zhí)行失敗則會返回-1,而錯誤號則會存放在errno中。否則,函數(shù)將會返回實際接收到的字節(jié)數(shù)。這將是我們接收到的數(shù)據(jù)報的尺寸。 然而在這里要特別注意的是,最后一個參數(shù)是一個指向接收地址結(jié)構(gòu)長度的指針。指針?biāo)赶虻膇nt值必須包含接收地址結(jié)構(gòu)from的最大字節(jié)尺寸。一旦由函數(shù)返回,返回的地址尺寸放置在int變量中。實際上,由fromlen所指向的值同時作為輸入值和返回值。 可用的標(biāo)志值如下所示: 標(biāo)志 十六進(jìn)制 意義 0 0x0000 普通 MSG_OOB 0x0001 處理超過邊界的數(shù)據(jù) MSG_PEEK 0x0002 讀取一個數(shù)據(jù)報而不從內(nèi)核接收隊列中刪除 MSG_WAITALL 0x0100 請求操作塊直到全部請求都已滿足 MSG_ERRQUEUE 0x2000 從錯誤隊列獲取一個數(shù)據(jù)包 MSG_NOSIGNAL 0x4000 當(dāng)另一端斷開連接時為流套接口關(guān)閉信號 編寫一個UDP數(shù)據(jù)報服務(wù)器 現(xiàn) 在我們已經(jīng)有足夠的知識來編寫一個數(shù)據(jù)報客戶端與服務(wù)器程序了。在這一部分,我們將從編寫一個數(shù)據(jù)報服務(wù)的例子開始。這個程序接受一個strftime (3)格式字符串作為輸入并且返回格式化的日期與時間字符串作為響應(yīng)。在這一節(jié)我們將會編寫一個數(shù)據(jù)報服務(wù)器,他可以獨立運行,接受格式化字符串作為輸入 數(shù)據(jù)報。在服務(wù)器使用strftime函數(shù)格式化日期字符串之后,他會將結(jié)果返回給客戶端程序。 /* * dgramsrvr.c * * Example datagram server: */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <time.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> /* * this function reports the error and * exits back to the shell: */ static void bail(const char *on_what) { fputs(strerror(errno),stderr); fputs(": ",stderr); fputs(on_what,stderr); fputs("\n",stderr); exit(1); } int main(int argc,char **argv) { int z; char *srvr_addr = NULL; struct sockaddr_in adr_inet; /* AF_INET */ struct sockaddr_in adr_clnt; /* AF_INET */ int len_inet; /* length */ int s; /* socket */ char dgram[512]; /* recv buffer */ char dtfmt[512]; /* date/time result */ time_t td; /* current time and date */ struct tm tm; /* date time values */ /* * use a server address from the command * line,if one has been provided. * otherwise,this program will default * to using the arbitrary address * 127.0.0.23: */ if(argc >=2) { /* addr on cmdline */ srvr_addr = argv[1]; } else { /* use default address */ srvr_addr = "127.0.0.23"; } /* * create a UDP socket to use: */ s = socket(AF_INET,SOCK_DGRAM,0); if(s==-1) bail("socket()"); /* * create a socket address,for use * with bind: */ memset(&adr_inet,0,sizeof adr_inet); adr_inet.sin_family = AF_INET; adr_inet.sin_port = htons(9090); adr_inet.sin_addr.s_addr = inet_addr(srvr_addr); if(adr_inet.sin_addr.s_addr == INADDR_NONE) bail("bad address"); len_inet = sizeof adr_inet; /* * bind a address to our socket,so that * client program can contact this * server: */ z = bind(s,(struct sockaddr *)&adr_inet,len_inet); if(z==-1) bail("bind()"); /* * now wait for requests: */ for(;;) { /* * block until the program receives a * datagram at our address an port: */ len_inet = sizeof adr_clnt; z = recvfrom(s, /* socket */ dgram, /* receiveing buffer */ sizeof dgram, /* max recv buf size */ 0, /* flags */ (struct sockaddr *)&adr_clnt, /* addr */ &len_inet); /* addr len,in & out */ if(z<0) bail("recvfrom()"); /* * process the request: */ if(!strcasecmp(dgram,"QUIT")) break; /* quit server */ /* * get the current date and time: */ time(&td); /* get current time & date */ tm = *localtime(&td); /* get componets */ /* * formate a new date and time string, * based upon the input formate string: */ strftime(dtfmt, /* formatted result */ sizeof dtfmt, /* max result size */ dgram, /* input date/time format */ &tm); /* input date/time values */ /* * send the formatted result back to the * client program: */ z = sendto(s, /* socket to send result */ dtfmt, /* the datagram result to send */ strlen(dtfmt), /* the datagram length */ 0, /* flags */ (struct sockaddr *)&adr_clnt, /* addr */ len_inet); /* client address length */ if(z<0) bail("sendto()"); } /* * close the socket and exit: */ close(s); return 0; } 編寫一個UDP數(shù)據(jù)報客戶端 為了使用我們在前面所編寫的數(shù)據(jù)報服務(wù)程序,我們需要一個數(shù)據(jù)報客戶端程序。下面例子所提供的數(shù)據(jù)報客戶端可以提示我們?yōu)閟trftime輸入格式化文本。 /* * dgramclnt.c * * Example datagram client: */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <time.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> /* * This function reports the error and * exits back to the shell: */ static void bail(const char *on_what) { fputs(strerror(errno),stderr); fputs(": ",stderr); fputs(on_what,stderr); fputs("\n",stderr); exit(1); } int main(int argc,char **argv) { int z; int x; char *srvr_addr = NULL; struct sockaddr_in adr_srvr; /* AF_INET */ struct sockaddr_in adr; /* AF_INET */ int len_inet; /* length */ int s; /* socket */ char dgram[512]; /* recv buffer */ /* * use a server address from the command * line,if one has been provided. * otherwise,this programe will default * to using the arbitrary address * 127.0.0.23: */ if(argc>=2) { /* addr on command line: */ srvr_addr = argv[1]; } else { srvr_addr = "127.0.0.23"; } /* * create a socket address,to use * to contact the server with: */ memset(&adr_srvr,0,sizeof adr_srvr); adr_srvr.sin_family = AF_INET; adr_srvr.sin_port = htons(9090); adr_srvr.sin_addr.s_addr = inet_addr(srvr_addr); if(adr_srvr.sin_addr.s_addr == INADDR_NONE) bail("bad address"); len_inet = sizeof adr_srvr; /* * create a UDP socket to use: */ s = socket(AF_INET,SOCK_DGRAM,0); if(s==-1) bail("socket()"); for(;;) { /* * prompt user for a date formate string: */ fputs("\nEnter format string: ",stdout); if(!fgets(dgram,sizeof dgram,stdin)) break; /* EOF */ /* * send format string to server: */ z = sendto(s, /* socket to send result */ dgram, /* the datagram result to snd */ strlen(dgram), /* the datagram length */ 0, /* flags */ (struct sockaddr *)&adr_srvr, /* addr */ len_inet); /* server address length */ if(z<0) bail("sendto()"); /* * test if we asked for a server shutdown: */ if(!strncmp(dgram,"QUIT",strlen(dgram)-1)) break; /* yes,we quit too */ /* * wait for a response: */ x = sizeof adr; z = recvfrom(s, /* socket */ dgram, /* receiving buffer */ sizeof dgram, /* max recv buf size */ 0, /* flags */ (struct sockaddr *)&adr, /* addr */ &x); /* addr len,in & out */ if(z<0) bail("recvfrom()"); dgram[z]=0; /* * report result */ printf("Result from %s port %u:\n\t‘%s‘\n", inet_ntoa(adr.sin_addr), (unsigned)ntohs(adr.sin_port), dgram); } /* * close the socket and exit: */ close(s); putchar(‘\n‘); return 0; } 測試數(shù)據(jù)報客戶端與服務(wù)器 我們將會在這里進(jìn)行的第一個測試適用于任何人,而無論你是否有可用的網(wǎng)絡(luò)或是獨立的PC。唯一的關(guān)鍵點就是我們需要將TCP/IP支持編譯進(jìn)入內(nèi)核。如果我們正在運行一個獨立的發(fā)行版本,例如RH系列,那么我們已經(jīng)滿足這些條件了。 要執(zhí)行這個測試,我們需要執(zhí)行下面的步驟: 1 啟動服務(wù)器程序 2 啟動客戶端程序 3 輸入客戶程序輸入 4 輸入C-D來結(jié)束程序,或者是使用QUIT來退出程序。 啟動服務(wù)器程序的輸出結(jié)果如下: $ ./dgramsrvr & [1] 4405 $ 字符&將服務(wù)器程序放置在后臺運行,這樣我們就可以繼續(xù)使用當(dāng)前的終端來運行客戶端程序。 服務(wù)器程序啟動運行以后,我們就可以使用客戶程序來進(jìn)行測試了。下面顯示如何啟動客戶程序并進(jìn)行測試: $ ./dgramclnt Enter format string: %D Result from 127.0.0.23 port 9090 : 08/13/99‘ Enter format string: %A %D %H:%M:%S Result from 127.0.0.23 port 9090 : Friday 08/13/99 22:14:02‘ Enter format string: quit [1]+ Done ./dgramsrvr $ 測試無服務(wù)器的情況 下面的輸出顯示沒有服務(wù)器運行時運行客戶程序的輸出結(jié)果: $ ./dgramclnt Enter format string: %D Connection refused: recvfrom(2) $ 在這里我們可以看到客戶程序可以運行,并且可以創(chuàng)建套接口,要求輸入。甚至sendto函數(shù)也報告執(zhí)行成功。這就進(jìn)一步確認(rèn)了數(shù)據(jù)報的發(fā)送只是確保發(fā)送成功,而不確保接收成功。 在這種情況下,程序很幸運的得到了錯誤號來表明錯誤原因 。錯誤標(biāo)識是通過recvfrom函數(shù)調(diào)用得到的。當(dāng)客戶與服務(wù)器程序獨立運行在一個大的網(wǎng)絡(luò)并有多個路由的情況下,也許就不會得到這個錯誤號。 在實際使用中,如果另一端并沒有監(jiān)聽,我們不能依賴于得到錯誤碼。因為這個原因,UDP程序通常包含定時器的使用,如果在一定的時間內(nèi)沒有收到響應(yīng)則認(rèn)為沒有建立連接。 使用其他的IP進(jìn)行測試 在 前一節(jié)我們談到可以在命令行指定IP地址。如果我們設(shè)置了我們自己的網(wǎng)絡(luò),我們可以試著在不同的主機(jī)上運行客戶端與服務(wù)器程序。在下一個例子中,服務(wù)器程 序運行在192.168.0.1的主機(jī)上,而客戶端程序運行192.168.0.2的主機(jī)上。服務(wù)器程序的啟動如下所示: $ ./dgramsrvr 192.168.0.1 & [1] 4416 $ 服務(wù)器成功啟動以后,就可以在另一個主機(jī)上調(diào)用客戶端程序??蛻舳说妮敵鼋Y(jié)果如下所示: $ ./dgramclnt 192.168.0.1 Enter format string: %D Result from 192.168.0.1 port 9090 : ‘08/13/99‘ Enter format string: %A (%D) Result from 192.168.0.1 port 9090 : ‘Friday (08/13/99)‘ Enter format string: QUIT $ 正如輸出結(jié)果所示,通過在命令行指定地址通知客戶程序服務(wù)器位于192.168.0.1主機(jī)上。在試驗了兩個例子以后,輸入QUIT命令退出。 盡管這兩個例子可以正確的運行,但是我們要認(rèn)識到UDP是不可靠的。如果客戶端程序不能由服務(wù)器得到響應(yīng),程序就會掛起。如果我們正在編寫一個產(chǎn)品模型程序,我們需要提供超時代碼。當(dāng)原始的或是響應(yīng)的數(shù)據(jù)報丟失時,這會允許程序修復(fù)響應(yīng)缺失。 在客戶程序中保留bind 一些讀者也許已經(jīng)注意到在上個例子中所創(chuàng)建的套接口的客戶端程序并沒有調(diào)用bind函數(shù)。如果bind函數(shù)調(diào)用可以消除,那么為什么還要使用呢? 我 們也許還記得在前面的章節(jié)中,“綁定地址到套接口”,其中有一節(jié)名為“接口與地址”的部分,在其中解釋了bind函數(shù)可用于限制用于執(zhí)行通信的接口。其中 的例子是一個防火墻程序,他只與信任的內(nèi)部網(wǎng)絡(luò)進(jìn)行通信。如果現(xiàn)在這些對于我們來說有一些茫然,那么我們需要返回來查閱bind的相關(guān)內(nèi)容。 而 在我們的數(shù)據(jù)報例子程序中,bind函數(shù)調(diào)用被忽略了。這對于發(fā)送套接口有什么影響呢?正如我們在第五章所了解的,這實際表明這個程序可以接受任何流出的 接口,這正是數(shù)據(jù)報到其目的地的路由所要求的。實際上,套接口據(jù)說有一個寬套接口地址。然后,當(dāng)這個程序等待響應(yīng)時,他也會接受從任何流入接口得到的輸入 數(shù)據(jù)報。在這里也要注意套接口端口號也是寬的。在這個特定的程序中,任何客戶端口號都是可以接受的。 我們可以顯式的使用bind函數(shù)來請 求一個寬地址和端口。可以使用寬地址INADDR_NONE來完成這個工作。要請求一個寬端口號,可以將端口號指定為0。通過組合IP地址的 INADDR_NONE和0端口號,我們已經(jīng)請求bind顯式的為我們提供一個寬地址,而這是與我們沒有使用bind調(diào)用而得到的寬地址相同。 回應(yīng)寬地址 如果客戶程序的地址和端口號是寬的,我們也許想要知道服務(wù)器如何與回應(yīng)特定的端口。畢竟,我們應(yīng)如何縮寫向沒有一個特定IP地址和UDP端口號的客戶程序返回響應(yīng)? 問 題的答案就在于:實際上當(dāng)數(shù)據(jù)報發(fā)送時,就同時賦值了IP地址和端口號。我們前面的例子運行在IP地址為192.168.0.2的主機(jī)上。當(dāng)客戶程序調(diào)用 sendto函數(shù)時,數(shù)據(jù)報知道將會發(fā)送到IP地址為192.168.0.1的主機(jī)上。路由表指明IP地址為192.168.0.2的以太網(wǎng)卡將會用于發(fā) 送數(shù)據(jù)報。相應(yīng)的,發(fā)送的數(shù)據(jù)報有一個192.168.0.2的from地址。這是在服務(wù)器端看到的地址。然而,端口號是寬的,并且將會所選擇的IP地址 選擇任意的自由端口號。 如果另一個數(shù)據(jù)報發(fā)送到一個不同的網(wǎng)絡(luò),那么from IP地址將會是不同的值。from地址返應(yīng)了用于發(fā)送數(shù)據(jù)報的網(wǎng)絡(luò)接口的IP地址。 這是需要理解的一個重要概念,而且也許對于初學(xué)者來說也是最難理解的事情。如果我們現(xiàn)在并不能完全的理解這些內(nèi)容,那么我們就需要回顧一下第五章的內(nèi)容。作為練習(xí),我們可以在我們的例子程序中加入下面的printf語句: printf("Client from %s port %u;\n", inet_ntoa(adr_clnt.sin_addr), (unsigned)ntohs(adr_clnt.sin_port)); 編譯以后重新運行程序,程序的運行結(jié)果如下: $ ./dgramsrvr & [1] 733 $ ./dgramclnt Enter format string: %D Client from 127.0.0.23 port 1027; Result from 127.0.0.23 port 9090 : ‘08/15/99‘ Enter format string: %A %D Client from 127.0.0.23 port 1027; Result from 127.0.0.23 port 9090 : ‘Sunday 08/15/99‘ Enter format string: QUIT Client from 127.0.0.23 port 1027; [1]+ Done ./dgramsrvr $ 在這里我們可以注意到所有的數(shù)據(jù)報發(fā)送到服務(wù)器,而數(shù)據(jù)報的from地址報告如下: Client from 127.0.0.23 port 1027; 這再一次驗證了當(dāng)在客戶端發(fā)送套接口沒有使用bind函數(shù)時,會依據(jù)需求賦值合適的IP地址和最終的端口號。 Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1676892
[收藏到我的網(wǎng)摘] mylxiaoyi發(fā)表于 2007年07月03日 16:20:00
|
|
|