《實戰(zhàn) Linux Socket編程》讀書筆記.doc_第1頁
《實戰(zhàn) Linux Socket編程》讀書筆記.doc_第2頁
《實戰(zhàn) Linux Socket編程》讀書筆記.doc_第3頁
《實戰(zhàn) Linux Socket編程》讀書筆記.doc_第4頁
《實戰(zhàn) Linux Socket編程》讀書筆記.doc_第5頁
已閱讀5頁,還剩49頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

書名:實戰(zhàn) Linux Socket編程作者:Warren W.Gay翻譯:詹俊鵠 于衛(wèi)出版:西安電子科技大學(xué)出版社第一章 套接口簡介1、套接口是通信中的一個端點,套接口創(chuàng)建后,就如同一個文件描述符,可以使用文件的I/O函數(shù)對它進行讀、寫和關(guān)閉操作。2、套接口和已打開的文件之間存在如下差別:l 不能在套接口上調(diào)用函數(shù)lseek(這個限制也適用于管道)l 套接口可以和網(wǎng)絡(luò)地址關(guān)聯(lián),文件和管道卻不能l 套接口具有很多能夠通過ioctl進行查詢和設(shè)置的選項l 套接口必須在正確的狀態(tài)下才能實現(xiàn)輸入和輸出,而已打開文件在任何時候都可以進行讀或?qū)懖僮鳌?、套接口和管道不同,它允許進行進行雙向通信,即可以使用同一個套接口描述符進行讀和寫【代碼test1.2.c】。4、創(chuàng)建套接口#include #include int socketpair ( int domain, int type, int protocol, int sv2);domain:套接口的域名,表示使用哪個地址族,要么是AF_LOCAL要么是AF_UNIX。地址族的作用是指明使用哪一種地址類型。AF_LOCAL(AF_UNIX)表示使用本地地址規(guī)則來生成地址,而AF_INET則表示使用IP地址規(guī)則生成地址。type:套接口的類型,可選值為SOCK_STREAM、SOCK_DGRAMprotocol:使用的協(xié)議。一般情況下為0,這樣可以使用適合所選domain的正確缺省協(xié)議。sv:套接口文件描述符數(shù)組。每個整型值代表一個套接口,類似于管道中的某一端的端點。如果函數(shù)調(diào)用成功,返回0,否則返回-1,錯誤值存放在errno中。5、socketpair用例請參看【代碼test1.1.c】6、可以對套接口調(diào)用read、write、close 等函數(shù),請參看【代碼test1.2.c】7、使用close關(guān)閉端口,接收端會收到文件結(jié)束符標(biāo)志,close是全關(guān)閉,也就是說關(guān)閉了套接口后,既不能讀也不能寫。shutdown半關(guān)閉,也就是說不能寫,但是可以使用該套接口從讀取對端發(fā)送的數(shù)據(jù)。#include int shutdown (int s, int how);how 有三個可選值:SHUT_RD、SHUT_WR、SHUT_RDWR如果函數(shù)調(diào)用成功,返回0,否則返回-1,錯誤值存放在errno中。僅對套接口的寫端進行關(guān)閉,可以解決如下問題:l 內(nèi)核緩沖區(qū)中任何將要發(fā)送的數(shù)據(jù)都將作廢l 向遠程套接口發(fā)送文件結(jié)束標(biāo)志,告訴對方進程,不再會向?qū)Ψ桨l(fā)送數(shù)據(jù)l 本地半關(guān)閉的套接口仍然可以進行讀操作l 不管套接口的訪問記數(shù)為多少,shutdown都將訪問數(shù)清0。如果使用dup或dup2復(fù)制了套接口描述符,則只有所有的描述符都被close才能關(guān)閉套接口,而shutdown則能夠一次性關(guān)閉復(fù)制的所有套接口。因此不論是要全關(guān)閉或半關(guān)閉套接口,都應(yīng)該使用shutdown,而不是close。然而shutdown不會釋放文件描述符,因此shutdown執(zhí)行后,還應(yīng)該調(diào)用close釋放文件描述符。8、當(dāng)調(diào)用fork生成子進程時,任何在fork操作之前存在的套接口都將復(fù)制到子進程中【代碼test1.2.c】。9、關(guān)閉從套接口的讀入將忽視任何等待讀入的數(shù)據(jù),如果有更多的數(shù)據(jù)從遠程端發(fā)送過來,也將同樣被忽視掉,如果這時進程嘗試從套接口進行讀入,就會發(fā)生錯誤。第二章 域和地址族1、套接口不一定需要地址,比如函數(shù)socketpair就生成了一對相互連接但是沒有地址的套接口,這就是所謂的無名套接口。2、匿名調(diào)用:在相互連接的兩個套接口中有一個套接口不需要地址,例如連接到一個遠程套接口時,只要確定遠程套接口的地址,但是發(fā)出調(diào)用的本地套接口可以是匿名的。此時本地套接口不需要bind3、socktpair 和 socket 函數(shù)都允許使用其他協(xié)議,而不只是TCP/IP 協(xié)議。4、int socketpair ( int domain, int type, int protocol, int sv2);domain:套接口的域名,表示使用哪個地址族,要么是AF_LOCAL要么是AF_UNIX。地址族的作用是指明使用哪一種地址類型。AF_LOCAL(AF_UNIX)表示使用本地地址規(guī)則來生成地址,而AF_INET則表示使用IP地址規(guī)則生成地址(IPv4等)一般情況下,protocol參數(shù)的值為0,這使操作系統(tǒng)可以選擇適合所選domain的正確的缺省協(xié)議5、通用地址結(jié)構(gòu)#include struct socketaddrsa_family_t sa_family; /*地址族類型*/char sa_data14; /*地址數(shù)據(jù)*/所有的地址都要在結(jié)構(gòu)中同樣的位置定義 sa_family成員,因為它決定了怎樣翻譯結(jié)構(gòu)中的包含地址信息的字節(jié)。6、生成本地地址例如使用本地的打印服務(wù),則可以使用本地套接口(本地地址)與打印服務(wù)通信,而不需要使用TCP/IP進行通信。本地套接口往往被稱為AF_UNIX域,因為這些地址使用UNIX文件名作為地址名。對應(yīng)于AF_LOCAL和AF_UNIX地址的結(jié)構(gòu)名是sockaddr_un,而對應(yīng)于IP地址的結(jié)構(gòu)名是sockaddr_in#include struct sockaddr_unsa_family_t sun_family; /*地址族*/char sun_path108; /*路徑名*/sun_family:只能選為AF_LOCAL或AF_UNIX。sun_path:包含一個有效的UNIX路徑名,這個字符串的末尾不需要一個空字符。l 【傳統(tǒng)本地地址】的命名空間就是文件系統(tǒng)的路徑名,一個進程可以使用任何有效的路徑名來命名本地套接口路徑,這里的有效指的是該進程可以訪問這個路徑名下的所有目錄,并有權(quán)在該路徑下生成套接口?!敬a】sockaddr_un soc;soc.sun_family = AF_UNIX;strcpy(soc.sun_path, ”/tmp/my_socket”);bind( s, (struct sockaddr *)&soc, SUN_LEN(&soc);l 生成【本地抽象地址】,只需把路徑名的第一個字節(jié)設(shè)置為空字節(jié),而空字節(jié)后的其他部分是抽象名。對本地套接口生成抽象地址,從而避免生成文件系統(tǒng)對象?!敬a】sockaddr_un soc;soc.sun_family = AF_UNIX;strcpy(soc.sun_path,”ZvirtualName”);soc.sun_path0=0;這里的Z是占位符(可以用任何一個字符作占位符),重要的是,需要在后面代碼中將路徑名的第一個字節(jié)設(shè)置為空字節(jié)。7、生成Internet(IPv4)套接口地址#include struct sockaddr_insa_family_t sin_family /*地址族,初始化為 AF_INET*/uint16_t sin_port; /*端口號,網(wǎng)絡(luò)字節(jié)序*/struct in_addr sin_addr; /*Internet 地址,IP地址,網(wǎng)絡(luò)字節(jié)序*/unsigned char sin_zero8 /*占位字符,不使用,用于使整個結(jié)構(gòu)體以16字節(jié)對齊*/struct in_addr uint32_t s_addr;8、網(wǎng)絡(luò)字節(jié)序小端:低序字節(jié)存儲在低地址大端:高字節(jié)放在低地址。例如 0x 1234小端(Intel CPU):低地址(0x34)高地址(0x12)大端(網(wǎng)絡(luò)字節(jié)序):低地址(0x12)高地址(0x34)現(xiàn)在網(wǎng)絡(luò)上傳輸數(shù)據(jù)的標(biāo)準(zhǔn)順序為大端字節(jié)序,使用下面的函數(shù)可以在主機字節(jié)序和網(wǎng)絡(luò)字節(jié)序之間轉(zhuǎn)換:#include unsigned long htonl (unsigned long hostlong);unsigned short htons (unsigned short hostshort);unsigned long ntohl(unsigned long netlong);unsigned short ntohs(unsigned short netshort);9、初始化一個通配的Internet地址使用通配地址,是因為有的主機安裝了多塊網(wǎng)卡,每塊網(wǎng)卡有獨立的IP地址,而且Linux允許為一塊網(wǎng)卡指定不同的IP地址,如果我們指定通配地址,則系統(tǒng)會自動選擇一條通往遠程服務(wù)的路由,當(dāng)連接建立時,內(nèi)核最終將決定使用哪一個本地的套接口地址。當(dāng)想內(nèi)核自動分配本地端口號時,也可以使用通配的端口號,要使用這一點很簡單,只需要將sinp_port 的值賦值為0?!敬a】struct sockaddr_in addr_inet;memset(&addr_inet, 0, sizeof(sockaddr_in);addr_inet.sin_family = AF_INET;addr_inet.sin_port = ntohs(0);addr_inet.sin_addr.s_addr = ntohl(INADDR_ANY); / 通配的IP地址10、初始化一個特定的Internet地址【代碼】struct sockaddr_in addr_inet;const unsigned char IPno=192, 168, 1, 100;memset(&addr_inet, 0, sizeof(sockaddr_in);addr_inet.sin_family = AF_INET;addr_inet.sin_port = ntohs(9000);memcpy( &addr_inet.sin_addr.s_addr, IPno, 4); / 通配的IP地址11、生成X.25地址用于定義X.25協(xié)議地址的結(jié)構(gòu)是 sockaddr_x25#include struct sockaddr_x25sa_family_t sx25_family; /*只能是AF_X25*/x25_address sx25_addr; /*X.25 地址*/typedef struct char x25_addr16;【代碼】int sock_x25;struct sockaddr_x25 adr_x25;const char x25_host=”79400900”;adr_x25.sx25_family = AF_X25;strcpy (adr_x25.sx25_addr.x25_addr, x25_host);sock_x25 = sock( AF_X25, SOCK_SEQPACKET, 0 );12、除了IPv4外,linux至少還支持以下三類地址族:l AF_INET6:IPv6l AF_AX25:業(yè)余無線電X.25協(xié)議l AF_APPLETALK:Linux下AppleTalk協(xié)議的實現(xiàn)。13、宏 AF_UNSPEC 代表不確定的地址族,它可以作為一個占位符,在不確定具體的地址族之前,可以使用它。在后續(xù)程序中可以修改。例如:unionsockaddr sa;sockaddr_un un;sockaddr_in in;sockaddr_in6 in6; u;u.sa_family = AF_UNSPEC;u.sa_family = AF_INET;第三章 地址轉(zhuǎn)換函數(shù)1、一個Internet地址是由以下兩個部分組成:網(wǎng)絡(luò)地址:表示主機所在網(wǎng)絡(luò)主機地址:用來區(qū)分同一網(wǎng)絡(luò)中的不同主機2、網(wǎng)絡(luò)掩碼的作用在于把網(wǎng)絡(luò)地址從IP地址中提取出來,實際上網(wǎng)絡(luò)掩碼與某一特定的IP地址進行“按位與”操作。3、如果你的系統(tǒng)沒有直接連接到Internet,就不需要全球唯一的地址,這時就可以用私有IP地址來替代。私有IP地址的分配:分類最低最高掩碼A55B55C554、inet_addr 函數(shù)#include #include #include unsigned long inet_addr (const char * string);作用:將 string 表示的點分十進制的IP地址轉(zhuǎn)換成32位二進制表示法,次函數(shù)的返回值是這個32位二進制的【網(wǎng)絡(luò)字節(jié)序】,如果string不是一個有效的IP地址,則返回INADDR_NONE?!敬a】struct sockaddr_in adr_inet;adr_inet.sin_addr.s_addr = inet_addr(“68”);if(adr_inet.sin_addr.s_addr = INADDR_NONE) printf(“bad address”);【注意】在新程序里應(yīng)避免使用 inet_addr,而應(yīng)該使用 inet_aton 來替代。對于 inet_addr,即使輸入 55 返回值仍然是 INADDR_NONE5、inet_aton 函數(shù)#include #include #include int inet_aton (const char * string, struct in_addr * addr);如果函數(shù)調(diào)用成功,返回一個非0值,否則返回0,并沒有建立有效的errno值,所以當(dāng)函數(shù)返回錯誤時,不要去測試errno 的值?!敬a】struct sockaddr_in adr_inet;inet_aton(”23”, &adr_inet.sin_addr);6、inet_ntoa函數(shù)#include #include #include char * inet_ntoa (struct in_addr addr);作用:把套接口中的IP地址轉(zhuǎn)換成字符串形式的點分十進制形式。返回值是函數(shù)內(nèi)部定義的靜態(tài)變量,一直到下次調(diào)用本函數(shù)前一直有效,這個特點在多線程程序中容易引入競爭窗口?!敬a】struct sockaddr_in adr_inet;printf(“IP is: %s”, inet_ntoa(adr_inet.sin_addr);7、inet_network 函數(shù)#include #include #include unsigned long inet_network(const char * addr);作用:將點分十進制的IP地址轉(zhuǎn)換成【主機字節(jié)序】的32位二進制IP地址,如果參數(shù)不正確,將返回 0xFFFFFF008、inet_lnaof 函數(shù)#include #include #include unsigned long inet_lnaof(struct in_addr addr);將套接口地址中的IP地址(網(wǎng)絡(luò)字節(jié)序)轉(zhuǎn)換成沒有網(wǎng)絡(luò)位的主機ID(主機字節(jié)序)。9、inet_netof 函數(shù)#include #include #include unsigned long inet_netof (struct in_addr addr);將套接口地址中的IP地址(網(wǎng)絡(luò)字節(jié)序)轉(zhuǎn)換成網(wǎng)絡(luò)ID(主機字節(jié)序)。函數(shù)的返回值是右端對齊的,主機位被移出。10、inet_makeaddr函數(shù)#include #include #include struct in_addr inet_makeaddr (int net, int host);net:網(wǎng)絡(luò)位,右端對齊的主機字節(jié)序。host:主機位,主機字節(jié)序。返回值是網(wǎng)絡(luò)字節(jié)序。第四章 套接口的類型與協(xié)議1、協(xié)議族/地址族socket/socketpair 函數(shù)的domain 參數(shù)指定了所使用的協(xié)議族,而不是某個特定的協(xié)議在生成套接口之前,需要給定下面的參數(shù):1)所使用的協(xié)議族,例如 PF_INET 表示使用Internet IP 協(xié)議族2)所使用的協(xié)議族中某個特定的協(xié)議,例如 IPPROTO_UDP 表明使用UDP 協(xié)議。3)所使用的地址族,例如 AF_INET 表明使用的是特定協(xié)議中的 Internet IP 地址。經(jīng)驗表明,對于任何一個給定的協(xié)議族,都只有一種地址格式。 AF_XXX:地址族PF_XXX:協(xié)議族應(yīng)該使用諸如 AF_INET 這樣的地址族常量,而使用 PF_INET 這樣的協(xié)議族常量:struct sockaddr_un adr_unix;int sp2;int z = socketpair( PF_LOCAL, SOCK_STREAM, 0, sp);adr_unix.sun_family = AF_LOCAL;2、socket 函數(shù)該函數(shù)可用于生成所支持的任何協(xié)議族的套接口。#include #include int socket( int domain, int type, int protocal);返回:套接口描述符l domain:協(xié)議族(PF_LOCAL 本地UNIX套接口協(xié)議族,PF_INET 表示Internet IPv4協(xié)議族,PF_INET6 表示 IPv6 協(xié)議族)l type:套接口類型(SOCK_STREAM、SOCK_DGRAM、SOCK_SEQPACKET、SOCK_RAW)l protocol:協(xié)議類型。為0時相當(dāng)于讓內(nèi)核根據(jù)參數(shù)domain和type的值選擇正確的協(xié)議。如果函數(shù)調(diào)用成功,套接口的描述符就作為函數(shù)的返回值,如果返回值為 -1 就表明發(fā)生了錯誤,發(fā)生錯誤的原因值存放在 errno 中。套接口類型說明:l SOCK_SEQPACKET:通常用于X.25或AX.25等非Internet協(xié)議。它和SOCK_STREAM有一些差別:SOCK_STREAM 不保留消息的邊界,而SOCK_SEQPACKET保留。如果發(fā)送端調(diào)用兩次write發(fā)送數(shù)據(jù),則接收端也會發(fā)生兩次接收。domain 為 PF_INET6 時,如果要使用 TCP 協(xié)議,則套接口類型為 SOCK_SEQPACKET。SOCK_SEQPACKET套接口有如下特點:1. 保留消息邊界。2. 數(shù)據(jù)字節(jié)的接收順序與發(fā)送順序保持一致。3. 保證發(fā)送的數(shù)據(jù)字節(jié)被無錯地傳送到接收端。4. 數(shù)據(jù)是通過一對連接的套接口進行傳送的。l SOCK_RAW:表示編程者要一個原始的套接口界面,可以對通信和分組進行更直接的控制。需要編程者對協(xié)議和底層的分組結(jié)構(gòu)非常熟悉。l SOCK_STREAM:流套接口,有連接。當(dāng)編程者想在遠程套接口上實現(xiàn)流IO時,就可以使用 SOCK_STREAM 套接口選項。套接口中流的概念與UNIX中管道的概念非常相近。寫入管道(或套接口)一端的連續(xù)字節(jié)流可以在另一端接收,字節(jié)流中沒有分界線,也沒有邊界;沒有記錄的長度或塊的大小,在接收端也不存在分組的概念,在接收端獲得的所有數(shù)據(jù)都返回到調(diào)用者的緩沖區(qū)中。有如下特點:1. 不保留消息的邊界,接收端無法知道發(fā)送端共執(zhí)行了多少次write調(diào)用,也無法知道在接收到的字節(jié)流中哪里是某一次write調(diào)用的開始和結(jié)束。例如發(fā)送端可能執(zhí)行了兩次write,分別發(fā)送了25和30個字節(jié),而接收端一次性收到了55個字節(jié)。2. 有序性,可以保證接收到的數(shù)據(jù)字節(jié)與發(fā)送時的順序完全一致。發(fā)送端發(fā)送的數(shù)據(jù)可能有多個包,在網(wǎng)絡(luò)上傳輸?shù)臅r候,先發(fā)的包可能后到,而套接口可以保證交給應(yīng)用程序的數(shù)據(jù)是有序的。3. 有數(shù)據(jù)校驗功能,能夠保證收到的數(shù)據(jù)都是正確的。這些校驗功能是底層處理的,不需要編程人員處理。如果底層進行數(shù)據(jù)糾錯不成功,則發(fā)送端和接收端都將收到有關(guān)錯誤的報告。l SOCK_DGRAM:數(shù)據(jù)報套接口,無連接。有如下特點:1. 分組在發(fā)送以后,可能無序地到達接收端。2. 分組可能丟失,系統(tǒng)不會采取任何補救措施,接收端也不會知道分組丟失。3. 數(shù)據(jù)報分組大小有限制,如果超出限制,則有可能無法傳送。4. 分組是在不建立連接的情況下被發(fā)送到遠程進程的,允許本地進程每次將消息發(fā)送給不同IP地址上的同樣的端口。3、當(dāng)需要保留消息的邊界時,可以在【本地套接口】上使用 SOCK_DGRAM,由于是本地套接口,不會經(jīng)過網(wǎng)絡(luò)傳輸,所以一般不會丟包?!敬a】int s;s = socket( PF_LOCAL, SOCK_DGRAM, 0);4、當(dāng)套接口生成后,它還處于“無名”狀態(tài),也就是說還沒有地址??梢哉{(diào)用bind把地址綁定到套接口。5、在domain參數(shù)為PF_INET 和 SOCK_STREAM 套接口中,protocol參數(shù)為0意味著告訴內(nèi)核選擇 IPPROTO_TCP;如果是 SOCK_DGRAM 套接口類型,則使用 UDP(User Datagram Protocol) 協(xié)議。6、有關(guān)協(xié)議族的一些宏定義在下面的頭文件中:#include 而實際上這個文件包含另外一個定義協(xié)議宏常量的頭文件:/usr/include/bits/socket.h7、如何知道特定的協(xié)議族支持哪些套接口類型,可以通過下面步驟完成:1)進入目錄:/usr/linux/src2)cd ./net3)ls F4)進入相關(guān)協(xié)議的目錄5)用grep顯示該子目錄中所有文件中包含 SOCK_ 的行:grep SOCK_*.c8、IPV6(domain為 PF_INET6)支持三種套接口類型:SOCK_STREAM、SOCK_SEQPACKET、SOCK_DGRAM。第五章 為套接口綁定地址1、bind函數(shù)bind 的作用在于為無名套接口分配地址,函數(shù)語法如下:#include #include int bind( int sockfd, struct sockaddr * my_addr, int addrlen);sockfd:socket函數(shù)返回的套接口的描述符。my_addr:分配給套接口的地址(采用通用地址類型:struct sockaddr)addrlen:地址結(jié)構(gòu)的長度如果函數(shù)調(diào)用成功,就返回0,否則返回-1,錯誤原因值放在errno中。2、提供給bind的套接口當(dāng)前必須是無名狀態(tài)(沒有地址),我們不能使用bind為一個已有地址的套接口重新綁定另外一個地址。3、實例代碼test5.1.c:int s;struct sockaddr_in adr_inet;s=socket(AF_INET, SOCK_STREAM, 0);memset(&adr_inet, 0, sizeof adr_inet);adr_inet.sin_family = AF_INET;adr_inet.sin_port = htons(9000);inet_aton(“”, &adr_inet.sin_addr);int z = bind ( s, (struct sockaddr *)&adr_inet, sizeof adr_inet);4、由于套接口是OS分配的,套接口只是一個描述符。系統(tǒng)提供了函數(shù),通過套接口描述符可以得到【本地】套接口的地址等信息。#include int getsockname(int s, struct sockaddr * name, socklen_t * namelen);s:套接口描述符name:指向接收緩沖區(qū)的指針namelen:這個變量給出了接收緩沖區(qū)所能接收的最大字節(jié)數(shù),當(dāng)接收緩沖區(qū)寫入后,這個值將被更新為實際寫入的字節(jié)數(shù),因此函數(shù)返回后,它可能小于等于原始值。這個參數(shù)不要賦常量值。實例:int s;struct sockaddr_in adr_inet;int z = getsockname ( s, (struct sockaddr * ) &adr_inet; sizeof adr_inet);5、獲取與本地套接口連接的【遠程】套接口的協(xié)議地址,可以使用函數(shù)getpeername:#include int getpeername(int s, struct sockaddr * name, socklen_t * namelen);6、套接口程序就可以做到只接收來自特定接口的連接請求,而忽視來自其他接口的請求。7、為了給通信指定一個特定的接口,需要做如下工作:(1)用socket函數(shù)生成套接口(2)用函數(shù)bind將想要接收連接的接口的IP地址綁定到本地套接口。代碼例子:int s;struct sockaddr_in adr_inet;s=socket(AF_INET, SOCK_STREAM, 0);memset(&adr_inet, 0, sizeof adr_inet);adr_inet.sin_family = AF_INET;adr_inet.sin_port = htons(9000);inet_aton(“”, &adr_inet.sin_addr);int z = bind ( s, (struct sockaddr *)&adr_inet, sizeof adr_inet);8、怎樣才能在任何一個接口上接收連接?(1)用socket函數(shù)生成套接口(2)用函數(shù)bind將 IP地址 INADDR_ANY綁定到本地套接口。代碼例子:int s;struct sockaddr_in adr_inet;s=socket(AF_INET, SOCK_STREAM, 0);memset(&adr_inet, 0, sizeof adr_inet);adr_inet.sin_family = AF_INET;adr_inet.sin_port = htons(9000);adr_inet.sin_addr.s_addr = htonl ( INADDR_ANY );int z = bind ( s, (struct sockaddr *)&adr_inet, sizeof adr_inet);通過這種方式的套接口綁定既可以接收來自任何接口的請求,也可以通過任何本地套接口連接到遠程套接口。INADDR_ANY 就是通常所稱的通配地址。第六章 面向非連接的協(xié)議1、非連接的通信就是在通信建立之前不建立連接,非連接有如下優(yōu)點:l 不需要建立連接,使用更加簡單l 富有彈性,每一次的消息發(fā)送都可以定向到不同的接收者l 高效快速,不需要建立和拆除連接,只有消息本身被發(fā)送,避免了大量在網(wǎng)絡(luò)中傳遞消息分組的開銷。l 具備廣播能力,可以將一個消息同時向多個接受者發(fā)送。缺點如下:l 通信過程不可靠l 多數(shù)據(jù)報的無序性l 消息的尺寸有限制2、UDP 數(shù)據(jù)報的最大尺寸略小于 64KB,但實際上許多UNIX主機只提供接近于 32KB 的最大尺寸,甚至還有小到 8KB 的。最終,套接口程序還會把這個最大尺寸限制為接收緩沖區(qū)的大小,許多程序限制 UDP 消息尺寸為 512 字節(jié)或略小于 512 字節(jié)。3、對于一個大尺寸的 UDP 分組,它將被分解為若干個較小的 IP 分組,然后在接收端重新組裝,為了容納接收到的分組,需要為重新組裝的過程分配緩沖,而且對緩沖區(qū)的使用還有時間限制,這中間的任何一個條件不滿足,都會引起 UDP 分組被丟棄。4、發(fā)送 UDP 數(shù)據(jù):sendto 函數(shù)#include #include int sendto ( int s, const void * msg, int len, unsigned flags, const struct sockaddr * to, int tolen);s:套接口描述符,由socket函數(shù)生成。msg:指向容納所要發(fā)送的數(shù)據(jù)報信息的緩沖區(qū)的指針。len:數(shù)據(jù)報信息的長度(字節(jié)數(shù))。flags:指定一些選項,一般情況下置為0即可。to:指向某通用套接口地址的指針,這個地址是我們在程序中所指定的接收者的地址。tolen:地址參數(shù)to的長度。如果函數(shù)sendto調(diào)用成功,它的返回值就是所發(fā)送的字節(jié)數(shù),如果調(diào)用不成功,函數(shù)的返回值為-1,錯誤值存在errno中。發(fā)送數(shù)據(jù)僅僅是將數(shù)據(jù)報發(fā)送出去,但并不能說明它已經(jīng)被成功地接收了。flags取值:flags十六進制含義00x0000正常無特定選項MSG_OOB0x0001處理帶外數(shù)據(jù)MSG_DONTROUTE0x0004旁路路由,使用直接接口MSG_DONTWAIT0x0040非阻塞,等待寫入MSG_NOSIGNAL0x4000當(dāng)另一端斷開連接時,不生成SIGPIPE信號5、recvfrom 函數(shù)recvfrom 函數(shù)使我們能夠在接收數(shù)據(jù)報的同時,也能得到發(fā)送者的地址:#include #include int recvfrom (int s, void * buf, int len, unsigned flags, struct sockaddr * from, int * fromlen);s:接收數(shù)據(jù)報的套接口的描述符buf:指向容納接收數(shù)據(jù)報信息緩沖區(qū)的指針len:接收緩沖區(qū)的最大長度(字節(jié)數(shù))flags:選項標(biāo)志,一般情況下置為0from:指向套接口地址結(jié)構(gòu)的指針,從這個地址結(jié)構(gòu)可以獲得發(fā)送者的地址fromlen:地址參數(shù)from 的長度,在調(diào)用recvfrom之前,這個指針?biāo)赶虻恼椭当仨氋x值以from地址結(jié)構(gòu)的最大字節(jié)數(shù);當(dāng)函數(shù)返回后,這個整型值將被實際的地址大小所替代,也就是說fromlen既是如參也是出參。所以不要使用指向常量的指針。如果函數(shù)recvfrom調(diào)用成功,它的返回值就是所接收的字節(jié)數(shù),如果調(diào)用不成功,函數(shù)的返回值為-1,錯誤值存在errno中。flags取值:flags十六進制含義00x0000正常MSG_OOB0x0001處理帶外數(shù)據(jù)MSG_PEEK0x0002讀入一個數(shù)據(jù)報,但不從內(nèi)核中刪除MSG_WAITALL0x0100進行阻塞,直到所有的要求滿足MSG_ERRQUEUE0x2000從錯誤隊列中接收一個分組MSG_NOSIGNAL0x4000當(dāng)另一端斷開連接時,不生成SIGPIPE信號6、UDP數(shù)據(jù)報服務(wù)器程序【代碼】s = socket(AF_INET, SOCK_DGRAM, 0);bind ( s, (struct sockaddr *) &adr_inet, len_inet);for( ; ; )z = recvfrom ( s, dgram, sizeof dgram, 0, (struct sockaddr *)&adr_clnt, &len_inet);z = sendto ( s, dtfmt, strlen(dtfmt), 0, (struct sockaddr * )&adr_clnt, len_inet);服務(wù)器程序嗲用recvfrom,函數(shù)將阻塞,直到服務(wù)器接收到一個數(shù)據(jù)報。7、UDP數(shù)據(jù)報客戶程序【代碼】s = socket ( AF_INET, SOCK_DGRAM, 0);for( ; ; )z = sendto ( s, dgram, strlen(dgram), 0, (struct sockaddr * )&adr_srvr, len_inet );z = recvfrom ( s, dgram, sizeof dgram, 0, (struct sockaddr *)&adr, &x);客戶端程序省略了bind函數(shù),這樣程序可以選擇任何一個接口進行發(fā)送,在效果上,套接口就像被綁定了通配套接口地址,當(dāng)程序等待響應(yīng)時,他也可以使用任何一個接口來接收數(shù)據(jù)報,這時,套接口的端口號也是通配的。使用bind明確地指定通配地址,可以通過使用INADDR_NONE 達到目的,另外,為得到一個通配的端口號,可以將端口號指定為0,所以,指定IP和端口號分別為 INADDR_NONE和0所達到的效果和不調(diào)用bind是相同的。通配端口號則是從目前所有可用的端口號中隨機挑選的。一般客戶端可以不綁定地址,使用通配地址,而服務(wù)端為了使客戶端有明確的地址,一般需要綁定地址。8、如果不清楚自己的終端使用什么字符作為文件結(jié)束符,可以執(zhí)行下面的命令:stty a在結(jié)果中查找 eof = D 之類的字符串第七章 面向連接的協(xié)議客戶端1、TCP/IP 的一個亮點就在于它的通信信道是可靠的,并且它對數(shù)據(jù)的傳輸也是有序的,在連接建立后,應(yīng)用程序?qū)μ捉涌诘淖x寫就無需擔(dān)心以下問題:l 分組的丟失l 超時和重發(fā)l 重復(fù)的分組l 分組的接收順序混亂l 流控2、為了將數(shù)據(jù)安全地傳送到遠端,我們的程序只需做以下工作:l 與遠程套接口建立 TCP/IP 連接。l 發(fā)送大量的數(shù)據(jù)。l 關(guān)閉套接口。丟失的分組會自動重發(fā),直到被遠程主機正確地接收。3、流控制TCP協(xié)議邏輯上位于 IP 協(xié)議的上層,它就像一個監(jiān)督人一樣,確保接收端不接收大于自身處理能力而導(dǎo)致過載的多余分組,當(dāng)接收端感到目前的處理能力吃緊時,它就會通知發(fā)送端暫時不要發(fā)送數(shù)據(jù);當(dāng)它感覺目前的處理能力有富裕時,會通知發(fā)送端可以再次開始發(fā)送,這就是所謂的“流控制”。4、在Linux系統(tǒng)中有一個名為 /etc/services 的文件,這個文件將某個特定的 Internet 服務(wù)名映射到協(xié)議的端口號,它的路徑名可以通過C語言的宏 _PATH_SERVICES 得到:#include printf (“File path is %s”, _PATH_SERVICES );這個文件的每一行都描述了一個 Internet 服務(wù),它的格式如下:服務(wù)名 端口號/協(xié)議 別名Service-name:大小寫敏感,易于理解的服務(wù)名Port:該服務(wù)的端口號Protocol:指定所使用的協(xié)議種類。Alias:別名,別名間使用制表符或空格分割,最多不能超過35個例如: ftp 21/tcptelnet 23/tcp5、getservent 函數(shù)#include struct servent * getservent ( void );函數(shù)的返回值是指向代表 /etc/services 文件中某一條目的結(jié)構(gòu)的指針,當(dāng)?shù)竭_文件末尾時,就返回 NULL 指針,如果有錯誤發(fā)生,也返回NULL指針,錯誤的原因值在 errno 中g(shù)etservent 所返回的指針僅在再次調(diào)用getservent之前有效。struct servent char * s_name;char * s_aliases; / 指向字符串?dāng)?shù)組的指針,最后一個指針為 NULLint s_port; / 網(wǎng)絡(luò)字節(jié)序char * s_proto;【代碼:打印出 /etc/services 文件的每個條目】int x;struct servent * sp;for(;)if ( ! (sp = getservent () ) break;printf (“service:%s, port : %d, protocol:%s ”, sp-s_name, ntohs ( sp-s_port), sp-s_proto);for( x=0; sp-s_aliasesx!=NULL; x+) printf(“%s”, sp-s_aliasesx);6、setservent 函數(shù)#include void setservent ( int stayopen );函數(shù)的作用是回卷到/etc/services 文件的開始處。stayopen 非0時,表示不需要重新打開 /etc/services 文件,而只需要對文件進行回卷,這樣可以提高性能;為0時,表示將現(xiàn)關(guān)閉已經(jīng)打開的 /etc/services 文件,然后重新打開,并為調(diào)用 getservent 做好準(zhǔn)備。7、函數(shù) endservent 用于關(guān)閉/etc/services 文件#include void endservent ( void );8、getservbyname 函數(shù)通過名字和協(xié)議查詢服務(wù)#include struct servent * getservbyname ( const char * name, const char * proto );name:要查詢的服務(wù)名稱,例如:telnetproto:使用的協(xié)議,例如tcp如果函數(shù)查找不到對應(yīng)的條目,返回值就是NULL,否則返回一個指向 servent 結(jié)構(gòu)的指針。這個返回的指針僅在再次調(diào)用 getservbyname之前有效。9、getservbyport 通過端口和協(xié)議查詢服務(wù)#include struct servent * getservbyport( int port, const char * proto );如果函數(shù)查找不到對應(yīng)的條目,返回值就是NULL,否則返回一個指向 servent 結(jié)構(gòu)的指針。這個返回的指針僅在再次調(diào)用 getservbyport之前有效。10、/etc/protocols 文件包含已定義的Internet協(xié)議值。#include struct protoent * getprotoent ( void );struct protoent char * p_name; / 協(xié)議名,例如 “tcp”char * p_aliases; / 別名清單char p_proto; / 協(xié)議號,例如tcp的協(xié)議號是6這個函數(shù)每一次調(diào)用返回 /etc/protocols 中的一個條目,當(dāng)?shù)竭_文件末尾或有錯誤發(fā)生時,就返回NULL函數(shù) getprotoent 返回的指針僅在再次調(diào)用 getprotoent 之前有效。11、setprotoent 對打開的文件進行回卷#include struct protoent * setprotoen

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論