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

            興海北路

            ---男兒仗劍自橫行
            <2013年10月>
            293012345
            6789101112
            13141516171819
            20212223242526
            272829303112
            3456789

            統計

            • 隨筆 - 85
            • 文章 - 0
            • 評論 - 17
            • 引用 - 0

            常用鏈接

            留言簿(6)

            隨筆分類

            隨筆檔案

            收藏夾

            全是知識啊

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            一個多線程web服務器實例(C,Linux,詳細的web服務器原理)

            系統:fedora core 5
            編譯器:g++
            實現功能:通過http協議,用瀏覽器查看服務器上的html,htm,jpg,jpeg,gif,png,css文件 ,或者說查看帶有jpg,jpeg,gif等文件的網頁,即是web~
            把代碼復制下來到linux里,照著后面的方法編譯、運行,就可以看到一個簡單的多線程服務器的效果了。

            原理:
            在瀏覽器中輸入一個網址,回車之后,瀏覽器會向相應主機的相應端口發送一段報文,如果是http協議的(如平??吹降木W頁的傳輸協議),就會發送HTTP請求報文。下面是一個報文的例子:

            GET /index.html HTTP/1.1
            Host: 127.0.0.1:8848
            User-Agent: Mozilla/5.0 (X11; U; Linux i686; zh-CN; rv:1.8.0.1) Gecko/20060313 Fedora/1.5.0.1-9 Firefox/1.5.0.1 pango-text
            Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
            Accept-Language: zh-cn,zh;q=0.5
            Accept-Encoding: gzip,deflate
            Accept-Charset: gb2312,utf-8;q=0.7,*;q=0.7
            Keep-Alive: 300
            Connection: keep-alive

            我們在服務器端把收到的數據打印出來,可以看到瀏覽器發過來的就是這個東西。當然,也可以用ethereal等抓包工具來抓獲這些報文。關于報文里寫的是什么意思,網上有很多資料的,GOOGLE一下就有了。我們只看第一行。

            GET表示是要從服務器獲取文件,/index.html是文件的路徑,這個路徑是相對于服務器端程序所在文件夾的路徑。如我的服務器端程序放在/home/mio/program/webserver1707/里面,那這個index.html在服務器上的絕對路徑就是/home/mio/program/webserver1707/index.html。如果報文里是GET /admin/login.html HTTP/1.1的話,那么login.html文件在服務器端的路徑是/home/mio/program/webserver1707/admin/login.html.HTTP/1.1表示的是HTTP協議的版本是1.1.

            服務器端程序運行后,一直監聽8848端品(0-1023的端口由IANA統一分配和控制的,不要用,最好選大一些的端口號。我原來用了個1234,用不了,還是選大一點好,可以用5460之類的啊~:) ),當監聽到客戶端發來的請求后,就與客戶端建立鏈接,接收客戶端發過來的請求報文。我們如果把這些報文打出來,就可以看到就是與上面請求報文類似的東西了。

            下面我們要根據所接受的到的請求報文(GET /index.html HTTP/1.1)來決定放給客戶端(即瀏覽器)什么東西。這里我們看到瀏覽器要的是index.html這樣一個html文本,我們就在相應路徑(/home/mio/program/webserver1707/index.html)找到這個文件,不過不要急著發給客戶端,我們要先告訴客戶端,發過去的是一個html文件,讓瀏覽器做好相應的準備。怎么讓瀏覽器知道呢?我們還是用報文,這個報文叫響應報文。報文由狀態行、首部行、實體主體三部分組成。狀態行只有一行,它和首部行、首部行的每行之間是沒有空行的,但是首部行與實體主體之間有一個空行,表明從這個空行開始,就是你瀏覽器要的數據了。下面是一個用ethereal抓到的響應報文: 

            HTTP/1.1 200 OK
            Cache-Control: private
            Content-Type: text/html; charset=UTF-8
            Content-Encoding: gzip
            Server: GWS/2.1
            Content-Length: 1851
            Date: Sat, 14 Oct 2006 11:33:39 GMT

            <html><head><meta http-equiv="content-type" content="text/html; charset=UTF-8"><title>Google</title><style><!--
            body,td,a,p,.h{font-family:arial,sans-serif}
            .h{font-size:20px}
            .q{color:#00c}
            --></style>
            <script>
            <!--
            function sf(){document.f.q.focus();}
            function clk(url,oi,cad,ct,cd,sg){if(document.images){var e = window.encodeURIComponent ? encodeURIComponent : escape;var u="";var oi_param="";var cad_param="";if (url) u="&url="+e(url.replace(/#.*/,"")).replace(/\+/g,"%2B");if (oi) oi_param="&oi="+e(oi);if (cad) cad_param="&cad="+e(cad);new Image().src="/url?sa=T"+oi_param+cad_param+"&ct="+e(ct)+"&cd="+e(cd)+u+"&ei=E8swRYIOkpKwAvzZ8JkB"+sg;}return true;}
            // -->
            </script>
            </head><body bgcolor=#ffffff text=#000000 link=#0000cc vlink=#551a8b alink=#ff0000 onLoad=sf() topmargin=3 marginheight=3><center><div align=right nowrap style="padding-bottom:4px" width=100%><font size=-1><b>manioster@gmail.com</b>&nbsp;|&nbsp;<a href="/url?sa=p&pref=ig&pval=3&q=http://www.google.com/ig%3Fhl%3Dzh-CN&sig=__1eXNMn0jGllmJ57x74DzjVvy6Vk=" onmousedown="return clk('/url?sa=p&pref=ig&pval=3&q=http://www.google.com/ig%3Fhl%3Dzh-CN&sig=__1eXNMn0jGllmJ57x74DzjVvy6Vk=','promos','hppphou:zh-cn_all','pro','1','&sig2=zclmOmtQiZPPuTCMWUJMZA')">個性化主頁</a>&nbsp;|&nbsp;<a href="https://www.google.com/accounts/ManageAccount">我的帳戶</a>&nbsp;|&nbsp;<a href="http://www.google.com/accounts/Logout?continue=http://www.google.com/intl/zh-CN/">退出</a></font></div><img src="/intl/zh-CN_ALL/images/logo.gif" width=286 height=110 alt="Google"><br><br>
            <form action=/search name=f><script><!--
            function qs(el) {if (window.RegExp && window.encodeURIComponent) {var ue=el.href;var qe=encodeURIComponent(document.f.q.value);if(ue.indexOf("q=")!=-1){el.href=ue.replace(new RegExp("q=[^&$]*"),"q="+qe);}else{el.href=ue+"&q="+qe;}}return 1;}
            // -->
            ..........

            第一個空行上面的就是“說明”了,下面是html代碼。有了說明,瀏覽器就知道這是什么了,拿到這段數據后,就把這些html標簽解釋成各種各樣的元素,在瀏覽器上有序地顯示出來。瀏覽器還蠻聰明的,當看到<img src=..>標簽,那就會又自己發一個請求報文給服務器,要求得到一個圖像文件,請求報文就像:

            GET /image/pp.jpg HTTP/1.1
            ....

            這樣,服務器端就找到這個.jpg圖像,加上"說明"之后發給瀏覽器,瀏覽器收到后就顯示在對應的位置上。遇到包含css、js...的標簽也一樣。

            如此重復,一個完整的web就會呈現在我們眼前了。

            服務器端代碼:

            /*****************************************************************

              mymultiwebserver.c 

              system:redhat linux Fedora Core 5

              enviroment:g++

              compile command:g++ -g -o mymultiwebserver -lpthread

              date:10/15/2006

              By Manio

             ****************************************************************
            */

            #include 
            <stdlib.h>

            #include 
            <sys/types.h>

            #include 
            <sys/socket.h>

            #include 
            <sys/stat.h>

            #include 
            <netinet/in.h>

            #include 
            <unistd.h>

            #include 
            <pthread.h>

            #include 
            <stdio.h>

            #include 
            <string.h>

            #include 
            <arpa/inet.h>



            #define PORT 8848

            #define BACKLOG 5

            #define MAXDATASIZE 1000

            #define DEBUG 1

            void process_cli(int connectfd, sockaddr_in client);

            int sendobj(int connectfd,char* serverfilepath);

            int IsDIR(char* fpath);

            int fileordirExist(char* fpath);

            char* getextname(char*);

            int writehead(FILE* cfp, char* extname);

            void* start_routine(void* arg);

            void msg404(int connectfd);



            struct ARG {

                   
            int connfd;

                   sockaddr_in client;

                   };

                   

            main()

            {

                  
            int listenfd, connectfd;

                  pthread_t thread;         
            //id of thread

                  ARG 
            *arg;            //pass this var to the thread

                  
            struct sockaddr_in server; //server's address info

                  
            struct sockaddr_in client; //client's

                  
            int sin_size;

                  

                  
            //create tcp socket

            #ifdef DEBUG

                  printf(
            "socket.... ");

            #endif

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

                                perror(
            "creating socket failed.");

                                exit(
            1);

                  }

                  

                  
            int opt = SO_REUSEADDR;

                  setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, 
            &opt, sizeof(opt));

                  

                  bzero(
            &server,sizeof(server));

                  server.sin_family 
            = AF_INET;

                  server.sin_port 
            = htons(PORT);

                  server.sin_addr.s_addr 
            = htonl(INADDR_ANY);

                  printf(
            "bind.... ");

                  
            if(bind(listenfd,(struct sockaddr *)&server,sizeof(struct sockaddr)) == -1) {

                      perror(
            "bind error.");

                      exit(
            1);

                  }

                  

                  printf(
            "listen.... ");

                  
            if(listen(listenfd,BACKLOG) == -1) {

                      perror(
            "listen() error ");

                      exit(
            1);

                  }



                  sin_size 
            = sizeof(struct sockaddr_in);

                  
            while(1)

                  {

                      
            //accept() using main thread

                      printf(
            "accepting.... ");

                      
            if((connectfd = accept(listenfd,

                                 (
            struct sockaddr *)&client,

                                 (socklen_t
            *)&sin_size)) == -1) {

                          printf(
            "accept() error ");

                      }



                      arg 
            = new ARG;

                      arg
            ->connfd = connectfd;

                      memcpy((
            void *)&arg->client, &client, sizeof(client));

                    

                      
            //invoke start_routine to handle this thread

            #ifdef DEBUG

                      printf(
            "thread_creating....");

            #endif

                      
            if(pthread_create(&thread, NULL, start_routine, (void*)arg)){

                          perror(
            "pthread_create() error");

                          exit(
            1);

                      }          

                  }

                  close(listenfd);      

            }





            //handle the request of the client

            void process_cli(int connectfd, sockaddr_in client)

            {

                
            int num;

                
            //char recvbuf[MAXDATASIZE], sendbuf[MAXDATASIZE], cli_name[MAXDATASIZE];

                
            char requestline[MAXDATASIZE], filepath[MAXDATASIZE], cmd[MAXDATASIZE],extname[MAXDATASIZE];

                
            int c;

                FILE 
            *fp;

                FILE 
            *cfp;

                fp 
            = fdopen(connectfd,"r");    

                

            #ifdef DEBUG

                printf(
            "the host is:%s  ",inet_ntoa(client.sin_addr) );

            #endif

                fgets(requestline,MAXDATASIZE,fp);

            #ifdef DEBUG

                printf(
            " THE REQUEST IS :%s ",requestline);

            #endif

                strcpy(filepath,
            "./");

                sscanf(requestline,
            "%s%s",cmd,filepath+2);

                strcpy(extname, getextname(filepath));

            #ifdef DEBUG

                printf(
            "cmd:%s filepath:%s extname:%s ",cmd,filepath,extname);

                

                printf(
            "string comparing :::::::::::::start::::::::::::::: ");    

            #endif

                
            if(strcmp(cmd,"GET"== 0){

                
            //the command is get

            #ifdef DEBUG

                    printf(
            "cmd(%s)==GET ",cmd);

            #endif

                    
            //is this a file or dir or notexist?

                    
            if(fileordirExist(filepath)){

                    
            //is a file or dir or none

                        
            //is this a dir 

                        
            if(IsDIR(filepath)){

                            
            //is a dir

            #ifdef DEBUG

                            printf(
            "%s is a DIR ",filepath);

            #endif

                            
            if( fileordirExist( strcat(filepath,"index.htm") )){

                                sendobj(connectfd,
            "index.htm");

                            }
            else if(fileordirExist(strcat(filepath,"index.html"))){

                                sendobj(connectfd,
            "index.htm");

                            }
            else{

                                msg404(connectfd);

                            }

                        }
            else{

                                
            //is a file

            #ifdef DEBUG

                                printf(
            "%s is a file",filepath);

            #endif

                                sendobj(connectfd,filepath);

                        }

                    }
            else{

            #ifdef DEBUG

                        printf(
            "404 ");

            #endif

                        msg404(connectfd);

                    }

                }
            else{

            #ifdef DEBUG

                    printf(
            "cmd(%s)!=GET ",cmd);

            #endif

                }

            #ifdef DEBUG

                printf(
            ":::::::::::::end::::::::::::::: ");    

            #endif

                close(connectfd);

            }

            //send the 404 error message to the client

            void msg404(int connectfd)

            {

                
            char* msg;

                msg  
            = "HTTP/1.0 404 Not Found Content-Type: text/plain 404 not found by Manio";

                send(connectfd,msg,strlen(msg),
            0);

            }

            //is the filepath a file  or directory

            int fileordirExist(char* fpath)

            {

                
            struct stat filestat;

                
            return (  stat(fpath,&filestat) != -1);

            }



            // is the filepath a directory

            int IsDIR(char* fpath)

            {

            #ifdef DEBUG

                printf(
            "IN IsDIR ");

            #endif

                
            struct stat filestat;

                
            return ( stat(fpath,&filestat) != -1 && S_ISDIR(filestat.st_mode));

            }



            //send the data of the file which the client want

            int sendobj(int connectfd,char* serverfilepath)

            {

                FILE
            * sfp,*cfp;

                
            int c;

                sfp 
            = fopen(serverfilepath,"r");

                cfp 
            = fdopen(connectfd,"w");



                writehead(cfp,getextname(serverfilepath));

                
            while( (c = getc(sfp)) != EOF)putc(c,cfp);    

                fflush(cfp);

                
            return 0;

            }

            //write the packet header to the client

            int writehead(FILE* cfp, char* extname)

            {

            #ifdef DEBUG

                printf(
            "INWRITEHEAD:::::::extname is %s::::::: ",extname);

            #endif

                
            char* content = "text/plain";

                
            if( strcmp(extname,"html"== 0 || strcmp(extname,"htm"== 0)

                    content 
            = "text/html";

                
            else if ( strcmp(extname,"css"== 0 )

                    content 
            = "text/css";

                
            else if ( strcmp(extname,"gif"== 0 )

                    content 
            = "image/gif";

                
            else if ( strcmp(extname,"jpeg"== 0 || strcmp(extname,"jpg"== 0)

                    content 
            = "image/jpeg";

                
            else if ( strcmp(extname,"png"== 0)

                    content 
            = "image/png";

            #ifdef DEBUG

                printf(
            "HTTP/1.1 200 OK ");

                printf(
            "Content-Type: %s ",content);

            #endif

                fprintf(cfp,
            "HTTP/1.1 200 OK ");

                fprintf(cfp,
            "Content-Type: %s ",content);

                
            return 0;

            }



            //get the extent name of the file

            char* getextname(char* filepath)

            {

                
            char* p;

                
            if(( p  =  strrchr(filepath,'.')) != NULL)

                           
            return p+1;

                
            return NULL;           

            }



            //invoked by pthread_create

            void* start_routine(void* arg)

            {

                ARG 
            *info;

                info 
            = (ARG *)arg;

                
            //handle client's requirement

                process_cli(info
            ->connfd, info->client);



                delete arg;

                pthread_exit(NULL);

            }

             

            運行方法:

            在fc5中打開控制臺,按下面的方法進行

            [root@localhost webserver1707]# ls
            admin           header   img        index.htm~
            chinaunix.html  header~  index.htm  mymultiwebserver.c
            [root@localhost webserver1707]# g++ -g -o mymultiwebserver mymultiwebserver.c -lpthread
            mymultiwebserver.c: In function 鈥榲oid* start_routine(void*)鈥?
            mymultiwebserver.c:253: 璀﹀憡錛氬垹闄?鈥榲oid*鈥?鏈畾涔?[root@localhost webserver1707]# ./mymultiwebserver socket....
            bind....
            listen....
            accepting....
            thread_creating....accepting....
            the host is:127.0.0.1
            THE REQUEST IS :GET / HTTP/1.1

            cmd:GET
            filepath:.//
            extname://
            string comparing
            :::::::::::::start:::::::::::::::
            cmd(GET)==GET
            IN IsDIR
            .// is a DIR
            INWRITEHEAD:::::::extname is htm:::::::
            HTTP/1.1 200 OK
            Content-Type: text/html

            thread_creating....accepting....
            :::::::::::::end:::::::::::::::
            thread_creating....accepting....
            the host is:127.0.0.1
            THE REQUEST IS :GET /img/sb.jpg HTTP/1.1

            cmd:GET
            filepath:.//img/sb.jpg
            extname:jpg
            string comparing
            :::::::::::::start:::::::::::::::
            cmd(GET)==GET
            IN IsDIR
            .//img/sb.jpg is a fileINWRITEHEAD:::::::extname is jpg:::::::
            HTTP/1.1 200 OK
            Content-Type: image/jpeg

            :::::::::::::end:::::::::::::::
            the host is:127.0.0.1
            THE REQUEST IS :GET /img/gcc.png HTTP/1.1

            cmd:GET
            filepath:.//img/gcc.png
            extname:png
            string comparing
            :::::::::::::start:::::::::::::::
            cmd(GET)==GET
            IN IsDIR
            .//img/gcc.png is a fileINWRITEHEAD:::::::extname is png:::::::
            HTTP/1.1 200 OK
            Content-Type: image/png

            :::::::::::::end:::::::::::::::

            放一個index.htm文件在此程序所在的文件夾,打開瀏覽器,在地址欄輸入http://127.0.0.1:8848/,就可以看到網頁了~

            posted on 2008-07-02 09:49 隨意門 閱讀(4801) 評論(1)  編輯 收藏 引用

            評論

            # re: 一個多線程web服務器實例(C,Linux,詳細的web服務器原理) 2013-10-01 21:13 OverSeven

            只能訪問一次,第二次訪問就出錯.
              回復  更多評論    
            中文字幕乱码人妻无码久久| AAA级久久久精品无码区| 欧美性大战久久久久久| 中文字幕精品无码久久久久久3D日动漫 | 久久综合久久综合久久| 久久亚洲国产精品一区二区| 久久国产精品免费一区二区三区| 亚洲国产成人乱码精品女人久久久不卡| 久久天天躁夜夜躁狠狠| 青青草原综合久久大伊人精品| 久久精品无码一区二区三区免费| 亚洲综合久久夜AV | 色综合久久88色综合天天| 久久久久久国产精品无码下载 | 久久久久高潮综合影院| 国产成人精品久久综合| 国产成年无码久久久久毛片| 久久青青草原精品国产软件| 久久精品国产秦先生| 欧美伊人久久大香线蕉综合| 久久er国产精品免费观看2| 国产毛片欧美毛片久久久| 无码任你躁久久久久久久| 久久精品国产免费| 久久久久亚洲av无码专区| 久久久这里有精品| 久久久久久久亚洲精品| 热99re久久国超精品首页| 久久久久成人精品无码中文字幕| 色婷婷狠狠久久综合五月| 久久久久亚洲AV无码专区桃色| 国产亚洲婷婷香蕉久久精品| 久久久精品国产免大香伊| 亚洲乱码中文字幕久久孕妇黑人| 国内精品久久国产| 女人高潮久久久叫人喷水| 亚洲伊人久久成综合人影院| 囯产精品久久久久久久久蜜桃| 久久这里的只有是精品23| 久久无码中文字幕东京热| 亚洲综合伊人久久大杳蕉|