原理:
在浏览器中输入一个网址,回车之后,浏览器会向相应主机的相应端口发送一段报文,如果是http协议的(如平常看到的网页的传输协议),就会发送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.

服务器端程序运行后,一直监听12345端品(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> | <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&
gt; | <a href="https://www.google.com/accounts/ManageAccount">我的帐户</a> | <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就会呈现在我们眼前了。

 

 

这里是服务器源码,本来打算可以传完整http网页的,包括图片。不过图片传输一直有问题。暂时放一放了。

 

    1. #include <stdio.h>
    2. #include <string.h>
    3. #include <fcntl.h>
    4. #include <pthread.h>
    5. #include <sys/socket.h>
    6. #include <netdb.h>
    7. #include <netinet/in.h>
    8. 
    9. #define HOSTLEN 256
    10. #define PORTNUM 12345
    11. 
    12. //#define DEBUG
    13. 
    14. int make_server_socket_q(int,int);
    15. 
    16. void error(char *err)
    17. {
    18.     fprintf(stderr,"error detected %s :",err);
    19.     perror("");
    20.     exit(1);
    21. }
    22. 
    23. int make_server_socket(int port)        //num for listen
    24. {
    25.     return make_server_socket_q(port,1);
    26. }
    27. 
    28. int make_server_socket_q(int port,int backlog)
    29. {
    30.     struct sockaddr_in saddr_in;
    31.     struct hostent *hp;         /*惠普*/
    32.     char hostname[HOSTLEN];
    33.     int sock_id;
    34. 
    35.     /* 获取一个socket*/
    36.     sock_id=socket(PF_INET,SOCK_STREAM,0);
    37. 
    38.     if(sock_id==-1)
    39.         error("socket failed");
    40.     
    41.     memset((void *)&saddr_in,0,sizeof(saddr_in));
    42. 
    43.     gethostname(hostname,HOSTLEN);
    44.     hp=gethostbyname(hostname);
    45. 
    46.     bcopy(hp->h_addr,(void *)&saddr_in.sin_addr,hp->h_length);
    47.     saddr_in.sin_port=htons(port);
    48.     saddr_in.sin_family=hp->h_addrtype;
    49. 
    50.     /* 绑定 */
    51.     if(bind(sock_id,(struct sockaddr *)&saddr_in,sizeof(struct sockaddr_in))==-1)
    52.     {
    53.         close(sock_id);
    54.         error("bind failed");
    55.     }
    56. 
    57.     /* 允许接入*/
    58.     if(listen(sock_id,3))
    59.         error("listen failed");
    60. 
    61.     return sock_id;
    62. 
    63. }
    64. 
    65. /* 测试是否存在 */
    66. int check_exist(char *filename)
    67. {
    68.     FILE *fp;
    69.     if((fp=fopen(filename,"r"))==NULL)
    70.         return 0;
    71.     fclose(fp);
    72.     return 1;
    73. }
    74. 
    75. /* 检测文件类型 */
    76. char * file_type(char *filename)
    77. {
    78.     char *p=filename;
    79.     char *ext=filename+strlen(filename);
    80.     while(ext>p&&*ext!='.')
    81.         ext--;
    82.     if(ext>p)
    83.         return ext+1;
    84.     return NULL;
    85. }
    86. 
    87. /* 报文 头部 */
    88. void do_head(int sock_fd,char *type)
    89. {
    90.     char buffer[64];
    91.     sprintf(buffer,"HTTP/1.0 200 OK/r/n");
    92.     if(type)
    93.         sprintf(buffer+strlen(buffer),"Content-Type:%s/r/n",type);
    94.     write(sock_fd,buffer,strlen(buffer));   
    95. }
    96. 
    97. /* 404 文件没找到 */
    98. void do_nofile(char *filename,int sock_fd)
    99. {
    100.     FILE *fp=fdopen(sock_fd,"w");
    101. 
    102.     fprintf(fp,"HTTP/1.1 404 Not Found/r/n");
    103.     fprintf(fp,"Content-type:text/plain/r/n");
    104.     fprintf(fp,"/r/n");
    105.     fprintf(fp,"The file you requested: %s is not found/r/n",filename);
    106. 
    107.     fclose(fp);
    108. }
    109. 
    110. /* 处理其它命令 */
    111. void do_unkown(int sock_fd)
    112. {
    113.     FILE *fp=fdopen(sock_fd,"w");
    114.     fprintf(fp,"HTTP/1.0 501 Not Implement /r/n");
    115.     fprintf(fp,"Content-Type:text/plain/r/n");
    116.     fprintf(fp,"/r/n");
    117.     fprintf(fp,"That command is not yet implement/r/n");
    118.     fclose(fp);
    119. }
    120. 
    121. /* 核心处理 从文件中读取 然后写入socket */
    122. void do_show(char *arg,int sock_fd)
    123. {
    124.     FILE *sock_fp,*file_fp;
    125.     char *extension=file_type(arg);
    126.     char *content="text/plain";
    127.     char buf[BUFSIZ]={0};
    128.     char c;
    129.     if(!extension)
    130.         return ;
    131. 
    132.     if(strcmp(extension,"html")==0)
    133.         content="text/html";
    134.     else if(strcmp(extension,"htm")==0)
    135.         content="text/htm";
    136.     else if(strcmp(extension,"gif")==0)
    137.         content="image/gif";
    138.     else if(strcmp(extension,"jpg")==0)
    139.         content="image/jpg";
    140.     else if(strcmp(extension,"png")==0)
    141.         content="image/png";
    142.     else
    143.         content="text/plain";       //默认处理
    144. 
    145.         sock_fp=fdopen(sock_fd,"w");
    146.         file_fp=fopen(arg,"rb");
    147. 
    148.         if(file_fp!=NULL&&sock_fp!=NULL)
    149.         {
    150.             do_head(sock_fd,content);
    151.             while((fgets(buf,BUFSIZ,file_fp)))
    152.                 fputs(buf,sock_fp);
    153.             fclose(file_fp);
    154.             fclose(sock_fp);
    155.         }
    156. }
    157. 
    158. /* request处理 */
    159. int process_rq(char *request,int sock_fd)
    160. {
    161.     char cmd[16],arg[BUFSIZ],buf[BUFSIZ];
    162.     
    163.     strcpy(arg,"./");
    164. 
    165.     if(sscanf(request,"%s%s",cmd,buf)!=2)
    166.         return -1;
    167. 
    168.     /* 下面是处理文件名 */
    169.     if(buf[0]=='.'&&buf[1]=='/')
    170.         strcpy(arg,buf);
    171.     else if(buf[0]=='/')
    172.         strcpy(arg+1,buf);
    173.     else
    174.         strcpy(arg+2,buf);
    175. 
    176.     /* 目前只能支持get命令 */
    177.     if(!(strcmp(cmd,"GET")==0||strcmp(cmd,"get")==0))
    178.         do_unkown(sock_fd);
    179.     else if(!check_exist(arg))
    180.         do_nofile(arg,sock_fd);
    181.     else
    182.         do_show(arg,sock_fd);
    183. 
    184. }
    185. 
    186. /* 线程主函数 */
    187. void thread_routine(void *fd)
    188. {
    189.     int sock_fd=*((int *)fd);
    190.     char request[BUFSIZ];
    191.     FILE *sock_fp=fdopen(sock_fd,"r");
    192.     fgets(request,BUFSIZ,sock_fp);
    193. #ifdef DEBUG
    194.     printf("a call:request= %s/n",request);
    195. #else
    196.     process_rq(request,sock_fd);
    197. #endif
    198.     fclose(sock_fp);
    199. }
    200. 
    201. 
    202. int main(int argc,char *argv[])
    203. {
    204.     int port;
    205.     int sock_id;
    206.     int sock_fd;
    207.     
    208.     FILE *sock_fp;
    209.     
    210.     pthread_t thread;
    211. 
    212.     if(!(argc==1||argc==2))
    213.     {
    214.         printf("usage : %s port",argv[0]);  
    215.         return -1;
    216.     }
    217.     
    218.     port=argc==1?PORTNUM:atoi(argv[1]); //注意这里如果只有一个参数,即是设置默认端口
    219. 
    220.     if((sock_id=make_server_socket(port))==-1)
    221.         return -1;
    222. 
    223.     printf("now listen to the port #%d/n",port);
    224. 
    225.     fflush(stdout);
    226. 
    227.     while(1)
    228.     {
    229.         sock_fd=accept(sock_id,NULL,NULL);
    230.         sock_fp=fdopen(sock_fd,"r");
    231. 
    232.         pthread_create(&thread,NULL,(void *)thread_routine,(void *)&sock_fd);   //开辟线程处理
    233.         fclose(sock_fp);
    234.     }
    235.     
    236.     return 0;
    237. }
    238. 
    239.