• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            tqsheng

            go.....
            隨筆 - 366, 文章 - 18, 評(píng)論 - 101, 引用 - 0
            數(shù)據(jù)加載中……

            Unix網(wǎng)絡(luò)API

            索引:

             1.字節(jié)序函數(shù)
            2.字節(jié)操作函數(shù)
            3.地址轉(zhuǎn)換函數(shù)
            4.readn、writen和readline
            5.測(cè)試描述符類型
            6.socket函數(shù)
            7.connect函數(shù)
            8.bind函數(shù)
            9.listen函數(shù)
            10.accept函數(shù)
            11.close函數(shù)
            12.getsockname和getpeername
            13.select函數(shù)
            14.shutdown函數(shù)
            15.pselect函數(shù)
            16.poll函數(shù)
            17.getsockopt和setsockopt
            18.套接口選項(xiàng)列表
            19.處理套接口的fcntl函數(shù)
            20.gethostbyname函數(shù)
            21.gethostbyname2函數(shù)
            22.ethostbyaddr函數(shù)
            23.uname函數(shù)
            24.gethostname函數(shù)
            25.getservbyname函數(shù)
            26.getservbyport函數(shù)
            27.recv和send
            28.readv和writev
            29.readmsg和writemsg
            30.socketpair函數(shù)
            31.套接口ioctl函數(shù)


            1.字節(jié)序函數(shù)

             #include <netinet.h>
            uint16_t htons(uint16_t host16bitvalue);
            uint32_t htonl(uint32_t host32bitvalue);
            返回:網(wǎng)絡(luò)字節(jié)序值

            uint16_t ntohs(uint16_t net16bitvalue);
            uint32_t ntohl(uint32_t net32bitvalue);
            返回:主機(jī)字節(jié)序值

            一個(gè)測(cè)試本機(jī)字節(jié)序的程序,可參見(jiàn)見(jiàn)unpv12e:intro/byteorder.c。


            2.字節(jié)操作函數(shù)

             #include <strings.h>
            void bzero(void *dest, size_t nbytes);
            void bcopy(const void *src, void *dest, size_t nbytes);
            int bcmp(const void *ptr1, const void *ptr2, size_t nbytes);
            返回:0—相等,非0—不相等

            #include <string.h>
            void *memset(void *dest, int c, size_t len);
            void *memcpy(void *dest, void *src, size_t nbytes);
            int memcmp(const void *ptr1, const void *ptr2, size_t nbytes);
            返回:0—相同,>0或<0—不相同;進(jìn)行比較操作時(shí),假定兩個(gè)不相等的字節(jié)均為無(wú)符號(hào)字符(unsigned char)。


            3.地址轉(zhuǎn)換函數(shù)

             #include <arpa/inet.h>
            int inet_aton(const char *strptr, struct in_addr *addrptr);
            返回:1—串有效,0—串有錯(cuò)。

            in_addr_t inet_addr(const char *strptr);
            返回:若成功,返回32為二進(jìn)制的網(wǎng)絡(luò)字節(jié)序地址;若有錯(cuò),則返回INADDR_NONE。

            char *inet_ntoa(struct in_addr inaddr);
            返回:指向點(diǎn)分十進(jìn)制數(shù)串的指針。

            int inet_pton(int family, const char *strptr, void *addrptr);
            返回:1—成功;0—輸入不是有效的表達(dá)格式,-1—出錯(cuò)。

            const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len);
            返回:指向結(jié)果的指針—成功,NULL—失敗。

            說(shuō)明:

            • inet_aton函數(shù)的指針若為空,則函數(shù)仍然執(zhí)行輸入串的有效性檢查,但不存儲(chǔ)任何結(jié)果。
            • inet_addr的缺陷:出錯(cuò)返回值INADDR_NONE等于255.255.255.255(IPv4的有限廣播地址),所以該函數(shù)不能處理此地址。
              盡量使用inet_aton,不使用inet_addr。
            • inet_ntoa函數(shù)的執(zhí)行結(jié)果放在靜態(tài)內(nèi)存中,是不可重入的。
            • 參數(shù)family可以是AF_INET,也可以是AF_INET6,若參數(shù)family不被支持,則出錯(cuò),errno置為EAFNOSUPPORT。
            • 指針addrptr是結(jié)構(gòu)指針。
            • len指定目標(biāo)的大小,避免緩沖區(qū)溢出。如果len太小,則返回一個(gè)空指針,errno置為ENOSPC。為有助于規(guī)定該大小,有如下定義:
              #include <netinet.h>
              #define INET_ADDRSTRLEN 16 /*fro IPv4 dotted-decimal */
              #define INET6_ADDRSTRLEN 46 /*for IPv6 hex string */
            • inet_ntop函數(shù)的參數(shù)strptr不能為空指針,成功時(shí),此指針即是函數(shù)的返回值。

            實(shí)現(xiàn)IPv4版本的inet_pton和inet_ntop的程序,參見(jiàn):unpv12e:libfree/inet_pton_ipv4.c和libfree/inet_ntop_ipv4.c。

            4.readn、writen和readline

             函數(shù)原型如下:
            ssize_t readn(int filedes, void *buff, size_t nbytes);
            ssize-t writen(int filedes, void *buff, size_t nbytes);
            ssize_t readline(int filedes, void *buff, size_t maxlen);
            返回:讀寫(xiě)字節(jié)數(shù),-1—出錯(cuò)。

            實(shí)現(xiàn)程序見(jiàn):unpv12e:lib/readn.c、lib/writen.c、lib/readline1.c和lib/readline.c。


            5.測(cè)試描述符類型

             #include <sys/stat.h>
            int isfdtype( int fd, int fdtype);
            返回:1—是指定類型,0—不是指定類型,-1—出錯(cuò)。

            要測(cè)試是否為套接口描述子,fdtype應(yīng)設(shè)為S_IFSOCK。

            該函數(shù)的一個(gè)實(shí)現(xiàn)程序,參見(jiàn)unpv12e:lib/isfdtype.c


            6.socket函數(shù)

             #include <sys/socket.h>
            int socket(int family, int type, int protocol);
            返回:非負(fù)描述字—成功,-1—出錯(cuò)。

            family指定協(xié)議族,有如下取值:

            • AF_INET     IPv4協(xié)議
            • AF_INET6    IPv6協(xié)議
            • AF_LOCAL    Unix域協(xié)議
            • AF_ROUTE    路由套接口
            • AF_KEY      密鑰套接口
            type指定套接口類型:
            • SOCK_STREAM    字節(jié)流套接口
            • SOCK_DGRAM     數(shù)據(jù)報(bào)套接口
            • SOCK_RAW       原始套接口
            protocol一般設(shè)為0,除非用在原始套接口上。

            并非所有family和type的組合都是有效的。

            AF_LOCAL等于早期的AF_UNIX。


            7.connect函數(shù)

             #include <sys/socket.h>
            int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);
            返回:0—成功,-1—出錯(cuò)。

            sockfd是socket函數(shù)返回的套接口描述字,servaddr和addrlen是指向服務(wù)器的套接口地址結(jié)構(gòu)指針和結(jié)構(gòu)大小。

            在調(diào)用connect之前不必非得調(diào)用bind函數(shù)。

            如果是TCP,則connect激發(fā)TCP的三路握手過(guò)程,在阻塞情況下,只有在連接建立成功或出錯(cuò)時(shí)該函數(shù)才返回,
            出錯(cuò)情況:

            • 沒(méi)有收到SYN分節(jié)的響應(yīng),在規(guī)定時(shí)間內(nèi)經(jīng)過(guò)重發(fā)仍無(wú)效,則返回ETIMEDOUT;
            • 如果對(duì)SYN分節(jié)的響應(yīng)是RST,表示服務(wù)器在指定端口上沒(méi)有相應(yīng)的服務(wù),返回ECONNREFUSED;
            • 如果發(fā)出 SYN在中間路由器上引發(fā)一個(gè)目的地不可達(dá)ICMP錯(cuò)誤,在規(guī)定時(shí)間內(nèi)經(jīng)過(guò)重發(fā)仍無(wú)效,則返回EHOSTUNREACH或ENETUNREACH錯(cuò)誤。
            注意:如果connect失敗,則套接口將不能再使用,必須關(guān)閉,不能對(duì)此套接口再調(diào)用函數(shù)connect。

            8.bind函數(shù)

             #include <sys/socket.h>
            int bind(int sockfd, const struct sockaddr *maddr, socklen_t addrlen);
            返回:0—成功,-1—出錯(cuò)。

            進(jìn)程可以把一個(gè)特定的IP地址捆綁到他的套接口上,但此IP地址必須是主機(jī)的一個(gè)接口。

            對(duì)于IPv4,通配地址是INADDR_ANY,其值一般為0;使用方法如下:
            struct sockaddr_in servaddr;
            servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

            對(duì)于IPv6,方法如下:
            struct sockaddr_in6 serv;
            serv.sin6_addr = in6addr_any; (系統(tǒng)分配變量in6addr_any并將其初始化為常值IN6ADDR_ANY_INIT。)

            如果讓內(nèi)核選擇臨時(shí)端口,注意的是bind并不返回所選的斷口值,要得到一個(gè)端口,必須使用getsockname函數(shù)。

            bind失敗的常見(jiàn)錯(cuò)誤是EADDRINUSE(地址已使用)。


            9.listen函數(shù)

             #include <sys/socket.h>
            int listen(int sockfd, int backlog);
            返回:0—成功,-1—出錯(cuò)。

            listen把未連接的套接口轉(zhuǎn)化為被動(dòng)套接口,指示內(nèi)核應(yīng)接受指向此套接口的連接請(qǐng)求。第二個(gè)參數(shù)規(guī)定了內(nèi)核為此套接口排隊(duì)的最大連接數(shù)。

            參數(shù)backlog曾經(jīng)規(guī)定為監(jiān)聽(tīng)套接口上的未完成連接隊(duì)列和已完成連接隊(duì)列總和的最大值,但各個(gè)系統(tǒng)的定義方法都不盡相同;歷史上常把backlog置為5,但對(duì)于繁忙的服務(wù)器是不夠的;backlog的設(shè)置沒(méi)有一個(gè)通用的方法,依情況而定,但不要設(shè)為0。


            10.accept函數(shù)

             #include <sys/socket.h>
            int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
            返回:非負(fù)描述字—OK,-1—出錯(cuò)。

            accept從已完成連接隊(duì)列頭返回下一個(gè)連接,若已完成連接隊(duì)列為空,則進(jìn)程睡眠(套接口為阻塞方式時(shí))。

            參數(shù)cliaddr和addrlen返回連接對(duì)方的協(xié)議地址,其中addrlen是值-結(jié)果參數(shù),調(diào)用前addrlen所指的整數(shù)值要置為cliaddr所指的套接口結(jié)構(gòu)的長(zhǎng)度,返回時(shí)由內(nèi)核修改。

            accept成功執(zhí)行后,返回一個(gè)連接套接口描述字。

            如果對(duì)客戶的協(xié)議地址沒(méi)有興趣,可以把cliaddr和addrlen置為空指針。


            11.close函數(shù)

             #include <unistd.h>
            int close(int sockfd);
            返回:0—OK,-1—出錯(cuò)。

            TCP套接口的close缺省功能是將套接口做上“已關(guān)閉”標(biāo)記,并立即返回到進(jìn)程。這個(gè)套接口描述字不能再為進(jìn)程使用,但TCP將試著發(fā)送已排隊(duì)待發(fā)的任何數(shù)據(jù),然后按正常的TCP連接終止序列進(jìn)行操作。

            close把描述字的訪問(wèn)計(jì)數(shù)減1,當(dāng)訪問(wèn)計(jì)數(shù)仍大于0時(shí),close并不會(huì)引發(fā)TCP的四分組連接終止序列。若確實(shí)要發(fā)一個(gè)FIN,可以用函數(shù)shutdown。


            12.getsockname和getpeername

             #include <sys/socket.h>
            int getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *addrlen);
            int getpeername(int sockfd, struct sockaddr *peeraddr, socklen_t *addrlen);
            返回:0—OK,-1—出錯(cuò)。

            getsockname函數(shù)返回與套接口關(guān)聯(lián)的本地協(xié)議地址。

            getpeername函數(shù)返回與套接口關(guān)聯(lián)的遠(yuǎn)程協(xié)議地址。

            addrlen是值-結(jié)果參數(shù)。

            使用場(chǎng)合:

            • 在不調(diào)用bind的TCP客戶,當(dāng)connect成功返回后,getsockname返回分配給此連接的本地IP地址和本地端口號(hào);
            • 在以端口號(hào)為0調(diào)用bind后,使用getsockname返回內(nèi)核分配的本地端口號(hào);
            • getsockname可用來(lái)獲取某套接口的地址族;
            • 在捆綁了通配IP地址的TCP服務(wù)器上,當(dāng)連接建立后,可以使用getsockname獲得分配給此連接的本地IP地址;
            • 當(dāng)一個(gè)服務(wù)器調(diào)用exec啟動(dòng)后,他獲得客戶身份的唯一途徑是調(diào)用getpeername函數(shù)。

            13.select函數(shù)

             #include <sys/select.h>
            #include <sys/time.h>
            int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);
            返回:準(zhǔn)備好描述字的正數(shù)目,0—超時(shí),-1—出錯(cuò)。

            結(jié)構(gòu)timeval的定義:
            struct timeval {
                long tv_sec; /* seconds */
                long tv_usec; /* microseconds */
            };

            timeout取值的三種情況:

            • 永遠(yuǎn)等下去:僅在有一個(gè)描述字準(zhǔn)備好I/O時(shí)才返回,設(shè)置timeout為空指針;
            • 等待固定時(shí)間:在有一個(gè)描述字準(zhǔn)備好I/O時(shí)返回,但不超過(guò)由timeout參數(shù)所指定的秒數(shù)和微秒數(shù);
            • 根本不等待:檢查描述字后立即返回,將timeout中的秒數(shù)和微秒數(shù)都設(shè)置為0。
            在等待過(guò)程中,若進(jìn)程捕獲了信號(hào)并從信號(hào)處理程序返回,等待一般被中斷,為了可移植性,必須準(zhǔn)備好select返回EINTR錯(cuò)誤。

            timeout的值在返回時(shí)并不會(huì)被select修改(const標(biāo)志)。

            readset、writeset、exceptset指定我們要讓內(nèi)核測(cè)試讀、寫(xiě)和異常條件所需的描述字。

            當(dāng)前支持的異常條件有兩個(gè):

            1. 套接口帶外數(shù)據(jù)的到達(dá);
            2. 控制狀態(tài)信息的存在,可從一個(gè)已置為分組方式的偽終端主端讀到。
            描述字集的使用:
            數(shù)據(jù)類型:fd_set;
            void FD_ZERO(fd_set *fdset);
            void FD_SET(int fd, fd_set *fdset);
            void FD_CLR(int fd, fd_set *fdset);
            void FD_ISSET(int fd, fd_set *fdset);

            參數(shù)maxfdp1指定被測(cè)試的描述字個(gè)數(shù),它的值是要被測(cè)試的最大描述字加1。描述字0,1,2,…,maxfdp1-1都被測(cè)試。

            readset、writeset、exceptset是值-結(jié)果參數(shù),select修改三者所指的描述字集。所以,每次調(diào)用select時(shí),我們都要將所有描述字集中關(guān)心的位置為1。

            套接口準(zhǔn)備好讀的條件:

            • 套接口接收緩沖區(qū)中的數(shù)據(jù)字節(jié)數(shù)大于等于套接口接收緩沖區(qū)低潮限度的當(dāng)前值。對(duì)這樣的套接口的讀操作將不阻塞并返回一個(gè)大于0的值(即準(zhǔn)備好讀入的數(shù)據(jù)量)。可以用套接口選項(xiàng)SO_RCVLOWAT來(lái)設(shè)置低潮限度,對(duì)于TCP和UDP,缺省值為1;
            • 連接的讀這一半關(guān)閉(接收了FIN的TCP連接)。對(duì)這樣的套接口讀操作將不阻塞并且返回0(即文件結(jié)束符);
            • 套接口是一個(gè)監(jiān)聽(tīng)套接口且已完成的連接數(shù)為非0;
            • 有一個(gè)套接口錯(cuò)誤待處理。對(duì)這樣的套接口讀操作將不阻塞且返回一個(gè)錯(cuò)誤,errno設(shè)置成明確的錯(cuò)誤條件。這些待處理錯(cuò)誤也可以通過(guò)指定套接口選項(xiàng)SO_ERROR調(diào)用getsockopt來(lái)取得并清除。
            套接口準(zhǔn)備好寫(xiě)的條件:
            • 套接口發(fā)送緩沖區(qū)中的可用字節(jié)數(shù)大于等于套接口發(fā)送緩沖區(qū)低潮限度的當(dāng)前值,且或者(1)套接口已連接,或者(2)套接口不要求連接(如UDP套接口)。可以用套接口選項(xiàng)SO_SNDLOWAT來(lái)設(shè)置此低潮限度,對(duì)于TCP和UDP,缺省值為2048;
            • 連接的寫(xiě)這一半關(guān)閉。對(duì)這樣的套接口寫(xiě)將產(chǎn)生信號(hào)SIGPIPE;
            • 有一個(gè)套接口錯(cuò)誤待處理。對(duì)這樣的套接口寫(xiě)操作將不阻塞且返回一個(gè)錯(cuò)誤,errno設(shè)置成明確的錯(cuò)誤條件。這些待處理錯(cuò)誤也可以通過(guò)指定套接口選項(xiàng)SO_ERROR調(diào)用getsockopt來(lái)取得并清除。
            如果一個(gè)套接口存在帶外數(shù)據(jù)或者仍處于帶外標(biāo)記,那它有異常條件待處理。

            一個(gè)套接口出錯(cuò)時(shí),它被select標(biāo)記為既可讀又可寫(xiě)。


            14.shutdown函數(shù)

             #include <sys/socket.h>
            int shutdown(int sockfd, int howto);
            返回:0—成功,-1—失敗。

            函數(shù)的行為依賴于參數(shù)howto的值:

            • SHUT_RD:關(guān)閉連接的讀這一半,不再接收套接口中的數(shù)據(jù)且留在套接口緩沖區(qū)中的數(shù)據(jù)都作廢。進(jìn)程不能再對(duì)套接口任何讀函數(shù)。調(diào)用此函數(shù)后,由TCP套接口接收的任何數(shù)據(jù)都被確認(rèn),但數(shù)據(jù)本身被扔掉。
            • SHUT_WR:關(guān)閉連接的寫(xiě)這一半,在TCP場(chǎng)合下,這稱為半關(guān)閉。當(dāng)前留在套接口發(fā)送緩沖區(qū)中的數(shù)據(jù)都被發(fā)送,后跟正常的TCP連接終止序列。此半關(guān)閉不管套接口描述字的訪問(wèn)計(jì)數(shù)是否大于0。進(jìn)程不能再執(zhí)行對(duì)套接口的任何寫(xiě)函數(shù)。
            SHUT_RDWR:連接的讀這一半和寫(xiě)這一半都關(guān)閉。這等效于調(diào)用shutdown兩次:第一次調(diào)用時(shí)用SHUT_RD,第二次調(diào)用時(shí)用SHUT_WR。

            15.pselect函數(shù)

             #include <sys/select.h>
            #include <signal.h>
            #include <time.h>
            int pselect(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timespec *timeout, const sigset_t *sigmask);
            返回:準(zhǔn)備好描述字的個(gè)數(shù),0—超時(shí),-1—出錯(cuò)。

            pselect是Posix.1g發(fā)明的。相對(duì)select的變化:

            1. pselect使用結(jié)構(gòu)timespec:
              struct timespec {
                  time_t tv_sec; /* seconds */
                  long tv_nsec; /* nanoseconds */
              };
              新結(jié)構(gòu)中的tv_nsec規(guī)定納秒數(shù)。
            2. pselect增加了第六個(gè)參數(shù):指向信號(hào)掩碼的指針。允許程序禁止遞交某些信號(hào)。

            16.poll函數(shù)

             #include <poll.h>
            int poll(struct pollfd *fdarray, unsigned long nfds, int timeout);
            返回:準(zhǔn)備好描述字的個(gè)數(shù),0—超時(shí),-1—出錯(cuò)。

            第一個(gè)參數(shù)是指向一個(gè)結(jié)構(gòu)數(shù)組的第一個(gè)元素的指針,每個(gè)數(shù)組元素都是一個(gè)pollfd結(jié)構(gòu):
            struct pollfd {
                int fd; /* descriptor to check */
                short events; /* events of interest on fd */
                short revents; /* events that occurred on fd */
            };

            要測(cè)試的條件由成員events規(guī)定,函數(shù)在相應(yīng)的revents成員中返回描述字的狀態(tài)(一個(gè)描述字有兩個(gè)變量:一個(gè)為調(diào)用值,一個(gè)為結(jié)果)。

            第二個(gè)參數(shù)指定數(shù)組中元素的個(gè)數(shù)。

            第三個(gè)參數(shù)timeout指定函數(shù)返回前等待多長(zhǎng)時(shí)間,單位是毫秒。可能值如下:

            • INFTIM,永遠(yuǎn)等待;
            • 0,立即返回,不阻塞;
            • >0,等待指定數(shù)目的毫秒數(shù)。
            標(biāo)志的范圍:
            常量能作為events的輸入嗎?能作為revents的結(jié)果嗎?解釋
            POLLINyesyes普通或優(yōu)先級(jí)帶數(shù)據(jù)可讀
            POLLRDNORMyesyes普通數(shù)據(jù)可讀
            POLLRDBANDyesyes優(yōu)先級(jí)帶數(shù)據(jù)可讀
            POLLPRIyesyes高優(yōu)先級(jí)數(shù)據(jù)可讀
            POLLOUTyesyes普通或優(yōu)先級(jí)帶數(shù)據(jù)可寫(xiě)
            POLLWRNORMyesyes普通數(shù)據(jù)可寫(xiě)
            POLLWRBANDyesyes優(yōu)先級(jí)帶數(shù)據(jù)可寫(xiě)
            POLLERR
            yes發(fā)生錯(cuò)誤
            POLLHUP
            yes發(fā)生掛起
            POLLNVAL
            yes描述字不是一個(gè)打開(kāi)的文件
            圖可分為三部分:處理輸入的四個(gè)常值;處理輸出的三個(gè)常值;處理錯(cuò)誤的三個(gè)常值。

            poll識(shí)別三個(gè)類別的數(shù)據(jù):普通(normal)、優(yōu)先級(jí)帶(priority band)、高優(yōu)先級(jí)(high priority)。術(shù)語(yǔ)來(lái)自流的概念。

            返回條件:

            • 所有正規(guī)TCP數(shù)據(jù)和UDP數(shù)據(jù)都被認(rèn)為是普通數(shù)據(jù);
            • TCP的帶外數(shù)據(jù)被認(rèn)為是優(yōu)先級(jí)帶數(shù)據(jù);
            • 當(dāng)TCP連接的讀這一半關(guān)閉時(shí)(如接收了一個(gè)FIN),這也認(rèn)為是普通數(shù)據(jù),且后續(xù)的讀操作將返回0;
            • TCP連接存在錯(cuò)誤既可以認(rèn)為是普通數(shù)據(jù),也可以認(rèn)為是錯(cuò)誤(POLLERR)。無(wú)論哪種情況,后續(xù)的讀操作將返回-1,并將errno置為適當(dāng)?shù)闹担@就處理了諸如接收到RST或超時(shí)等條件;
            • 在監(jiān)聽(tīng)套接口上新連接的可用性既可認(rèn)為是普通數(shù)據(jù),也可以認(rèn)為是優(yōu)先級(jí)帶數(shù)據(jù),大多數(shù)實(shí)現(xiàn)都將其作為普通數(shù)據(jù)考慮。
            • 如果不關(guān)心某個(gè)特定的描述字,可將其pollfd結(jié)構(gòu)的fd成員置為一個(gè)負(fù)值,這樣就可以忽略成員events,且返回時(shí)將成員revents的值置為0。
            poll沒(méi)有select存在的最大描述字?jǐn)?shù)目問(wèn)題。但可移植性select要好于poll。

            17.getsockopt和setsockopt

             #include <sys/socket.h>
            int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
            int setsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
            返回:0—OK,-1—出錯(cuò)。

            sockfd必須是一個(gè)打開(kāi)的套接口描述字;level(級(jí)別)指定系統(tǒng)中解釋選項(xiàng)的代碼:普通套接口代碼或特定于協(xié)議的代碼);optval是一個(gè)指向變量的指針;此變量的大小由最后一個(gè)參數(shù)決定。

            對(duì)于某些套接口選項(xiàng),什么時(shí)候進(jìn)行設(shè)置或獲取是有差別的。下面的套接口選項(xiàng)是由TCP已連接套接口從監(jiān)聽(tīng)套接口繼承來(lái)的:

            • SO_DEBUG;
            • SO_DONTROUTE;
            • SO_KEEPALIVE;
            • SO_LINGER;
            • SO_OOBINLINE;
            • SO_RCVBUF;
            • SO_SNDBUF。
            如果想在三路握手完成時(shí)確保這些套接口選項(xiàng)中的某一個(gè)是給已連接套接口設(shè)置的,我們必須先給監(jiān)聽(tīng)套接口設(shè)置此選項(xiàng)。

            18.套接口選項(xiàng)列表

            levelOptnamegetset說(shuō)明標(biāo)志數(shù)據(jù)類型







            SOL_SOCKETSO_BROADCASTyy允許發(fā)送廣播數(shù)據(jù)報(bào)yint

            SO_DEBUGyy使能調(diào)試跟蹤yint

            SO_DONTROUTEyy旁路路由表查詢yint

            SO_ERRORy
            獲取待處理錯(cuò)誤并消除
            int

            SO_KEEPALIVEyy周期性測(cè)試連接是否存活yint

            SO_LINGERyy若有數(shù)據(jù)待發(fā)送則延遲關(guān)閉
            linger{}

            SO_OOBINLINEyy讓接收到的帶外數(shù)據(jù)繼續(xù)在線存放yint

            SO_RCVBUFyy接收緩沖區(qū)大小
            int

            SO_SNDBUFyy發(fā)送緩沖區(qū)大小
            int

            SO_RCVLOWATyy接收緩沖區(qū)低潮限度
            int

            SO_SNDLOWATyy發(fā)送緩沖區(qū)低潮限度
            int

            SO_RCVTIMEOyy接收超時(shí)
            timeval{}

            SO_SNDTIMEOyy發(fā)送超時(shí)
            timeval{}

            SO_REUSEADDRyy允許重用本地地址yint

            SO_REUSEPORTyy允許重用本地地址yint

            SO_TYPEy
            取得套接口類型
            int

            SO_USELOOPBACKyy路由套接口取得所發(fā)送數(shù)據(jù)的拷貝yint







            IPPROTO_IPIP_HDRINCLyyIP頭部包括數(shù)據(jù)yint

            IP_OPTIONSyyIP頭部選項(xiàng)
            見(jiàn)后面說(shuō)明

            IP_RECVDSTADDRyy返回目的IP地址yint

            IP_RECVIFyy返回接收到的接口索引yint

            IP_TOSyy服務(wù)類型和優(yōu)先權(quán)
            int

            IP_TTLyy存活時(shí)間
            int

            IP_MULTICAST_IFyy指定外出接口
            in_addr{}

            IP_MULTICAST_TTLyy指定外出TTL
            u_char

            IP_MULTICAST_LOOPyy指定是否回饋
            u_char

            IP_ADD_MEMBERSHIP
            y加入多播組
            ip_mreq{}

            IP_DROP_MEMBERSHIP
            y離開(kāi)多播組
            ip_mreq{}







            IPPROTO_ICMPV6ICMP6_FILTERyy指定傳遞的ICMPv6消息類型
            icmp6_filter{}







            IPPROTO_IPV6IPV6_ADDRFORMyy改變套接口的地址結(jié)構(gòu)
            int

            IPV6_CHECKSUMyy原始套接口的校驗(yàn)和字段偏移
            int

            IPV6_DSTOPTSyy接收目標(biāo)選項(xiàng)yint

            IPV6_HOPLIMITyy接收單播跳限yint

            IPV6_HOPOPTSyy接收步跳選項(xiàng)yint

            IPV6_NEXTHOPyy指定下一跳地址ysockaddr{}

            IPV6_PKTINFOyy接收分組信息yint

            IPV6_PKTOPTIONSyy指定分組選項(xiàng)
            見(jiàn)后面說(shuō)明

            IPV6_RTHDRyy接收原路徑yint

            IPV6_UNICAST_HOPSyy缺省單播跳限
            int

            IPV6_MULTICAST_IFyy指定外出接口
            in6_addr{}

            IPV6_MULTICAST_HOPSyy指定外出跳限
            u_int

            IPV6_MULTICAST_LOOPyy指定是否回饋yu_int

            IPV6_ADD_MEMBERSHIP
            y加入多播組
            ipv6_mreq{}

            IPV6_DROP_MEMBERSHIP
            y離開(kāi)多播組
            ipv6_mreq{}







            IPPROTO_TCPTCP_KEEPALIVEyy控測(cè)對(duì)方是否存活前連接閑置秒數(shù)
            int

            TCP_MAXRTyyTCP最大重傳時(shí)間
            int

            TCP_MAXSEGyyTCP最大分節(jié)大小
            int

            TCP_NODELAYyy禁止Nagle算法yint

            TCP_STDURGyy緊急指針的解釋yint

            詳細(xì)說(shuō)明:

            SO_BROADCAST

             使能或禁止進(jìn)程發(fā)送廣播消息的能力。只有數(shù)據(jù)報(bào)套接口支持廣播,并且還必須在支持廣播消息的網(wǎng)絡(luò)上(如以太網(wǎng)、令牌環(huán)網(wǎng)等)。

            如果目的地址是廣播地址但此選項(xiàng)未設(shè),則返回EACCES錯(cuò)誤。

            SO_DEBUG

             僅僅TCP支持。當(dāng)打開(kāi)此選項(xiàng)時(shí),內(nèi)核對(duì)TCP在此套接口所發(fā)送和接收的所有分組跟蹤詳細(xì)信息。這些信息保存在內(nèi)核的環(huán)形緩沖區(qū)內(nèi),可由程序trpt進(jìn)行檢查。

            SO_DONTROUTE

             此選項(xiàng)規(guī)定發(fā)出的分組將旁路底層協(xié)議的正常路由機(jī)制。

            該選項(xiàng)經(jīng)常由路由守護(hù)進(jìn)程(routed和gated)用來(lái)旁路路由表(路由表不正確的情況下),強(qiáng)制一個(gè)分組從某個(gè)特定接口發(fā)出。

            SO_ERROR

             當(dāng)套接口上發(fā)生錯(cuò)誤時(shí),源自Berkeley的內(nèi)核中的協(xié)議模塊將此套接口的名為so_error的變量設(shè)為標(biāo)準(zhǔn)的UNIX Exxx值中的一個(gè),它稱為此套接口的待處理錯(cuò)誤(pending error)。內(nèi)核可立即以以下兩種方式通知進(jìn)程:
            1. 如果進(jìn)程阻塞于次套接口的select調(diào)用,則無(wú)論是檢查可讀條件還是可寫(xiě)條件,select都返回并設(shè)置其中一個(gè)或所有兩個(gè)條件。
            2. 如果進(jìn)程使用信號(hào)驅(qū)動(dòng)I/O模型,則給進(jìn)程或進(jìn)程組生成信號(hào)SIGIO。
            進(jìn)程然后可以通過(guò)獲取SO_ERROR套接口選項(xiàng)來(lái)得到so_error的值。由getsockopt返回的整數(shù)值就是此套接口的待處理錯(cuò)誤。so_error隨后由內(nèi)核復(fù)位為0。

            當(dāng)進(jìn)程調(diào)用read且沒(méi)有數(shù)據(jù)返回時(shí),如果so_error為非0值,則read返回-1且errno設(shè)為so_error的值,接著so_error的值被復(fù)位為0。如果此套接口上有數(shù)據(jù)在排隊(duì),則read返回那些數(shù)據(jù)而不是返回錯(cuò)誤條件。

            如果進(jìn)程調(diào)用write時(shí)so_error為非0值,則write返回-1且errno設(shè)為so_error的值,隨后so_error也被復(fù)位。

            SO_KEEPALIVE

             打開(kāi)此選項(xiàng)后,如果2小時(shí)內(nèi)在此套接口上沒(méi)有任何數(shù)據(jù)交換,TCP就會(huì)自動(dòng)給對(duì)方發(fā)一個(gè)保持存活探測(cè)分節(jié),結(jié)果如下:
            1. 對(duì)方以期望的ACK響應(yīng),則一切正常,應(yīng)用程序得不到通知;
            2. 對(duì)方以RST響應(yīng),套接口的待處理錯(cuò)誤被置為ECONNRESET,套接口本身則被關(guān)閉;
            3. 對(duì)方對(duì)探測(cè)分節(jié)無(wú)任何響應(yīng),經(jīng)過(guò)重試都沒(méi)有任何響應(yīng),套接口的待處理錯(cuò)誤被置為ETIMEOUT,套接口本身被關(guān)閉;若接收到一個(gè)ICMP錯(cuò)誤作為某個(gè)探測(cè)分節(jié)的響應(yīng),則返回相應(yīng)錯(cuò)誤。
            此選項(xiàng)一般由服務(wù)器使用。服務(wù)器使用它是為了檢測(cè)出半開(kāi)連接并終止他們。

            SO_LINGER

             此選項(xiàng)指定函數(shù)close對(duì)面向連接的協(xié)議如何操作(如TCP)。缺省close操作是立即返回,如果有數(shù)據(jù)殘留在套接口緩沖區(qū)中則系統(tǒng)將試著將這些數(shù)據(jù)發(fā)送給對(duì)方。

            SO_LINGER選項(xiàng)用來(lái)改變此缺省設(shè)置。使用如下結(jié)構(gòu):
            struct linger {
                int l_onoff; /* 0 = off, nozero = on */
                int l_linger; /* linger time */
            };

            有下列三種情況:

            1. l_onoff為0,則該選項(xiàng)關(guān)閉,l_linger的值被忽略,等于缺省情況,close立即返回;
            2. l_onoff為非0,l_linger為0,則套接口關(guān)閉時(shí)TCP夭折連接,TCP將丟棄保留在套接口發(fā)送緩沖區(qū)中的任何數(shù)據(jù)并發(fā)送一個(gè)RST給對(duì)方,而不是通常的四分組終止序列,這避免了TIME_WAIT狀態(tài);
            3. l_onoff 為非0,l_linger為非0,當(dāng)套接口關(guān)閉時(shí)內(nèi)核將拖延一段時(shí)間(由l_linger決定)。如果套接口緩沖區(qū)中仍殘留數(shù)據(jù),進(jìn)程將處于睡眠狀態(tài),直 到(a)所有數(shù)據(jù)發(fā)送完且被對(duì)方確認(rèn),之后進(jìn)行正常的終止序列(描述字訪問(wèn)計(jì)數(shù)為0)或(b)延遲時(shí)間到。此種情況下,應(yīng)用程序檢查close的返回值是 非常重要的,如果在數(shù)據(jù)發(fā)送完并被確認(rèn)前時(shí)間到,close將返回EWOULDBLOCK錯(cuò)誤且套接口發(fā)送緩沖區(qū)中的任何數(shù)據(jù)都丟失。close的成功返 回僅告訴我們發(fā)送的數(shù)據(jù)(和FIN)已由對(duì)方TCP確認(rèn),它并不能告訴我們對(duì)方應(yīng)用進(jìn)程是否已讀了數(shù)據(jù)。如果套接口設(shè)為非阻塞的,它將不等待close完 成。
            l_linger的單位依賴于實(shí)現(xiàn),4.4BSD假設(shè)其單位是時(shí)鐘滴答(百分之一秒),但Posix.1g規(guī)定單位為秒。

            讓客戶知道服務(wù)器已經(jīng)讀其數(shù)據(jù)的一個(gè)方法時(shí):調(diào)用shutdown(SHUT_WR)而不是調(diào)用close,并等待對(duì)方close連接的本地(服務(wù)器)端。

            SO_OOBINLINE

             此選項(xiàng)打開(kāi)時(shí),帶外數(shù)據(jù)將被保留在正常的輸入隊(duì)列中(即在線存放)。當(dāng)發(fā)生這種情況時(shí),接收函數(shù)的MSG_OOB標(biāo)志不能用來(lái)讀帶外數(shù)據(jù)。

            SO_RCVBUF和SO_SNDBUF

             每個(gè)套接口都有一個(gè)發(fā)送緩沖區(qū)和一個(gè)接收緩沖區(qū),使用這兩個(gè)套接口選項(xiàng)可以改變?nèi)笔【彌_區(qū)大小。

            當(dāng)設(shè)置TCP套接口接收緩沖區(qū)的大小時(shí),函數(shù)調(diào)用順序是很重要的,因?yàn)門(mén)CP的窗口規(guī)模選項(xiàng)是在建立連接時(shí)用SYN與對(duì)方互換得到的。對(duì)于客戶,SO_RCVBUF選項(xiàng)必須在connect之前設(shè)置;對(duì)于服務(wù)器,SO_RCVBUF選項(xiàng)必須在listen前設(shè)置。

            TCP套接口緩沖區(qū)的大小至少是連接的MSS的三倍,而必須是連接的MSS的偶數(shù)倍。

            SO_RCVLOWAT和SO_SNDLOWAT

             每個(gè)套接口有一個(gè)接收低潮限度和一個(gè)發(fā)送低潮限度,他們由函數(shù)select使用。這兩個(gè)選項(xiàng)可以修改他們。

            接收低潮限度是讓select返回“可讀”而在套接口接收緩沖區(qū)中必須有的數(shù)據(jù)量,對(duì)于一個(gè)TCP或UDP套接口,此值缺省為1。發(fā)送低潮限度是讓select返回“可寫(xiě)”而在套接口發(fā)送緩沖區(qū)中必須有的可用空間,對(duì)于TCP套接口,此值常為2048。

            SO_RCVTIMEO和SO_SNDTIMEO

             使用這兩個(gè)選項(xiàng)可以給套接口設(shè)置一個(gè)接收和發(fā)送超時(shí)。通過(guò)設(shè)置參數(shù)的值為0秒和0微秒來(lái)禁止超時(shí)。缺省時(shí)兩個(gè)超時(shí)都是禁止的。

            接收超時(shí)影響5個(gè)輸入函數(shù):read、readv、recv、recvfrom和recvmsg;發(fā)送超時(shí)影響5個(gè)輸出函數(shù):write、writev、send、sendto和sendmsg。

            SO_REUSEADDR和SO_REUSEPORT

             SO_REUSEADDR提供如下四個(gè)功能:
            1. SO_REUSEADDR允許啟動(dòng)一個(gè)監(jiān)聽(tīng)服務(wù)器并捆綁其眾所周知端口,即使以前建立的將此端口用做他們的本地端口的連接仍存在。這通常是重啟監(jiān)聽(tīng)服務(wù)器時(shí)出現(xiàn),若不設(shè)置此選項(xiàng),則bind時(shí)將出錯(cuò)。
            2. SO_REUSEADDR允許在同一端口上啟動(dòng)同一服務(wù)器的多個(gè)實(shí)例,只要每個(gè)實(shí)例捆綁一個(gè)不同的本地IP地址即可。對(duì)于TCP,我們根本不可能啟動(dòng)捆綁相同IP地址和相同端口號(hào)的多個(gè)服務(wù)器。
            3. SO_REUSEADDR允許單個(gè)進(jìn)程捆綁同一端口到多個(gè)套接口上,只要每個(gè)捆綁指定不同的本地IP地址即可。這一般不用于TCP服務(wù)器。
            4. SO_REUSEADDR允許完全重復(fù)的捆綁:當(dāng)一個(gè)IP地址和端口綁定到某個(gè)套接口上時(shí),還允許此IP地址和端口捆綁到另一個(gè)套接口上。一般來(lái)說(shuō),這個(gè)特性僅在支持多播的系統(tǒng)上才有,而且只對(duì)UDP套接口而言(TCP不支持多播)。
            SO_REUSEPORT選項(xiàng)有如下語(yǔ)義:
            1. 此選項(xiàng)允許完全重復(fù)捆綁,但僅在想捆綁相同IP地址和端口的套接口都指定了此套接口選項(xiàng)才性。
            2. 如果被捆綁的IP地址是一個(gè)多播地址,則SO_REUSEADDR和SO_REUSEPORT等效。
            使用這兩個(gè)套接口選項(xiàng)的建議:
            1. 在所有TCP服務(wù)器中,在調(diào)用bind之前設(shè)置SO_REUSEADDR套接口選項(xiàng);
            2. 當(dāng)編寫(xiě)一個(gè)同一時(shí)刻在同一主機(jī)上可運(yùn)行多次的多播應(yīng)用程序時(shí),設(shè)置SO_REUSEADDR選項(xiàng),并將本組的多播地址作為本地IP地址捆綁。

            SO_TYPE

             該選項(xiàng)返回套接口的類型,返回的整數(shù)值是一個(gè)諸如SOCK_STREAM或SOCK_DGRAM這樣的值。

            SO_USELOOPBACK

             該選項(xiàng)僅用于路由域(AF_ROUTE)的套接口,它對(duì)這些套接口的缺省設(shè)置為打開(kāi)(這是唯一一個(gè)缺省為打開(kāi)而不是關(guān)閉的SO_xxx套接口選項(xiàng))。當(dāng)此套接口打開(kāi)時(shí),套接口接收在其上發(fā)送的任何數(shù)據(jù)的一個(gè)拷貝。

            禁止這些回饋拷貝的另一個(gè)方法是shutdown,第二個(gè)參數(shù)應(yīng)設(shè)為SHUT_RD。

            IP_HDRINCL

             如果一個(gè)原始套接口設(shè)置該選項(xiàng),則我們必須為所有發(fā)送到此原始套接口上的數(shù)據(jù)報(bào)構(gòu)造自己的IP頭部。

            IP_OPTIONS

             設(shè)置此選項(xiàng)允許我們?cè)贗Pv4頭部中設(shè)置IP選項(xiàng)。這要求掌握IP頭部中IP選項(xiàng)的格式信息。

            IP_RECVDSTADDR

             該選項(xiàng)導(dǎo)致所接收到的UDP數(shù)據(jù)報(bào)的目的IP地址由函數(shù)recvmsg作為輔助數(shù)據(jù)返回。

            IP_RECVIF

             該選項(xiàng)導(dǎo)致所接收到的UDP數(shù)據(jù)報(bào)的接口索引由函數(shù)recvmsg作為輔助數(shù)據(jù)返回。

            IP_TOS

             該選項(xiàng)使我們可以給TCP或UDP套接口在IP頭部中設(shè)置服務(wù)類型字段。如果我們給此選項(xiàng)調(diào)用getsockopt,則放到外出IP數(shù)據(jù)報(bào)頭部的TOS字段中的當(dāng)前值將返回(缺省為0)。還沒(méi)有辦法從接收到的IP數(shù)據(jù)報(bào)中取此值。

            可以將TOS設(shè)置為如下的值:

            • IPTOS_LOWDELAY:最小化延遲
            • IPTOS_THROUGHPUT:最大化吞吐量
            • IPTOS_RELIABILITY:最大化可靠性
            • IPTOS_LOWCOST:最小化成本

            IP_TTL

             用次選項(xiàng),可以設(shè)置和獲取系統(tǒng)用于某個(gè)給定套接口的缺省TTL值(存活時(shí)間字段)。與TOS一樣,沒(méi)有辦法從接收到的數(shù)據(jù)報(bào)中得到此值。

            ICMP6_FILTER

             可獲取和設(shè)置一個(gè)icmp6_filter結(jié)構(gòu),他指明256個(gè)可能的ICMPv6消息類型中哪一個(gè)傳遞給在原始套接口上的進(jìn)程。

            IPV6_ADDRFORM

             允許套接口從IPv4轉(zhuǎn)換到IPv6,反之亦可。

            IPV6_CHECKSUM

             指定用戶數(shù)據(jù)中校驗(yàn)和所處位置的字節(jié)偏移。如果此值為非負(fù),則內(nèi)核將(1)給所有外出分組計(jì)算并存儲(chǔ)校驗(yàn)和;(2)輸入時(shí)檢查所收到的分組的校驗(yàn)和,丟棄 帶有無(wú)效校驗(yàn)和的分組。此選項(xiàng)影響出ICMPv6原始套接口外的所有IPv6套接口。如果指定的值為-1(缺省值),內(nèi)核在此原始套接口上將不給外出的分 組計(jì)算并存儲(chǔ)校驗(yàn)和,也不檢查所收到的分組的校驗(yàn)和。

            IPV6_DSTOPTS

             設(shè)置此選項(xiàng)指明:任何接收到的IPv6目標(biāo)選項(xiàng)都將由recvmsg作為輔助數(shù)據(jù)返回。此選項(xiàng)缺省為關(guān)閉。

            IPV6_HOPLIMIT

             設(shè)置此選項(xiàng)指明:接收到的跳限字段將由recvmsg作為輔助數(shù)據(jù)返回。

            IPV6_HOPOPTS

             設(shè)置此選項(xiàng)指明:任何接收到的步跳選項(xiàng)都將由recvmsg作為輔助數(shù)據(jù)返回。

            IPV6_NEXTHOP

             這不是一個(gè)套接口選項(xiàng),而是一個(gè)可指定個(gè)sendmsg的輔助數(shù)據(jù)對(duì)象的類型。此對(duì)象以一個(gè)套接口地址結(jié)構(gòu)指定某個(gè)數(shù)據(jù)報(bào)的下一跳地址。

            IPV6_PKTINFO

             設(shè)置此選項(xiàng)指明:下面關(guān)于接收到的IPv6數(shù)據(jù)報(bào)的兩條信息將由recvmsg作為輔助數(shù)據(jù)返回:目的IPv6地址和到達(dá)接口索引。

            IPV6_PKTOPTIONS

             大多數(shù)IPv6套接口選項(xiàng)假設(shè)UDP套接口使用recvmsg和sendmsg所用的輔助數(shù)據(jù)在內(nèi)核與應(yīng)用進(jìn)程間傳遞信息。TCP套接口使用IPV6_PKTOPTIONS來(lái)獲取和存儲(chǔ)這些值。

            IPV6_RTHDR

             設(shè)置此選項(xiàng)指明:接收到的IPv6路由頭部將由recvmsg作為輔助數(shù)據(jù)返回。

            IPV6_UNICAST_HOPS

             類似于IPv4的IP_TTL,它的設(shè)置指定發(fā)送到套接口上的外出數(shù)據(jù)報(bào)的缺省跳限,而它的獲取則返回內(nèi)核將用于套接口的跳限值。為了從接收到的IPv6數(shù)據(jù)報(bào)中得到真實(shí)的跳限字段,要求使用IPV6_HOPLIMIT套接口選項(xiàng)。

            TCP_KEEPALIVE

             它指定TCP開(kāi)始發(fā)送保持存活探測(cè)分節(jié)前以秒為單位的連接空閑時(shí)間。缺省值至少為7200秒,即2小時(shí)。該選項(xiàng)僅在SO_KEEPALIVE套接口選項(xiàng)打開(kāi)時(shí)才有效。

            TCP_MAXRT

             它指定一旦TCP開(kāi)始重傳數(shù)據(jù),在連接斷開(kāi)之前需經(jīng)歷的以秒為單位的時(shí)間總量。值0意味著使用系統(tǒng)缺省值,值-1意味著永遠(yuǎn)重傳數(shù)據(jù)。

            TCP_MAXSEG

             允許獲取或設(shè)置TCP連接的最大分節(jié)大小(MSS)。返回值是我們的TCP發(fā)送給另一端的最大數(shù)據(jù)量,他常常就是由另一端用SYN分節(jié)通告的MSS,除非 我們的TCP選擇使用一個(gè)比對(duì)方通告的MSS小的值。如果此選項(xiàng)在套接口連接之前取得,則返回值為未從另一端收到的MSS選項(xiàng)的情況下所用的缺省值。

            TCP_NODELAY

             如果設(shè)置,此選項(xiàng)禁止TCP的Nagle算法。缺省時(shí),該算法是使能的。

            Nagle算法的目的是減少WAN上小分組的數(shù)目。

            Nagle算法常常與另一個(gè)TCP算法聯(lián)合使用:延遲ACK(delayed ACK)算法。

            解決多次寫(xiě)導(dǎo)致Nagle算法和延遲ACK算法負(fù)面影響的方法:

            1. 使用writev而不是多次write;
            2. 合并緩沖區(qū),對(duì)此緩沖區(qū)使用一次write;
            3. 設(shè)置TCP_NODELAY選項(xiàng),繼續(xù)調(diào)用write多次,這是最不可取的解決方法。

            TCP_STDURG

             它影響對(duì)TCP緊急指針的解釋。

            19.處理套接口的fcntl函數(shù)

             #include <fcntl.h>
            int fcntl(int fd, int cmd, … /* arg */);
            返回:依賴于參數(shù)cmd—成功,-1—失敗。

            函數(shù)fcntl提供了如下關(guān)于網(wǎng)絡(luò)編程的特性:

            1. 非阻塞I/O:通過(guò)用F_SETFL命令設(shè)置O_NONBLOCK文件狀態(tài)標(biāo)志來(lái)設(shè)置套接口為非阻塞型。
            2. 信號(hào)驅(qū)動(dòng)I/O:用F_SETFL命令來(lái)設(shè)置O_ASYNC文件狀態(tài)標(biāo)志,這導(dǎo)致在套接口狀態(tài)發(fā)生變化時(shí)內(nèi)核生成信號(hào)SIGIO。
            3. F_SETOWN命令設(shè)置套接口屬主(進(jìn)程ID或進(jìn)程組ID),由它來(lái)接收信號(hào)SIGIO和SIGURG。SIGIO在設(shè)置套接口為信號(hào)驅(qū)動(dòng)I/O型時(shí)生成,SIGURG在新的帶外數(shù)據(jù)到達(dá)套接口時(shí)生成。
            4. F_GETOWN命令返回套接口的當(dāng)前屬主。
            注意事項(xiàng):
            • 設(shè)置某個(gè)文件狀態(tài)標(biāo)志時(shí),先取得當(dāng)前標(biāo)志,與新標(biāo)志路邏輯或后再設(shè)置標(biāo)志。
            • 信號(hào)SIGIO和SIGURG與其他信號(hào)不同之處在于,這兩個(gè)信號(hào)只有在已使用命令F_SETOWN給套接口指派了屬主后才會(huì)生成。F_SETOWN命令的整參數(shù)arg既可以是一個(gè)正整數(shù),指明接收信號(hào)的進(jìn)程ID,也可以是一個(gè)負(fù)整數(shù),它的絕對(duì)值是接收信號(hào)的進(jìn)程組ID。
            • 當(dāng)一個(gè)新的套接口由函數(shù)socket創(chuàng)建時(shí),他沒(méi)有屬主,但是當(dāng)一個(gè)新的套接口從一個(gè)監(jiān)聽(tīng)套接口創(chuàng)建時(shí),套接口屬主便由已連接套接口從監(jiān)聽(tīng)套接口繼承而來(lái)。

            20.gethostbyname函數(shù)

             #include <netdb.h>
            struct hostent *gethostbyname(const char *hostname);
            返回:非空指針—成功,空指針—出錯(cuò),同時(shí)設(shè)置h_errno。

            函數(shù)返回的非空指針指向的結(jié)構(gòu)如下:
            struct hostent {
                char *h_name; /*規(guī)范主機(jī)名 */
                char **h_aliases; /* 別名列表 */
                int h_addrtype; /* AF_INET or AF_INET6 */
                int h_length; /* 地址長(zhǎng)度 */
                char **h_addr_list; /* IPv4或IPv6地址結(jié)構(gòu)列表 */
            };
            #define h_addr h_addr_list[0];

            按照DNS的說(shuō)法,gethostbyname執(zhí)行一個(gè)對(duì)A記錄的查詢或?qū)AAA記錄的查詢,返回IPv4或IPv6地址。

            h_addr的定義是為了兼容,在新代碼中不應(yīng)使用。

            返回的h_name稱為主機(jī)的規(guī)范(canonical)名字。當(dāng)返回IPv6地址時(shí),h_addrtype被設(shè)置為AF_INET6,成員h_length被設(shè)置為16。

            gethostbyname的特殊之處在于:當(dāng)發(fā)生錯(cuò)誤時(shí),他不設(shè)置errno,而是將全局整數(shù)h_errno設(shè)置為定義在頭文件<netdb.h>中的下列常值中的一個(gè):

            • HOST_NOT_FOUND;
            • TRY_AGAIN;
            • NO_RECOVERY;
            • NO_DATA(等同于NO_ADDRESS)。
            有函數(shù)hstrerror(),它將h_errno的值作為唯一的參數(shù),返回一個(gè)指向相應(yīng)錯(cuò)誤說(shuō)明的const char *型指針。

            DNS小常識(shí):

             DNS中的條目稱為資源記錄RR(resource record),僅有少數(shù)幾類RR會(huì)影響我們的名字與地址轉(zhuǎn)換:
            • A:A記錄將主機(jī)名映射為32位的IPv4地址;
            • AAAA:“四A”記錄將主機(jī)名映射為128位的IPv6地址;
            • PTR:PTR記錄(稱為“指針記錄”)將IP地址映射為主機(jī)名;
            • MX:MX記錄指定一主機(jī)作為某主機(jī)的“郵件交換器”。
            • CNAME:CNAME代表“canonical name(規(guī)范名字)”,其常見(jiàn)的用法是為常用服務(wù)如ftp和www指派一個(gè)CNAME記錄。

            21.gethostbyname2函數(shù)

             #include <netdb.h>
            struct hostent *gethostbyname2(const char *hostname, int family);
            返回:非空指針—成功,空指針—出錯(cuò),同時(shí)設(shè)置h_errno。

            該函數(shù)允許指定地址族,其他與gethostbyname相似。


            22.gethostbyaddr函數(shù)

             #include <netdb.h>
            struct hostent *gethostbyaddr(const char *addr, size_t len, int family);
            返回:非空指針—成功,空指針—出錯(cuò),同時(shí)設(shè)置h_error。

            函數(shù)根據(jù)一個(gè)二進(jìn)制的IP地址并試圖找出相應(yīng)于此地址的主機(jī)名,我們關(guān)心的是規(guī)范主機(jī)名h_name。

            參數(shù)addr不是char *類型,而是一個(gè)真正指向含有IPv4或IPv6地址的結(jié)構(gòu)in_addr或in6_addr的指針;len是該結(jié)構(gòu)的大小,對(duì)于IPv4是4,對(duì)于IPv6是16;family或?yàn)锳F_INET或?yàn)锳F_INET6。

            按照DNS的說(shuō)法,該函數(shù)查詢PTR記錄。


            23.uname函數(shù)

             #include <sys/utsname.h>
            int uname(struct utsname *name);
            返回:非負(fù)值—成功,-1—失敗。

            返回當(dāng)前主機(jī)的名字,存放在如下的結(jié)構(gòu)里:
            #define UTS_NAMESIZE 16
            #define UTS_NODESIZE 256
            struct utsname {
                char sysname[UTS_NAMESIZE];
                char nodename[UTS_NODESIZE];
                char release[UTS_NAMESIZE];
                char version[UTS_NAMESIZE];
                char machine[UTS_NAMESIZE];
            };

            該函數(shù)經(jīng)常與gethostbyname一起用來(lái)確定本機(jī)的IP地址:先調(diào)用uname獲得主機(jī)名字,然后調(diào)用gethostbyname得到所有的IP地址。

            獲得本機(jī)IP地址的另一個(gè)方法是ioctl的命令SIOCGIFCONF。


            24.gethostname函數(shù)

             #include <unistd.h>
            int gethostname(char *name, size_t namelen);
            返回:0—成功,-1—失敗。

            返回當(dāng)前主機(jī)的名字。name是指向主機(jī)名存儲(chǔ)位置的指針,namelen是此數(shù)組的大小,如果有空間,主機(jī)名以空字符結(jié)束。

            主機(jī)名的最大大小通常是頭文件<sys/param.h>定義的常值MAXHOSTNAMELEN。


            25.getservbyname函數(shù)

             #include <netdb.h>
            struct servent *getservbyname(const char *servname, const char *protoname);
            返回:非空指針—成功,空指針—失敗。

            函數(shù)返回如下結(jié)構(gòu)的指針:
            struct servent {
                char *s_name;
                char **s_aliases;
                int s_port;
                char *s_proto;
            };

            服務(wù)名servname必須指定,如果還指定了協(xié)議(protoname為非空指針),則結(jié)果表項(xiàng)必須有匹配的記錄。如果沒(méi)有指定協(xié)議名而服務(wù)支持多個(gè)協(xié)議,則返回哪個(gè)端口是依賴于實(shí)現(xiàn)的。

            結(jié)構(gòu)中的端口號(hào)是以網(wǎng)絡(luò)字節(jié)序返回的,所以在將它存儲(chǔ)在套接口地址結(jié)構(gòu)時(shí),絕對(duì)不能調(diào)用htons。


            26.getservbyport函數(shù)

             #include <netdb.h>
            struct servent *getservbyport(int port, const char *protname);
            返回:非空指針—成功,空指針—出錯(cuò)。

            port必須為網(wǎng)絡(luò)字節(jié)序。例如:
            sptr = getservbyport(htons(53), “udp”);


            27.recv和send

             #include <sys/socket.h>
            ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);
            ssize_t send(int sockfd, void *buf, size_t nbytes, int flags);
            返回:成功返回讀入或?qū)懗龅淖止?jié)數(shù),出錯(cuò)返回-1。

            前三個(gè)參數(shù)與read和write相同,參數(shù)flags的值或?yàn)?,或由以下的一個(gè)或多個(gè)常值邏輯或構(gòu)成:

            flags描述recvsend




            MSG_DONTROUTE不查路由表
            y
            MSG_DONTWAIT本操作不阻塞yy
            MSG_OOB發(fā)送或接收帶外數(shù)據(jù)yy
            MSG_PEEK查看外來(lái)的消息y
            MSG_WAITALL等待所有數(shù)據(jù)y

            下面說(shuō)明每個(gè)標(biāo)志的作用:

            • MSG_DONTROUTE:這個(gè)標(biāo)志告訴內(nèi)核目的主機(jī)在直接連接的本地網(wǎng)絡(luò)上,不要查路由表。這是對(duì)提供這種特性的SO_DONTROUTE套接口選項(xiàng)的補(bǔ)充。該標(biāo)志可以對(duì)單個(gè)輸出操作提供這種特性,而套接口選項(xiàng)則針對(duì)某個(gè)套接口上的所有輸出操作。
            • MSG_DONTWAIT:這個(gè)標(biāo)志將單個(gè)I/O操作設(shè)為非阻塞方式,而不需要在套接口上打開(kāi)非阻塞標(biāo)志,執(zhí)行I/O操作,然后關(guān)閉阻塞標(biāo)志。
            • MSG_OOB:用send時(shí),這個(gè)標(biāo)志指明發(fā)送的是帶外數(shù)據(jù),用recv時(shí),該標(biāo)志指明要讀的是帶外數(shù)據(jù)而不是一般數(shù)據(jù)。
            • MSG_PEEK:這個(gè)標(biāo)志可以讓我們查看可讀的數(shù)據(jù),在recv或recvfrom后系統(tǒng)不會(huì)將這些數(shù)據(jù)丟棄。
            • MSG_WAITALL: 由4.3BSD Reno引入,他告訴內(nèi)核在沒(méi)有讀到請(qǐng)求的字節(jié)數(shù)之前不使讀操作返回。如果系統(tǒng)支持這個(gè)標(biāo)志,則可以去掉readn函數(shù)。即使設(shè)定了該標(biāo)志 ,如果發(fā)生如下情況:(1)捕獲了一個(gè)信號(hào);(2)連接被終止;(3)在套接口上發(fā)生錯(cuò)誤,這個(gè)函數(shù)返回的字節(jié)數(shù)仍會(huì)比請(qǐng)求的少。

            28.readv和writev

             #include <sys/uio.h>
            ssize_t readv(int filedes, const struct iovec *iov, int iovcnt);
            ssize_t writev(int filedes, const struct iovec *iov, int iovcnt);
            返回:讀到或?qū)懗龅淖止?jié)數(shù),出錯(cuò)返回-1。

            readv和writev可以讓我們?cè)谝粋€(gè)函數(shù)調(diào)用中讀或?qū)懚鄠€(gè)緩沖區(qū),這些操作被稱為分散讀和集中寫(xiě)。

            iovec結(jié)構(gòu)定義如下:
            struct iovec {
                void *iov_base; /* starting address of buffer */
                size_t iov_len; /* size of buffer */
            };

            在具體的實(shí)現(xiàn)中對(duì)iovec結(jié)構(gòu)數(shù)組的元素個(gè)數(shù)有限制,4.3BSD最多允許1024個(gè),而Solaris2.5上限是16。Posix.1g要求定義一個(gè)常值IOV_MAX,而且它的值不小于16。

            readv和writev可用于任何描述字。writev是一個(gè)原子操作,可以避免多次寫(xiě)引發(fā)的Nagle算法。


            29.readmsg和writemsg

             #include <sys/socket.h>
            ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
            ssize_t sendmsg(int sockfd, struct msghdr *msg, int flags);
            返回:成功時(shí)為讀入或?qū)懗龅淖止?jié)數(shù),出錯(cuò)時(shí)為-1。

            這兩個(gè)函數(shù)是最通用的套接口I/O函數(shù),可以用recvmsg代替read、readv、recv和recvfrom,同樣,各種輸出函數(shù)都可以用sendmsg代替。

            參數(shù)msghdr結(jié)構(gòu)的定義如下:
            struct msghdr {
                void *msg_name; /* protocol address */
                socklen_t msg_namelen; /* size of protocol address */
                struct iovec *msg_iov; /* scatter/gather array */
                size_t msg_iovlen; /* elements in msg_iov */
                void *msg_control; /* ancillary data; must be aligned for a cmsghdr structure */
                socklen_t msg_controllen; /* length of ancillary data */
                int msg_flags; /* flags returned by recvmsg() */
            };

            該結(jié)構(gòu)源自4.3BSD Reno,也是Posix.1g中所說(shuō)明的,有些系統(tǒng)仍使用一種老的msghdr結(jié)構(gòu),此種結(jié)構(gòu)中沒(méi)有msg_flags成員,而且 msg_control和msg_controllen成員分別被叫做msg_accrights和msg_accrightslen。老系統(tǒng)中支持的唯 一一種輔助數(shù)據(jù)形式是文件描述字(稱為訪問(wèn)權(quán)限)的傳遞。

            msg_name和msg_namelen成員用于未經(jīng)連接的套接口,他們與 recvfrom和sendto的第五和第六個(gè)參數(shù)類似:msg_name指向一個(gè)套接口地址結(jié)構(gòu),如果不需要指明協(xié)議地址,msg_name應(yīng)被設(shè)置為 空指針,msg_namelen對(duì)sendmsg是一個(gè)值,而對(duì)recvmsg是一個(gè)值-結(jié)果參數(shù)。

            msg_iov和msg_iovlen成員指明輸入或輸出的緩沖區(qū)數(shù)組。

            msg_control和msg_controllen指明可選的輔助數(shù)據(jù)的位置和大小,msg_controllen對(duì)recvmsg是一個(gè)值-結(jié)果參數(shù)。

            msg_flags只用于revmsg,調(diào)用recvmsg時(shí),flags參數(shù)被拷貝 到msg_flags成員,而且內(nèi)核用這個(gè)值進(jìn)行接收處理,接著它的值會(huì)根據(jù)recvmsg的結(jié)果而更新,sendmsg會(huì)忽略msg_flags成員, 因?yàn)樗谶M(jìn)行輸出處理時(shí)使用flags參數(shù)。

            內(nèi)核檢查的flags和返回的msg_flags如下表所示:

            標(biāo)志在send flags、
            sendto flags、
            sendmsg flags中檢查
            在recv flags、
            recvfrom flags、
            recvmsg flags中檢查
            在recvmsg msg_flags
            中返回




            MSG_DONTROUTEy

            MSG_DONTWAITyy
            MSG_PEEK
            y
            MSG_WAITALL
            y
            MSG_EORy
            y
            MSG_OOByyy
            MSG_BCAST

            y
            MSG_MCAST

            y
            MSG_TRUNC

            y
            MSG_CTRUNC

            y

            前四個(gè)標(biāo)志只檢查不返回,下兩個(gè)標(biāo)志既檢查又返回,最后四個(gè)只返回。返回的六個(gè)標(biāo)志含義如下:

            • MSG_BCAST:當(dāng)收到的數(shù)據(jù)報(bào)是一個(gè)鏈路層的廣播或其目的IP地址為廣播地址時(shí),將返回此標(biāo)志。
            • MSG_MCAST:當(dāng)收到的數(shù)據(jù)報(bào)是鏈路層的多播時(shí),將返回該標(biāo)志。
            • MSG_TRUNC:這個(gè)標(biāo)志在數(shù)據(jù)報(bào)被截?cái)鄷r(shí)返回。
            • MSG_CTRUNC:這個(gè)標(biāo)志在輔助數(shù)據(jù)被截?cái)鄷r(shí)返回。
            • MSG_EOR:如果返回的數(shù)據(jù)不是一個(gè)邏輯記錄的結(jié)尾,該標(biāo)志被清位,反之則置位。TCP不使用這個(gè)標(biāo)志,因?yàn)樗且环N字節(jié)流協(xié)議。
            • MSG_OOB:這個(gè)標(biāo)志不是為T(mén)CP的帶外數(shù)據(jù)返回的,它用于其他協(xié)議族(譬如OSI協(xié)議等)。
            具體的實(shí)現(xiàn)可能會(huì)在msg_flags中返回一些輸入的flags的標(biāo)志,所以我們應(yīng)該只檢查那些感興趣的標(biāo)志的值。

            30.socketpair函數(shù)

             #include <sys/socket.h>
            int socketpair(int family, int type, int protocol, int sockfd[2]);
            返回:成功返回0,出錯(cuò)返回-1。

            family必須為AF_LOCAL,protocol必須為0,type可以是SOCK_STREAM或SOCK_DGRAM。新創(chuàng)建的兩個(gè)套接口描述字作為sockfd[0]和sockfd[1]返回。

            這兩個(gè)描述字相互連接,沒(méi)有名字,即沒(méi)有涉及隱式bind。

            以SOCK_STREAM作為type調(diào)用所得到的結(jié)果稱為流管道(stream pipe)。這與一般的UNIX管道類似,但流管道是全雙工的,兩個(gè)描述字都是可讀寫(xiě)的。


            31.套接口ioctl函數(shù)

             #include <unistd.h>
            int ioctl(int fd, int request, … /* void *arg */ );
            返回:成功返回0,出錯(cuò)返回-1。

            第三個(gè)參數(shù)總是一個(gè)指針,但指針的類型依賴于request。

            ioctl和網(wǎng)絡(luò)有關(guān)的請(qǐng)求可分為如下6類:

            類別request描述數(shù)據(jù)類型




            套接口SIOCATMARK在帶外標(biāo)志上嗎int

            SIOCSPGRP設(shè)置套接口的進(jìn)程ID或進(jìn)程組IDint

            SIOCGPGRP獲取套接口的進(jìn)程ID或進(jìn)程組IDint




            文件FIONBIO設(shè)置/清除非阻塞標(biāo)志int

            FIOASYNC設(shè)置/清除異步I/O標(biāo)志int

            FIONREAD獲取接收緩沖區(qū)中的字節(jié)數(shù)int

            FIOSETOWN設(shè)置文件的進(jìn)程ID或進(jìn)程組IDint

            FIOGETOWN獲取文件的進(jìn)程ID或進(jìn)程組IDint




            接口SIOCGIFCONF獲取所有接口的列表struct ifconf

            SIOCSIFADDR設(shè)置接口地址struct ifreq

            SIOCGIFADDR獲取接口地址struct ifreq

            SIOCSIFFLAGS設(shè)置接口標(biāo)志struct ifreq

            SIOCGIFFLAGS獲取接口標(biāo)志struct ifreq

            SIOCSIFDSTADDR設(shè)置點(diǎn)到點(diǎn)地址struct ifreq

            SIOCGIFDSTADDR獲取點(diǎn)到點(diǎn)地址struct ifreq

            SIOCGIFBRDADDR  獲取廣播地址struct ifreq

            SIOCSIFBRDADDR設(shè)置廣播地址struct ifreq

            SIOCGIFNETMASK獲取子網(wǎng)掩碼struct ifreq

            SIOCSIFNETMASK設(shè)置子網(wǎng)掩碼struct ifreq

            SIOCGIFMETRIC獲取接口的測(cè)度(metric)struct ifreq

            SIOCSIFMETRIC設(shè)置接口的測(cè)度(metric)struct ifreq

            SIOCxxx(有很多,依賴于實(shí)現(xiàn))




            ARPSIOCSARP創(chuàng)建/修改ARP項(xiàng)struct arpreq

            SIOCGARP獲取ARP項(xiàng)struct arpreq

            SIOCDARP刪除ARP項(xiàng)struct arpreq




            路由SIOCADDRT增加路徑struct rtentry

            SIOCDELRT刪除路徑struct rtentry




            I_xxx

            (1)套接口操作

            • SIOCATMARK:如果套接口的讀指針當(dāng)前在帶外標(biāo)志上,則通過(guò)第三個(gè)參數(shù)指向的整數(shù)返回一個(gè)非零值,否則返回零。Posix.1g用sockatmark代替了這種請(qǐng)求。
            • SIOCGPGRP:通過(guò)第三個(gè)參數(shù)指向的整數(shù)返回為接收來(lái)自這個(gè)套接口的SIGIO或SIGURG信號(hào)而設(shè)置的進(jìn)程ID或進(jìn)程組ID。這和fcntl的F_GETOWN相同。
            • SIOCSPGRP:用第三個(gè)參數(shù)指向的整數(shù)設(shè)置進(jìn)程ID或進(jìn)程組ID以接收這個(gè)套接口的SIGIO或SIGURG信號(hào)。這和fcntl的F_SETOWN相同。

            (2)文件操作

            • FIONBIO:套接口的非阻塞標(biāo)志會(huì)根據(jù)第三個(gè)參數(shù)指向的值是否為零而清除或設(shè)置。等價(jià)于fcntl的F_SETFL設(shè)置/清除O_NONBLOCK標(biāo)志。
            • FIOASYNC:根據(jù)第三個(gè)參數(shù)指向的值是否為零決定清除或接收套接口上的異步I/O信號(hào)。等價(jià)于fcntl的F_SETFL設(shè)置和清除O_AYNC標(biāo)志。
            • FIONREAD:在第三個(gè)參數(shù)指向的整數(shù)中返回套接口接收緩沖區(qū)中當(dāng)前的字節(jié)數(shù)。
            • FIOSETOWN:在套接口上等價(jià)于SIOCSPGRP。
            • FIOGETOWN:在套接口上等價(jià)于SIOCGPGRP。

            (3)接口配置

             SIOCGIFCONF:從內(nèi)核中獲取系統(tǒng)中配置的所有接口。它使用了結(jié)構(gòu)ifconf,ifconf又使用了ifreq結(jié)構(gòu)。

            結(jié)構(gòu)定義如下:
            struct ifconf {
                int ifc_len; /* size of buffer, value-result */
                union {
                    caddr_t ifcu_buf; /* input from user->kernel */
                    struct ifreq *ifcu_req; /* return from kernel->user */
                }ifc_ifcu;
            };
            #define ifc_buf ifc_ifcu.ifcu_buf
            #define ifc_req ifc_ifcu.ifcu_req
            #define IFNAMSIZ 16

            struct ifreq {
                char ifr_name[IFNAMSIZ];
                union {
                    struct sockaddr ifru_addr;
                    struct sockaddr ifru_dstaddr;
                    struct sockaddr ifru_broadaddr;
                    short ifru_flags;
                    int ifru_metric;
                    caddr_t ifru_data;
                }ifr_ifru;
            };
            #define ifr_addr ifr_ifru.ifru_addr
            #define ifr_dstaddr ifr_ifru.ifru_dstaddr
            #define ifr_broadaddr ifr_ifru.broadaddr
            #define ifr_flags ifr_ifru.ifru_flags
            #define ifr_metric ifr_ifru.ifru_metric
            #define ifr_data ifr_ifru.ifru_data

            在調(diào)用ioctl之前分配一個(gè)緩沖區(qū)和一個(gè)ifconf結(jié)構(gòu),然后初始化后者,iotctl的第三個(gè)參數(shù)指向ifconf結(jié)構(gòu)。

            一個(gè)實(shí)現(xiàn)獲取所有接口的程序,可參見(jiàn)unpv12e:lib/get_ifi_info.c

            (4)接口操作

            • SIOCGIFCONF:從內(nèi)核中獲取系統(tǒng)中配置的所有接口。

            (5)ARP高速緩存操作

             

            (6)路由表操作

            http://www.cnblogs.com/riky/archive/2006/11/24/570713.aspx

            posted on 2012-07-13 10:28 tqsheng 閱讀(318) 評(píng)論(0)  編輯 收藏 引用


            只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   Chat2DB   管理


            精品无码久久久久国产| 香蕉久久永久视频| 狠狠干狠狠久久| 久久久久国色AV免费观看| 久久天天躁狠狠躁夜夜2020| 欧美久久久久久| 国产精品久久成人影院| 无码人妻少妇久久中文字幕 | 思思久久99热只有频精品66| 无码人妻少妇久久中文字幕蜜桃| 久久这里只有精品久久| 久久综合视频网| 久久青青草原国产精品免费| 国产精品久久久久久久app| 亚洲中文字幕无码久久2020| 国产毛片久久久久久国产毛片| 久久这里都是精品| 国产精品欧美久久久久天天影视| 久久亚洲私人国产精品| 久久乐国产精品亚洲综合| 久久99热精品| 97精品国产97久久久久久免费 | 老司机午夜网站国内精品久久久久久久久 | 久久这里的只有是精品23| 久久国产精品久久| 久久精品天天中文字幕人妻| 欧美国产成人久久精品| 色婷婷久久久SWAG精品| 国产精品xxxx国产喷水亚洲国产精品无码久久一区| 欧美一级久久久久久久大| 91久久精品国产免费直播| 久久狠狠高潮亚洲精品| 亚洲AV无码久久精品蜜桃| 免费精品久久天干天干| 久久人妻少妇嫩草AV无码蜜桃 | 久久人妻无码中文字幕| 久久嫩草影院免费看夜色| 亚洲国产成人久久精品99| 久久青青国产| 亚洲AV伊人久久青青草原| 久久青青草视频|