posts - 189,comments - 115,trackbacks - 0

          Linux C編程---網(wǎng)絡(luò)編程

          摘要

            網(wǎng)絡(luò)編程,一定離不開套接口;那什么是套接口呢?在Linux下,所有的I/O操作都是通過讀寫文件描述符而產(chǎn)生的,文件描述符是一個(gè) 和打開的文件相關(guān)聯(lián)的整數(shù),這個(gè)文件并不只包括真正存儲(chǔ)在磁盤上的文件,還包括一個(gè)網(wǎng)絡(luò)連接、一個(gè)命名管道、一個(gè)終端等,而套接口就是系統(tǒng)進(jìn)程和文件描述 符通信的一種方法。目前最常用的套接口是字:字節(jié)流套接口(基于TCP)和數(shù)據(jù)報(bào)套接口(基于UDP),當(dāng)然還有原始套接口(原始套接口提供TCP套接口 和UDP套接口所不提供的功能,如構(gòu)造自己的TCP或UDP分組)等,我們這里主要介紹字節(jié)流套接口和數(shù)據(jù)報(bào)套接口。




          By Wing


            網(wǎng)絡(luò)編程,一定離不開套接口;那什么是套接口呢?在Linux下,所有的I/O操作都是通過讀寫文件描述符而產(chǎn)生的,文件描述符是一個(gè) 和打開的文件相關(guān)聯(lián)的整數(shù),這個(gè)文件并不只包括真正存儲(chǔ)在磁盤上的文件,還包括一個(gè)網(wǎng)絡(luò)連接、一個(gè)命名管道、一個(gè)終端等,而套接口就是系統(tǒng)進(jìn)程和文件描述 符通信的一種方法。目前最常用的套接口是字:字節(jié)流套接口(基于TCP)和數(shù)據(jù)報(bào)套接口(基于UDP),當(dāng)然還有原始套接口(原始套接口提供TCP套接口 和UDP套接口所不提供的功能,如構(gòu)造自己的TCP或UDP分組)等,我們這里主要介紹字節(jié)流套接口和數(shù)據(jù)報(bào)套接口。


            要學(xué)習(xí)網(wǎng)絡(luò)編程,一定離不開網(wǎng)絡(luò)庫的函數(shù),在Linux系統(tǒng)下,可以用"man 函數(shù)名"來得到這個(gè)函數(shù)的幫助,不過為了照顧E文不大好的朋友,下面就將常用的網(wǎng)絡(luò)函數(shù)和用法列出來供大家參考:


           ?。薄ocket函數(shù):為了執(zhí)行網(wǎng)絡(luò)輸入輸出,一個(gè)進(jìn)程必須做的第一件事就是調(diào)用socket函數(shù)獲得一個(gè)文件描述符。






          -----------------------------------------------------------------



          #include <sys/socket.h>



          int socket(int family,int type,int protocol);    



          返回:非負(fù)描述字---成功   -1---失敗



          -----------------------------------------------------------------




            第一個(gè)參數(shù)指明了協(xié)議簇,目前支持5種協(xié)議簇,最常用的有AF_INET(IPv4協(xié)議)和AF_INET6(IPv6協(xié)議);第二個(gè) 參數(shù)指明套接口類型,有三種類型可選:SOCK_STREAM(字節(jié)流套接口)、SOCK_DGRAM(數(shù)據(jù)報(bào)套接口)和SOCK_RAW(原始套接 口);如果套接口類型不是原始套接口,那么第三個(gè)參數(shù)就為0。


           ?。?、connect函數(shù):當(dāng)用socket建立了套接口后,可以調(diào)用connect為這個(gè)套接字指明遠(yuǎn)程端的地址;如果是字節(jié)流套接口,connect就使用三次握手建立一個(gè)連接;如果是數(shù)據(jù)報(bào)套接口,connect僅指明遠(yuǎn)程端地址,而不向它發(fā)送任何數(shù)據(jù)。






          -----------------------------------------------------------------



          #include <sys/socket.h>      



          int connect(int sockfd,const struct sockaddr * servaddr,socklen_t



          addrlen);  



          返回:0---成功   -1---失敗



          -----------------------------------------------------------------




            第一個(gè)參數(shù)是socket函數(shù)返回的套接口描述字;第二和第三個(gè)參數(shù)分別是一個(gè)指向套接口地址結(jié)構(gòu)的指針和該結(jié)構(gòu)的大小。


            這些地址結(jié)構(gòu)的名字均已“sockaddr_”開頭,并以對應(yīng)每個(gè)協(xié)議族的唯一后綴結(jié)束。以IPv4套接口地址結(jié)構(gòu)為例,它以“sockaddr_in”命名,定義在頭文件<netinet/in.h>;以下是結(jié)構(gòu)體的內(nèi)容:






          ------------------------------------------------------------------



          struct in_addr {



          in_addr_t s_addr;      /* IPv4地址 */



          };



          struct sockaddr_in {



          uint8_t sin_len; /* 無符號(hào)的8位整數(shù) */



          sa_family_t sin_family;



          /* 套接口地址結(jié)構(gòu)的地址簇,這里為AF_INET */



          in_port_t sin_port; /* TCP或UDP端口 */



          struct in_addr sin_addr;



          char sin_zero[8];



          };   



          -------------------------------------------------------------------




            3、bind函數(shù):為套接口分配一個(gè)本地IP和協(xié)議端口,對于網(wǎng)際協(xié)議,協(xié)議地址是32位IPv4地址或128位IPv6地址與 16位的TCP或UDP端口號(hào)的組合;如指定端口為0,調(diào)用bind時(shí)內(nèi)核將選擇一個(gè)臨時(shí)端口,如果指定一個(gè)通配IP地址,則要等到建立連接后內(nèi)核才選擇 一個(gè)本地IP地址。






          -------------------------------------------------------------------



          #include <sys/socket.h>  



          int bind(int sockfd,const struct sockaddr * myaddr,socklen_t



          addrlen);



          返回:0---成功   -1---失敗 



          -------------------------------------------------------------------




            第一個(gè)參數(shù)是socket函數(shù)返回的套接口描述字;第二和第第三個(gè)參數(shù)分別是一個(gè)指向特定于協(xié)議的地址結(jié)構(gòu)的指針和該地址結(jié)構(gòu)的長度。


           ?。?、listen函數(shù):listen函數(shù)僅被TCP服務(wù)器調(diào)用,它的作用是將用sock創(chuàng)建的主動(dòng)套接口轉(zhuǎn)換成被動(dòng)套接口,并等待來自客戶端的連接請求。






          -------------------------------------------------------------------



          #include <sys/socket.h>



          int listen(int sockfd,int backlog);   



          返回:0---成功   -1---失敗



          -------------------------------------------------------------------




            第一個(gè)參數(shù)是socket函數(shù)返回的套接口描述字;第二個(gè)參數(shù)規(guī)定了內(nèi)核為此套接口排隊(duì)的最大連接個(gè)數(shù)。由于listen函數(shù)第二個(gè)參 數(shù)的原因,內(nèi)核要維護(hù)兩個(gè)隊(duì)列:以完成連接隊(duì)列和未完成連接隊(duì)列。未完成隊(duì)列中存放的是TCP連接的三路握手為完成的連接,accept函數(shù)是從以連接隊(duì) 列中取連接返回給進(jìn)程;當(dāng)以連接隊(duì)列為空時(shí),進(jìn)程將進(jìn)入睡眠狀態(tài)。


           ?。?、accept函數(shù):accept函數(shù)由TCP服務(wù)器調(diào)用,從已完成連接隊(duì)列頭返回一個(gè)已完成連接,如果完成連接隊(duì)列為空,則進(jìn)程進(jìn)入睡眠狀態(tài)。






          -------------------------------------------------------------------



          #include <sys/socket.h>         



          int accept(int sockfd,struct sockaddr *



          cliaddr,socklen_t * addrlen);  



          回:非負(fù)描述字---成功   -1---失敗



          -------------------------------------------------------------------




            第一個(gè)參數(shù)是socket函數(shù)返回的套接口描述字;第二個(gè)和第三個(gè)參數(shù)分別是一個(gè)指向連接方的套接口地址結(jié)構(gòu)和該地址結(jié)構(gòu)的長度;該函數(shù)返回的是一個(gè)全新的套接口描述字;如果對客戶段的信息不感興趣,可以將第二和第三個(gè)參數(shù)置為空。


            6、inet_pton函數(shù):將點(diǎn)分十進(jìn)制串轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序二進(jìn)制值,此函數(shù)對IPv4地址和IPv6地址都能處理。






          -------------------------------------------------------------------



          #include <arpa/inet.h>



          int inet_pton(int family,const char * strptr,void * addrptr);



          返回:1---成功 0---輸入不是有效的表達(dá)格式 -1---失敗



          -------------------------------------------------------------------




            第一個(gè)參數(shù)可以是AF_INET或AF_INET6:第二個(gè)參數(shù)是一個(gè)指向點(diǎn)分十進(jìn)制串的指針:第三個(gè)參數(shù)是一個(gè)指向轉(zhuǎn)換后的網(wǎng)絡(luò)字節(jié)序的二進(jìn)制值的指針。


           ?。贰net_ntop函數(shù):和inet_pton函數(shù)正好相反,inet_ntop函數(shù)是將網(wǎng)絡(luò)字節(jié)序二進(jìn)制值轉(zhuǎn)換成點(diǎn)分十進(jìn)制串。






          -------------------------------------------------------------------



          #include <arpa/inet.h>  



          const char * inet_ntop(int family,const void *



          addrptr,char * strptr,size_t len);  



          返回:指向結(jié)果的指針---成功   NULL---失敗



          -------------------------------------------------------------------




            第一個(gè)參數(shù)可以是AF_INET或AF_INET6:第二個(gè)參數(shù)是一個(gè)指向網(wǎng)絡(luò)字節(jié)序的二進(jìn)制值的指針;第三個(gè)參數(shù)是一個(gè)指向轉(zhuǎn)換后的點(diǎn)分十進(jìn)制串的指針;第四個(gè)參數(shù)是目標(biāo)的大小,以免函數(shù)溢出其調(diào)用者的緩沖區(qū)。


           ?。?、fock函數(shù):在網(wǎng)絡(luò)服務(wù)器中,一個(gè)服務(wù)端口可以允許一定數(shù)量的客戶端同時(shí)連接,這時(shí)單進(jìn)程是不可能實(shí)現(xiàn)的,而fock就分配一個(gè)子進(jìn)程和客戶端會(huì)話,當(dāng)然,這只是fock的一個(gè)典型應(yīng)用。






          -------------------------------------------------------------------



          #include <unistd.h>  



          pid_t fock(void);



          返回:在子進(jìn)程中為0,在父進(jìn)程中為子進(jìn)程ID   -1---失敗



          -------------------------------------------------------------------




            fock函數(shù)調(diào)用后返回兩次,父進(jìn)程返回子進(jìn)程ID,子進(jìn)程返回0。


            有了上面的基礎(chǔ)知識(shí),我們就可以進(jìn)一步了解TCP套接口和UDP套接口


           ?。薄CP套接口


            TCP套接口使用TCP建立連接,建立一個(gè)TCP連接需要三次握手,基本過程是服務(wù)器先建立一個(gè)套接口并等待客戶端的連接請求;當(dāng)客戶 端調(diào)用connect進(jìn)行主動(dòng)連接請求時(shí),客戶端TCP發(fā)送一個(gè)SYN,告訴服務(wù)器客戶端將在連接中發(fā)送的數(shù)據(jù)的初始序列號(hào);當(dāng)服務(wù)器收到這個(gè)SYN后也 給客戶端發(fā)一個(gè)SYN,里面包含了服務(wù)器將在同一連接中發(fā)送的數(shù)據(jù)的初始序列號(hào);最后客戶在確認(rèn)服務(wù)器發(fā)的SYN。到此為止,一個(gè)TCP連接被建立。


            下面就用一個(gè)例子來說明服務(wù)器和客戶是怎么連接的






          -------------------------------------------------------------------



          /* client.c */



          #include <stdio.h>



          #include <stdlib.h>



          #include <errno.h>



          #include <string.h>



          #include <netdb.h>



          #include <sys/types.h>



          #include <netinet/in.h>



          #include <sys/socket.h>



          int main(int argc,char *argv[]) {



          int sockfd,numbytes;



          char buf[100];



          struct hostent *he;



          struct sockaddr_in their_addr;



          int i = 0;



          //將基本名字和地址轉(zhuǎn)換



          he = gethostbyname(argv[1]);



          //建立一個(gè)TCP套接口



          if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1) {



          perror("socket");



          exit(1);



          }



          //初始化結(jié)構(gòu)體,連接到服務(wù)器的2323端口



          their_addr.sin_family = AF_INET;



          their_addr.sin_port = htons(2323);



          their_addr.sin_addr = *((struct in_addr *)he->h_addr);



          bzero(&(their_addr.sin_zero),8);



          //和服務(wù)器建立連接



          if(connect(sockfd,(struct sockaddr *)&their_addr,



          sizeof(struct sockaddr))



          ==-1){



          perror("connect");



          exit(1);



          }



          //向服務(wù)器發(fā)送字符串"hello!"



          if(send(sockfd,"hello!",6,0)==-1) {



          perror("send");



          exit(1);



          }



          //接受從服務(wù)器返回的信息



          if((numbytes = recv(sockfd,buf,100,0))==-1) {



          perror("recv");



          exit(1);



          }



          buf[numbytes] = '';



          printf("result:%s",buf);



          close(sockfd);



          return 0;



          }



          --------------------------------------------------------------------



          /* server.c */



          #include <stdio.h>



          #include <stdlib.h>



          #include <errno.h>



          #include <string.h>



          #include <sys/types.h>



          #include <netinet/in.h>



          #include <sys/socket.h>



          #include <sys/wait.h>



          main() {



          int sockfd,new_fd;



          struct sockaddr_in my_addr;



          struct sockaddr_in their_addr;



          int sin_size;



          //建立TCP套接口



          if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1) {



          perror("socket");



          exit(1);



          }



          //初始化結(jié)構(gòu)體,并綁定2323端口



          my_addr.sin_family = AF_INET;



          my_addr.sin_port = htons(2323);



          my_addr.sin_addr.s_addr = INADDR_ANY;



          bzero(&(my_addr.sin_zero),8);



          //綁定套接口



          if(bind(sockfd,(struct sockaddr *)&my_addr,sizeof(struct



          sockaddr))==-1)



          {



          perror("bind");



          exit(1);



          }



          //創(chuàng)建監(jiān)聽套接口



          if(listen(sockfd,10)==-1) {



          perror("listen");



          exit(1);



          }



          //等待連接



          while(1) {



          sin_size = sizeof(struct sockaddr_in);



          perror("server is run");



          //如果建立連接,將產(chǎn)生一個(gè)全新的套接字



          if((new_fd = accept(sockfd,(struct sockaddr *)



          &their_addr,&sin_size))==-1)



          {



          perror("accept");



          exit(1);



          }



          //生成一個(gè)子進(jìn)程來完成和客戶端的會(huì)話,父進(jìn)程繼續(xù)監(jiān)聽



          if(!fork()) {



          //讀取客戶端發(fā)來的信息



          if((numbytes = recv(new_fd,buff,strlen(buff),0))==-1)



          {



          perror("recv");



          exit(1);



          }



          printf("%s",buff);



          //將從客戶端接收到的信息再發(fā)回客戶端



          if(send(new_fd,buff,strlen(buff),0)==-1)



          perror("send");



          close(new_fd);



          exit(0);



          }



          close(new_fd);



          }



          close(sockfd);



          }



          ------------------------------------------------------------------




             現(xiàn)在讓我們來編譯這兩個(gè)程序:






          root@linuxaid#gcc -o server server.c



          root@linuxaid#gcc -o client client.c




          然后在一臺(tái)計(jì)算機(jī)上先運(yùn)行服務(wù)器程序,再在另一個(gè)終端上運(yùn)行客戶端就會(huì)看到結(jié)果;如果不運(yùn)行服務(wù)器程序而先運(yùn)行客戶程序?qū)⒘⒓刺崾?Connect: Connection refused",這就是TCP套接口的好處,如果是UDP套接口將會(huì)有一個(gè)延時(shí)才會(huì)得到錯(cuò)誤信息(UDP套接口后面有介紹)。


            建立一個(gè)TCP連接需要三次握手,而斷開一個(gè)TCP則需要四個(gè)分節(jié)。當(dāng)某個(gè)應(yīng)用進(jìn)程調(diào)用close(主動(dòng)端)后(可以是服務(wù)器端,也可 以是客戶端),這一端的TCP發(fā)送一個(gè)FIN,表示數(shù)據(jù)發(fā)送完畢;另一端(被動(dòng)端)發(fā)送一個(gè)確認(rèn),當(dāng)被動(dòng)端待處理的應(yīng)用進(jìn)程都處理完畢后,發(fā)送一個(gè)FIN 到主動(dòng)端,并關(guān)閉套接口,主動(dòng)端接收到這個(gè)FIN后再發(fā)送一個(gè)確認(rèn),到此為止這個(gè)TCP連接被斷開。


           ?。?、UDP套接口


            UDP套接口是無連接的、不可靠的數(shù)據(jù)報(bào)協(xié)議;既然他不可靠為什么還要用呢?其一:當(dāng)應(yīng)用程序使用廣播或多播是只能使用UDP協(xié)議;其 二:由于他是無連接的,所以速度快。因?yàn)閁DP套接口是無連接的,如果一方的數(shù)據(jù)報(bào)丟失,那另一方將無限等待,解決辦法是設(shè)置一個(gè)超時(shí)。


            在編寫UDP套接口程序時(shí),有幾點(diǎn)要注意:建立套接口時(shí)socket函數(shù)的第二個(gè)參數(shù)應(yīng)該是SOCK_DGRAM,說明是建立一個(gè) UDP套接口;由于UDP是無連接的,所以服務(wù)器端并不需要listen或accept函數(shù);當(dāng)UDP套接口調(diào)用connect函數(shù)時(shí),內(nèi)核只記錄連接放 的IP地址和端口,并立即返回給調(diào)用進(jìn)程,正因?yàn)檫@個(gè)特性,UDP服務(wù)器程序中并不使用fock函數(shù),用單進(jìn)程就能完成所有客戶的請求。



          IO::Socket簡介

          另一個(gè)構(gòu)造socket庫,使用對象構(gòu)造模式。如果您看過wawa老大的動(dòng)網(wǎng)EXPLOIT與isno大哥的WEBDAVX,您就會(huì)發(fā)現(xiàn)這些EXPLOITS都是使用這個(gè)庫做的,所以如果您想寫EXPLOITS的話,不妨看看此文。

          以前我寫的是傳統(tǒng)的C語言‘遺留’下了的SOCKET庫它使用了部分C庫的二進(jìn)制格式,導(dǎo)致PERL無法完全使用它。而這篇文所介紹的IO::Socket庫是IO::Handle的子類,完全對象編程,一切就會(huì)‘自由’很多了......

          使用格式與常用方法(父類IO::Handle與IO::File的通用方法就不在下文中說明了):
          =============================================================================
          -----------------------------------------------------------------------------
          導(dǎo)入IO::Socket包:
          use IO::Socket;

          講解:
          IO::Socket下又有兩個(gè)子類IO::Socket::INET與IO::Socket::UNIX,我們現(xiàn)在用的當(dāng)然是IO::Socket::INET了。
          -----------------------------------------------------------------------------

          new()方法:
          SOCKET對象變量=IO::Socket::INET->new(SOCKET變量值);

          實(shí)例:
          $sock=IO::Socket::INET->new('192.168.1.2:23');

          講解:
          所有的PERL對象編程都把對象‘形象化’為某個(gè)變量,這里的SOCKET句柄對象也不例外,調(diào)用此方法的返回值便為SOCKET對象變量 了。這里使用參數(shù)為簡單參數(shù)模式,在雙引號(hào)或但引號(hào)內(nèi)的socket地址結(jié)構(gòu)為'主機(jī)IP或域名:端口號(hào)或服務(wù)名稱',也可以是'主機(jī)IP或域名:服務(wù)名 稱(端口號(hào))'。

          除了最簡單的單參數(shù)調(diào)用外,new方法還有很多參數(shù)可以選擇性調(diào)用的,下面就對這些參數(shù)作出一個(gè)簡單的概括吧:
          ***********************************************************************
          參數(shù) 描述 值類型
          ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
          PeerAddr 遠(yuǎn)程主機(jī)的地址 主機(jī)地址[:端口或服務(wù)]
          PeerHost 與PeerAddr相同
          PeerPort 遠(yuǎn)程端口或服務(wù) 端口或服務(wù)
          LocalAddr 本地地址 主機(jī)地址[:端口或服務(wù)]
          LocalHost 與LocalAddr相同
          LocalPort 本地端口 端口或服務(wù)
          Proto 所使用的協(xié)議 協(xié)議名或協(xié)議號(hào)
          Type 套接字類型 SOCK_STREAM/SOCK_DGRAM...
          Listen 監(jiān)聽的隊(duì)列長度 整形數(shù)
          Reuse 用于避免重啟時(shí)BIND時(shí)間間隙 布爾值
          Timeout 超時(shí)值 整形數(shù)
          MultiHomed 用于連接多IP地址 布爾值
          ***********************************************************************

          參數(shù)PeerAddr(遠(yuǎn)程主機(jī)地址)與PeerHost(遠(yuǎn)程主機(jī)名)基本相同,調(diào)用方式也相同,其值格式除了標(biāo)準(zhǔn)的格式外,還可以加':'號(hào)后再加端口或服務(wù),這樣的的話,后面的參數(shù)PeerPort(遠(yuǎn)程主機(jī)端口或服務(wù))的值就無效了。

          參數(shù)PeerPort(遠(yuǎn)程主機(jī)端口或服務(wù)),其值的格式可以是端口,還可以是服務(wù)名,更可以是‘組合’,如:"telnet(23)";當(dāng)PeerAddr(遠(yuǎn)程主機(jī)地址)或
          PeerHost(遠(yuǎn)程主機(jī)名)的值格式中指明了端口,再調(diào)用此參數(shù)時(shí),此參數(shù)的值無效。

          參數(shù)LocalAddr(本地主機(jī)地址)、LocalHost(本地主機(jī)名)、LocalPort(本地主機(jī)端口或服務(wù))之間的關(guān)系與調(diào)用方 式與上面介紹的三個(gè)參數(shù)PeerAddr(遠(yuǎn)程主機(jī)地址)、PeerHost(遠(yuǎn)程主機(jī)名)、PeerPort(遠(yuǎn)程主機(jī)端口或服務(wù))相當(dāng)。

          還有一種情況,就是如果只定義了LocalPort(本地主機(jī)端口或服務(wù)),而沒有定義LocalAddr(本地主機(jī)地址)或 LocalHost(本地主機(jī)名),那IO::Socket會(huì)將本地機(jī)器的地址的值默認(rèn)為INADDR_ANY通配符,也就是不定義本地主機(jī)的地址值的話 就定義為允許所有接口。

          Proto(協(xié)議類型)的值可以用兩種方式表示。一種是直接的字符串表示方式,如:

          proto=>"tcp"

          表示該協(xié)議類型為TCP。第二種方式就是直接使用協(xié)議號(hào)了,EGP---8、HMP---20、ICMP---1、RAW---255、RDP- --27、RVD---66、TCP---6、UDP---17、XNS-IDP---22、其他---22、ALL---0;也可以使用 getprotobyname函數(shù)加協(xié)議名為參數(shù)調(diào)用獲的該值,如:

          proto=>getprotobyname('tcp')

          該形式也表示該協(xié)議的類型為TCP。建議還是使用第一種方式比較方便。

          Type(套接字類型)的值通常為SOCK_STREAM(流套接字)、SOCK_DGRAM(數(shù)據(jù)報(bào)套接字)、SOCK_RAW(原始套接字)等,不用說大家都知道,TCP用的是流套接字,UDP用的是數(shù)據(jù)報(bào)套接字,構(gòu)造IP包用的是原始套接字。

          如果上面的參數(shù)Proto(協(xié)議類型)與Type(套接字類型)的值都不定義的話,IO::Socket::INET就會(huì)通過程序中上下‘文’部分猜估它們的值,猜估不到的話就會(huì)默認(rèn)為'tcp'。

          參數(shù)Listen(監(jiān)聽隊(duì)列的長度)的值是一個(gè)整形數(shù)。它代表能接受的連接主機(jī)數(shù)量。如果您要構(gòu)造服務(wù)端的話,Listen這個(gè)步驟是必不可少的。

          調(diào)用Reuse(在綁定前設(shè)置SO_REUSEADDR)可以免去服務(wù)器在終止到重啟之間的所停留的時(shí)間。

          Timeout(超時(shí)值)以秒計(jì)算,用于連接中的connect與accept這兩個(gè)步驟,調(diào)用目的是為了在連接遠(yuǎn)程主機(jī)不可到達(dá)時(shí)限制連接的掛起時(shí)間。

          MultiHomed(用于連接多IP地址)的值是一個(gè)布爾值,當(dāng)其值為真時(shí),如果要連接的主機(jī)擁有多個(gè)IP地址,則本機(jī)的new方法調(diào)用gethostbyname()窮舉其所有IP地址,直到能成功調(diào)用為止。

          從樓上的列表中可以看到IO::Socket與傳統(tǒng)C庫的Socket API接口在調(diào)用上有什么不同了:

          1)控制范圍不同。C庫提供的接口在生成SOCKET句柄時(shí)只能控制的只有域、套接字類型、協(xié)議這幾個(gè)參數(shù)。而IO::Socket接口的創(chuàng)建語句(調(diào)用new方法)幾乎能決定這個(gè)套接字的所有參數(shù)。

          2)調(diào)用所使用的‘協(xié)議’定義部分不同。IO::Socket接口調(diào)用new方法中的參數(shù)'Proto'的值可以直接定義為'tcp',這比傳統(tǒng)C庫的Socket定義更為簡便。

          3)IO::Socket在定義時(shí)能直接定義本地主機(jī)地址、本地端口與遠(yuǎn)程主機(jī)地址、遠(yuǎn)程端口在一個(gè)Socket中,如果是這種情況的服務(wù)端 就無需調(diào)用accept了,在I/O讀寫部分可以直接向這個(gè)Socket進(jìn)行讀寫操作,而無需再定義遠(yuǎn)程客戶端的Socket了。
          -----------------------------------------------------------------------------

          accept()方法:
          遠(yuǎn)程連接套接字對象變量=服務(wù)端套接字對象變量->accept();

          實(shí)例:
          $remote_sock=$sock->accept();

          講解:
          此方法的調(diào)用環(huán)境與傳統(tǒng)C中SOCKET庫調(diào)用原理一樣,用于服務(wù)端的等待監(jiān)聽過程。無參數(shù),返回值為遠(yuǎn)程連接的套接字對象變量。調(diào)用此方法 也是一個(gè)生成套接字的過程,只不過此套接字為遠(yuǎn)程連接的套接字而已,它以對象變量方式存在,據(jù)有與本地套接字變量相同的屬性與方法。

          accept()方法在IO::Socket包里還提供另一種雙返回值的調(diào)用方法:

          (遠(yuǎn)程連接套接字對象變量,遠(yuǎn)程主機(jī)壓縮地址變量)=服務(wù)端對象變量->accept();

          實(shí)例:
          ($remote_sock,$remote_addr)=$sock->accept();

          講解:
          與樓上一個(gè)返回值的調(diào)用方式基本相同,只是返回值中多了一個(gè)變量而已,返回值中多了個(gè)變量------遠(yuǎn)程主機(jī)壓縮地址變量。
          -----------------------------------------------------------------------------

          bind()方法:
          返回值變量=服務(wù)端套接字對象變量->bind(本地端口號(hào),本地主機(jī)網(wǎng)絡(luò)地址);

          實(shí)例:
          $result=$sock->bind(80,'127.0.0.1');

          講解:
          bind方法用于在服務(wù)器端綁定主機(jī)的地址與端口。它使用的兩個(gè)參數(shù)都為未壓縮值,第一個(gè)為端口,第二個(gè)為主機(jī)的網(wǎng)絡(luò)適配器接口地址(可以使 用默認(rèn)的保留字INADDR_ANY,此保留字包括了主機(jī)的所有網(wǎng)絡(luò)適配器接口地址,調(diào)用它時(shí),它會(huì)以窮舉的方法窮舉所有的網(wǎng)絡(luò)適配器接口地址,直到找到 為止);返回值為布爾值,用于檢測這次調(diào)用是否成功。
          -----------------------------------------------------------------------------

          connect()方法:
          返回值變量=套接字對象變量->connect(壓縮地址變量);

          實(shí)例:
          $result=$sock->connect($pack_addr);

          講解:
          常用于TCP連接(也可用于UDP,不過不常用),調(diào)用將向遠(yuǎn)程主機(jī)發(fā)送連接請求。參數(shù)‘壓縮地址變量’為sockaddr_in形式值,返 回值為布爾值。若調(diào)用此方法則建立IO::Socket::INET對象時(shí)不能賦予參數(shù)'PeerAddr'或'PeerHost'、 'PeerPort',否則就會(huì)出現(xiàn)程序邏輯錯(cuò)誤。

          connect()方法也有雙參數(shù)調(diào)用方式,使用起來更簡單:
          返回值變量=套接字對象變量->connect(遠(yuǎn)程端口號(hào),遠(yuǎn)程主機(jī)地址);

          實(shí)例:
          $result=$sock->connect($remote_port,$remote_host);

          講解:
          調(diào)用的目的與樓上單參數(shù)的調(diào)用方式相當(dāng)。第一個(gè)參數(shù)為遠(yuǎn)程需要連接的主機(jī)的端口(等于new方法的參數(shù)'PeerPort'),第二個(gè)參數(shù)為需要連接的主機(jī)地址(等于new方法的參數(shù)'PeerAddr'或'PeerHost'),返回值為布爾值。
          -----------------------------------------------------------------------------

          listen()方法:
          返回值變量=套接字對象變量->listen(請求隊(duì)列的最大長度值);

          實(shí)例:
          $result=$sock->listen(20);

          講解:
          TCP服務(wù)端不可缺少的方法。單參數(shù),參數(shù)為此服務(wù)端接受遠(yuǎn)端請求隊(duì)列的最大長度值,返回值為布爾值。調(diào)用此方法等同于在建立IO:: Socket::INET對象時(shí)定義參數(shù)'Listen'的值,所以若在new方法中定義了參數(shù)'Listen'再調(diào)用此方法的話就會(huì)出現(xiàn)‘程序定義沖突 ’這樣的邏輯錯(cuò)誤了。
          -----------------------------------------------------------------------------

          shutdown()方法:
          返回值變量=套接字對象變量->shutdown(控制參數(shù));

          實(shí)例:
          $result=$sock->shutdown(2);

          講解:
          此方法是除了close外的另一個(gè)關(guān)閉套接字對象的方法。單參數(shù),參數(shù)值為外加參數(shù)定義,下為此方法的外加參數(shù)列表:
          ***********************************************************************
          參數(shù)值 描述
          ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
          0 關(guān)閉對象套接字的讀操作
          1 關(guān)閉對象套接字的寫操作
          2 關(guān)閉對象套接字的所有操作
          ***********************************************************************
          其返回值為布爾值。
          -----------------------------------------------------------------------------

          send()方法:
          成功發(fā)送的數(shù)據(jù)值變量=套接字對象變量->send(發(fā)送數(shù)據(jù),標(biāo)志值,目標(biāo)地址值);

          實(shí)例:
          $succ_bytes=$sock->send('hihi\n',0,$pack_host);

          講解:
          send方法是專門為SOCKET發(fā)送數(shù)據(jù)的特殊方法,調(diào)用格式與參數(shù)格式也基本與C庫的SOCKET API中的send函數(shù)相同。第一個(gè)參數(shù)是需要發(fā)送的數(shù)據(jù);第二參數(shù)是標(biāo)志值,不添的話默認(rèn)為0;第三個(gè)參數(shù)通常只用于UDP連接,是需要連接的 sockaddr_in格式地址值(注意:當(dāng)?shù)谌齻€(gè)參數(shù)有必要一定要寫時(shí),第二個(gè)參數(shù)也一定要加上);返回值為成功發(fā)送的數(shù)據(jù)值大小(以byte為單 位)。
          -----------------------------------------------------------------------------

          recv()方法:
          壓縮遠(yuǎn)程地址地址=套接字對象變量->recv(接收數(shù)據(jù)變量,接收數(shù)據(jù)值長度,標(biāo)志值);

          實(shí)例:
          $remote_pack_address=$sock->recv($mem,100,0);

          講解:
          recv方法是專門為SOCKET接收數(shù)據(jù)的特殊方法,調(diào)用格式與參數(shù)格式也與C庫的SOCKET API基本一樣。第一個(gè)參數(shù)是存放接收后的數(shù)據(jù)的變量值;第二個(gè)參數(shù)是接收的數(shù)據(jù)的長度值;第三個(gè)參數(shù)是標(biāo)志值,默認(rèn)為0就可以了(省略此值不填,系統(tǒng)默認(rèn)也為0)。
          -----------------------------------------------------------------------------
          ===================================================================================

          IO::Socket接口的常用方法就介紹完了,不過還有一個(gè)問題是需要注意的: 作為一個(gè)簡單的客戶端,它的步驟只需要先調(diào)用new方法,然后立刻就可以進(jìn)行基本I/O操作(使用print與getline等基本I/O方法)了,最后 只需調(diào)用close方法結(jié)束會(huì)話,那么整個(gè)SOCKET會(huì)話就算完成了。

          典型使用例子:
          wawa's dvbbs exploit: http://haowawa.8866.org/wawa/new/tech/dvbbs.pl
          isno's webdavx exploit: http://www.xfocus.net/tools/200304/webdavx3.pl

          究竟C庫的傳統(tǒng)SOCKET接口與本文介紹的IO::Socket接口哪個(gè)比較好用呢???我只能回答你:"蘿卜青菜,各有所愛"......:P



          posted on 2007-10-18 11:18 MEYE 閱讀(833) 評論(0)  編輯  收藏

          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 岐山县| 年辖:市辖区| 连南| 永泰县| 宜黄县| 宁明县| 施甸县| 保定市| 全南县| 汕尾市| 略阳县| 澎湖县| 巢湖市| 康马县| 德江县| 巨野县| 南昌县| 依安县| 玛纳斯县| 泸水县| 周口市| 巴东县| 嘉善县| 黎城县| 巴南区| 冀州市| 汉中市| 东阿县| 杭锦旗| 文山县| 页游| 浠水县| 灵川县| 乌海市| 富阳市| 什邡市| 台北市| 兴业县| 南宁市| 玉溪市| 天峻县|