• <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>

            牽著老婆滿街逛

            嚴(yán)以律己,寬以待人. 三思而后行.
            GMail/GTalk: yanglinbo#google.com;
            MSN/Email: tx7do#yahoo.com.cn;
            QQ: 3 0 3 3 9 6 9 2 0 .

            如何編寫Linux下的客戶機/服務(wù)器軟件

               Linux以其源代碼公開聞名于世,并以其穩(wěn)定性和可靠性雄霸操作系統(tǒng)領(lǐng)域,在網(wǎng)絡(luò)應(yīng)用技術(shù)方面使用得更加廣泛。很久以來它就是Windows的重要對手之一。隨著網(wǎng)絡(luò)時代的來臨,Linux的這種優(yōu)勢已變得更加突出。本文將論述如何在Linux環(huán)境下利用Socket實現(xiàn)客戶機/服務(wù)器通信。
            隨著網(wǎng)絡(luò)技術(shù)的發(fā)展,網(wǎng)絡(luò)結(jié)構(gòu)已從過去的主機/終端型、對等型發(fā)展到現(xiàn)在廣為使用的客戶機/服務(wù)器型。客戶機/服務(wù)器模型應(yīng)用十分廣泛,在Internet上WWW,E-mail,F(xiàn)TP等都是基于這種模型的。在面向連接的通信模式下,服務(wù)器打開監(jiān)聽端口,監(jiān)聽網(wǎng)絡(luò)上其它客戶機向該服務(wù)器發(fā)出的連接請求,當(dāng)收到一個請求信號時與該客戶機建立一個連接,之后兩者進行交互式的通信。具體步驟可這樣組織:

            服務(wù)器:
            1.打開一個已知的監(jiān)聽端口,如smtp為25、pop3為110、ftp為21、telnet為23等。
            2.在監(jiān)聽端口上監(jiān)聽客戶機的連接請求,如果有客戶機請求連接則建立一個連接線路。
            3.在連接線路上與客戶機通信。
            4.通信完畢后關(guān)閉連接線路并繼續(xù)監(jiān)聽客戶機的連接請求。

            客戶機:
            1.向指定的服務(wù)器主機及端口發(fā)出連接請求。
            2.當(dāng)服務(wù)器建立連接線路后與服務(wù)器進行通信。
            3.通信完畢后關(guān)閉連接線路。

            Linux的許多特性都非常有助于網(wǎng)絡(luò)程序設(shè)計:首先Linux擁有POSIX.1標(biāo)準(zhǔn)庫函數(shù),socket()、bind()、listen()這幾個庫函數(shù)可以非常方便地實現(xiàn)服務(wù)器/客戶機模型,有關(guān)這幾個庫函數(shù)的使用說明將在后邊介紹。其次Linux的進程管理也非常符合服務(wù)器的工作原理,所謂進程就是程序在內(nèi)存中運行時的狀態(tài),可以說進程是動態(tài)的程序。在運行著Linux操作系統(tǒng)的計算機中,每一個進程都有一個創(chuàng)建它的父進程,而且它也能創(chuàng)建多個子進程。在服務(wù)器端我們可以用父進程去監(jiān)聽客戶機的連接請求,當(dāng)有客戶機的連接請求時父進程創(chuàng)建一個子進程去建立連接線路并與客戶機通信,而它本身可繼續(xù)監(jiān)聽其它客戶機的連接請求,這樣就可避免當(dāng)有一個客戶機與服務(wù)器建立連接后服務(wù)器就不能再與其它客戶機通信的問題。Linux的另一個特性是它秉承了UNIX設(shè)備無關(guān)性這一優(yōu)秀特征,即它通過文件描述符實現(xiàn)了統(tǒng)一的設(shè)備接口,磁盤、顯示終端、音頻設(shè)備、打印設(shè)備甚至網(wǎng)絡(luò)通信都使用統(tǒng)一的I/O調(diào)用。這三個特性將使Linux下的網(wǎng)絡(luò)程序設(shè)計變得易如反掌。上述三個特性的綜合利用將是這篇文章所要講述的真諦所在。下邊的客戶機/服務(wù)器實現(xiàn)過程可以說明一二,注意與上文所述步驟的不同。

            服務(wù)器:
            1.打開一個已知的監(jiān)聽端口。
            2.在監(jiān)聽端口上監(jiān)聽客戶機的連接請求,當(dāng)有一客戶機請求連接時建立連接線路并返回通信文件描述符。
            4.父進程創(chuàng)建一子進程,父進程關(guān)閉通信文件描述符并繼續(xù)監(jiān)聽端口上的客戶機連接請求。
            3.子進程通過通信文件描述符與客戶機進行通信,通信結(jié)束后終止子進程并關(guān)閉通信文件描述符。

            客戶機:
            1.向指定的服務(wù)器主機及端口發(fā)出連接請求,請求成功將返回通信文件描述符。
            2.通過通信文件描述符與服務(wù)器進行通信。
            3.通信完畢后關(guān)閉通信文件描述符。


            Linux的以下幾個庫函數(shù)是網(wǎng)絡(luò)程序設(shè)計的核心部分,它們分別是:
            (1)socket
            調(diào)用方式:
            #include
            #include

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

            簡要說明:
            此函數(shù)為通信創(chuàng)建一個端口,正常調(diào)用將返回一個文件描述符,錯誤調(diào)用將返回-1。domain參數(shù)有兩種選擇:AF_UNIX與AF_INET,其中AF_INET為Internet通信協(xié)議。type參數(shù)也有兩種選擇:SOCK_STREAM用于TCP,SOCK_DGRAM用于UDP。protocol參數(shù)通常為0。可通過下列代碼為基于TCP協(xié)議的Internet通信建立套接口傳輸端口:

            #include
            #include
            #include
            int sock;

            if((sock=socket(AF_INET,SOCK_STREAM,0))==-1)
            perror("Could not create socket");

            (2)bind
            調(diào)用方式:
            #include
            #include

            int bind(int s,const struct sockaddr *address,size_t address_len);

            簡要說明:
            bind英文含意是關(guān)聯(lián),捆綁。其目的就是把socket返回的套接口端口與網(wǎng)絡(luò)上的物理位置相關(guān)聯(lián)。
            bind正常調(diào)用返回0,出錯返回-1。此函數(shù)有三個參數(shù):其中s為socket調(diào)用返回的文件描述符,*address設(shè)置了與網(wǎng)絡(luò)上的物理位置相關(guān)的信息,它的類型是struct sockaddr,但在Internet上它是struct sockaddr_in。在socket.h中struct sockaddr_in定義為:
            struct sockaddr_in{
            short sin_family;
            u_short sin_port;
            struct in_addr sin_addr;
            char sin_zero[8];
            };
            sin_family一般為AF_INET,sin_port為端口號,由于使用不同字節(jié)順序的機器必須作轉(zhuǎn)換,故應(yīng)使用宏命令htons(host to network short)來轉(zhuǎn)換端口號,sin_addr將置為INADDR_ANY。這三個值設(shè)置完成后*address參數(shù)才有意義。在編寫代碼時,應(yīng)先設(shè)置*address參數(shù)內(nèi)部各成員變量的值,再調(diào)用bind。

            (3)listen
            調(diào)用方式:
            #include
            #include

            int listen(int s,int backlog);

            簡要說明:
            本函數(shù)使socket端口能夠接受從客戶機來的連接請求,正常調(diào)用返回0,出錯返回-1。
            s參數(shù)為socket產(chǎn)生的文件描述符,backlog為所能接受客戶機的最大數(shù)目。
            socket,bind,listen 三個函數(shù)的綜合調(diào)用最終在服務(wù)器上產(chǎn)生一個能接受客戶機請求的監(jiān)聽文件描述符s。

            (4)accept
            調(diào)用方式:
            #include
            #include

            int accept(int s,struct sockaddr *address,int *address_len);

            簡要說明:
            當(dāng)有客戶機發(fā)出連接請求時,此函數(shù)初始化這個連接。正常調(diào)用返回與客戶機通信的通信文件描述符,出錯返回-1。參數(shù)s為socket調(diào)用返回的文件描述符,address將用來存儲客戶機的信息,此信息由accept填入,當(dāng)與客戶機連接時,客戶機的地址與端口將填到此處。address_len是客戶機地址長度的字節(jié)數(shù),也由accept填入。

            (5)connect
            調(diào)用方式:
            #include
            #include

            int connect(int s,struct sockaddr *address,size_t address_len);

            簡要說明:
            客戶機調(diào)用socket建立傳輸端口后,調(diào)用connect來建立與遠程服務(wù)器相連的連接線路。
            此函數(shù)的參數(shù)調(diào)用同bind。

            (6)inet_addr
            調(diào)用方式:
            #include
            #include
            #include

            in_addr_t inet_addr(const char *addstring);

            簡要說明:
            此函數(shù)將字符串a(chǎn)ddstring表示的網(wǎng)絡(luò)地址(如192.168.0.1)轉(zhuǎn)換成32位的網(wǎng)絡(luò)字節(jié)序二進制值,若成功返回32位二進制的網(wǎng)絡(luò)字節(jié)序地址,若出錯返回 INADDR_NONE。INADDR_NONE是32位均為1的值(即255.255.255.255,它是Internet的有限廣播地址),故如果要轉(zhuǎn)換的addstring是255.255.255.255,函數(shù)調(diào)用將失敗。

            (7)fork
            調(diào)用方式:
            #include
            #include


            pid_t fork(void);

            簡要說明:
            fork的作用是拷貝父進程的內(nèi)存映象來創(chuàng)建子進程,兩個進程將接著fork后的指令繼續(xù)執(zhí)行。 事實上它返回兩個進程控制號,對于父進程它返回子進程的進程ID,對于子進程它返回0。

            可用下邊的代碼調(diào)用fork:

            pid_t childpid;
            if((childpid=fork())=-1){
            perror("The fork failed");
            exit(1);
            }
            else if(child==0){
            調(diào)用子進程;
            }
            else if(child>0){
            調(diào)用父進程;
            }


            以上介紹了網(wǎng)絡(luò)編程的有關(guān)庫函數(shù)的調(diào)用方法,下面舉一個客戶機/服務(wù)器程序的小例子具體說明如何設(shè)計網(wǎng)絡(luò)程序。本例介紹如何查看服務(wù)器上的時間和日期,由于daytime服務(wù)器的通用端口為13,客戶機程序?qū)⑼ㄟ^調(diào)用13號端口對服務(wù)器上的時間和日期進行操作。
            /*timeserve.c*/?
            /*服務(wù)器程序偽代碼如下:?

            打開daytime監(jiān)聽端口;?
            while(客戶機與服務(wù)器成功連接——成功返回通信文件描述符)?
            {?
            fork()?
            子進程:?
            {?
            讀出當(dāng)前時間;?
            將當(dāng)前時間寫入通信文件描述符;?
            關(guān)閉通信文件描述符;?
            }?
            父進程:?
            關(guān)閉通信文件描述符;?
            }?
            */
            ?

            #include?
            #include?
            #include?
            #include?
            #include?
            #include?
            #include?
            #include?
            #include?
            #include?
            #include?
            #include?
            #include?
            #include?

            int?main(int?argc,char?*argv[])?
            {?
            int?listenfd,communfd;?
            struct?sockaddr_in?servaddr;?
            pid_t?childpid;?
            time_t?tick;?
            char?buf[1024];?

            if((listenfd=socket(AF_INET,SOCK_STREAM,0))==-1)?
            {?
            perror(
            "Could?not?create?socket");?
            exit(
            1);?
            }
            ?

            servaddr.sin_family
            =AF_INET;?
            servaddr.sin_addr.s_addr
            =INADDR_ANY;?
            servaddr.sin_port
            =htons(13);?
            if(bind(listenfd,(struct?sockaddr?*)&servaddr,sizeof(servaddr))==-1)?
            {?
            perror(
            "bind?error");?
            exit(
            1);?
            }
            ?
            if(listen(listenfd,254)==-1)?
            {?
            perror(
            "listen?error");?
            exit(
            1);?
            }
            ?
            while(communfd=accept(listenfd,(struct?sockaddr*)NULL,NULL))?
            {?
            if((childpid=fork())==-1)?
            {?
            perror(
            "fork?error");?
            exit(
            1);?
            }
            ?
            else?if(childpid==0)?
            {?
            tick
            =time(NULL);?
            snprintf(buf,
            sizeof(buf),"%.24s\r\n",ctime(&tick));?
            write(communfd,buf,strlen(buf));?
            close(communfd);?
            }
            ?
            else?if(childpid>0)?
            close(communfd);?

            }
            ?
            exit(
            0);?
            }
            ?


            /*timeclient.h*/?
            #include?
            #include?
            #include?
            #include?
            #include?
            #include?
            #include?
            #include?
            #include?

            int?main(int?argc,char?*argv[])?
            {?
            int?communfd,n;?
            struct?sockaddr_in?servaddr;?
            char?recieve[1024],buf[1024];?

            if(argc!=2)?
            {?
            perror(
            "Usage:?client?");?
            exit(
            1);?
            }
            ?
            if((communfd=socket(AF_INET,SOCK_STREAM,0))==-1)?
            {?
            perror(
            "socket?error");?
            exit(
            1);?
            }
            ?
            servaddr.sin_family
            =AF_INET;?
            servaddr.sin_port
            =htons(13);?
            if((servaddr.sin_addr.s_addr=inet_addr(argv[1]))==INADDR_NONE)?
            {?
            perror(
            "inet_addr?error");?
            exit(
            1);?
            }
            ?
            if(connect(communfd,(struct?sockaddr*)&servaddr,sizeof(servaddr))==-1)?
            {?
            perror(
            "connect?error");?
            exit(
            1);?
            }
            ?
            while((n=read(communfd,recieve,1024))>0)?
            {?
            recieve[n]
            =0;?
            if(fputs(recieve,stdout)==EOF)?
            perror(
            "fputs?error");?
            }
            ?
            close(communfd);?
            exit(
            0);?
            }
            ?
            用gcc編譯兩個源程序分別取名為server和client,以根用戶身份運行服務(wù)器程序(設(shè)服務(wù)器網(wǎng)絡(luò)地址為192.168.0.1):
            server &
            然后運行客戶機程序(設(shè)服務(wù)器網(wǎng)絡(luò)地址為192.168.0.1):
            client 192.168.0.1
            在客戶機上就會反映出服務(wù)器上當(dāng)前的時間如(Tue Feb 29 21:46:19 2000)。

            以上程序代碼在redhat 6.0上試驗通過。在程序代碼中有關(guān)庫函數(shù)snprintf、fputs、read、write、close的用法就不在這里說明了,如想了解這些庫函數(shù)的調(diào)用方法可到我的網(wǎng)頁http://lzdx.yeah. net/pro_unix.html去查找。在我的網(wǎng)頁http://lzdx.yeah.net/pro_uici.html中有關(guān)于通用Internet接口(UICI)專用庫的介紹,通用Internet接口(UICI)利用Socket庫函數(shù)提供了一個簡化的獨立于傳輸?shù)慕涌冢鼜恼w上簡化了網(wǎng)絡(luò)程序設(shè)計過程。有興趣的人可到那里去看看。最后祝愿我們每個人都能編寫出自己的網(wǎng)絡(luò)程序。

            posted on 2006-04-20 17:19 楊粼波 閱讀(816) 評論(0)  編輯 收藏 引用 所屬分類: 網(wǎng)絡(luò)編程

            狠狠色婷婷久久一区二区 | 久久久久久精品成人免费图片| 久久人人爽人人爽AV片| 久久午夜夜伦鲁鲁片免费无码影视| 色欲av伊人久久大香线蕉影院 | 久久精品国产亚洲av瑜伽| 久久这里有精品| 久久被窝电影亚洲爽爽爽| 综合久久一区二区三区| 99久久人妻无码精品系列| 伊人久久大香线蕉综合5g| 久久夜色精品国产亚洲| 精品久久人人爽天天玩人人妻| 色综合久久精品中文字幕首页| 99精品久久久久久久婷婷| 久久久久亚洲AV无码专区网站| 97久久精品午夜一区二区| 偷窥少妇久久久久久久久| 久久久久国产日韩精品网站| 99久久国产热无码精品免费| 久久精品国产色蜜蜜麻豆| 亚洲国产精品狼友中文久久久| 日本久久久精品中文字幕| 99久久99这里只有免费的精品| 久久精品一区二区三区AV| 色婷婷综合久久久久中文字幕| 久久综合丝袜日本网| 国产精品久久久久久搜索| 亚洲AV日韩精品久久久久 | 久久国产精品一区| 人人狠狠综合久久亚洲88| 欧美伊香蕉久久综合类网站| 精品久久久久久中文字幕人妻最新 | 精品多毛少妇人妻AV免费久久| 久久一区二区三区免费| 久久国产视屏| 亚洲乱码日产精品a级毛片久久 | 91精品国产综合久久久久久| 久久水蜜桃亚洲av无码精品麻豆| 一本久久知道综合久久| 久久精品卫校国产小美女|