Socket网络编程--简单Web服务器(4)
上一小节已经实现了对图片的传输,接下来就是判断文件是否为js,css,png等格式。我们增加一个函数用于判断格式
1 int WebServer::get_filetype(char *type,char *path)//用于判断该url指向文件的后缀 2 { 3 if(strstr(path,".html")) 4 strcpy(type,"text/html"); 5 else if(strstr(path,".gif")) 6 strcpy(type,"image/gif"); 7 else if(strstr(path,".jpg")) 8 strcpy(type,"image/jpeg"); 9 else if(strstr(path,".png")) 10 strcpy(type,"image/png"); 11 else if(strstr(path,".ico")) 12 strcpy(type,"image/x-icon"); 13 else if(strstr(path,".js")) 14 strcpy(type,"text/javascript"); 15 else if(strstr(path,".json")) 16 strcpy(type,"application/json"); 17 else if(strstr(path,".css")) 18 strcpy(type,"text/css"); 19 else 20 strcpy(type,"text/plain"); 21 return 0; 22 }
然后修改一下主页index.html的文件
1 <html> 2 <head> 3 <title>Test</title> 4 <meta http-equiv="Content-Type" content="text/html ; charset=utf-8"> 5 <link rel="stylesheet" href="style.css" type="text/css"/> 6 <script language="javascript" src="javascript.js"></script> 7 </head> 8 9 <body> 10 <div class="ceshi">图片</div><img src="ab.jpg"></img> 11 <input name="button" type="button" value="Click!" onclick=hi();></input> 12 </body> 13 </html>
两个外部文件如下
1 [myuser@bogon www]$ cat style.css 2 .ceshi {font-size:20px; color:#ff0000;} 3 [myuser@bogon www]$ cat javascript.js 4 function hi() 5 { 6 alert("js 调用,你单机了按钮"); 7 } 8 [myuser@bogon www]$
下面这个是运行的结果图
用chrome浏览器的F12调试工具获取到的状态,可以看到,该网页可以获取到index.html,然后浏览器会根据这个页面再发送index.html文件所需要的外部文件的请求,从上面可以看出请求了style.css,javascript.js和ab.jpg图片。然后显示网页,我单击了那个button也是可以调用js脚本的。
接下来就是判断一个提交是get还是post,网页index.html的源代码
1 <body> 2 <div class="ceshi">图片</div><img src="ab.jpg"></img> 3 <input name="button" type="button" value="Click!" onclick=hi();></input> 4 5 <hr> 6 <br>使用post方式<br> 7 <form method="post" name="frm1"> 8 <label>用户名:</label> 9 <input type="text" name="username" /> 10 <br> 11 <label>密码:</label> 12 <input type="password" name="password" /> 13 <br> 14 <input type="submit" name="commit" value="登陆"/> 15 <br> 16 </form> 17 <hr> 18 <br>使用get方式<br> 19 <form method="get" name="frm1" action="index.html"> 20 <label>用户名:</label> 21 <input type="text" name="username" /> 22 <br> 23 <label>密码:</label> 24 <input type="password" name="password" /> 25 <br> 26 <input type="submit" name="commit" value="登陆"/> 27 <br> 28 </form> 29 </body>
修改后的ServerRequest函数
1 int WebServer::ServerRequest(int cli_fd) 2 { 3 char buf[1024]; 4 int size=1024; 5 int i,j; 6 char method[255];//用于保存请求方式 7 char url[512]; 8 char path[1024]; 9 char args[1024]; 10 struct stat st; 11 int cgi;//cgi 为0 表示get普通方法 1表示get带参方法 2表示post方法 12 pid_t pid; 13 memset(buf,0,sizeof(buf)); 14 cgi=0; 15 //获取第一行请求信息 一般格式为: GET / HTTP/1.1 16 // POST / HTTP/1.1 17 size=get_line(cli_fd,buf,sizeof(buf)); 18 //cout<<"\t\t"<<buf<<endl; 19 i=0,j=0; 20 //截取第一个单词 21 while(!isspace(buf[j]) && (i<sizeof(method)-1)) 22 { 23 method[i]=buf[j]; 24 i++;j++; 25 } 26 method[i]='\0'; 27 //取第一个与第二个单词之间的空格 28 while(isspace(buf[j]) && (j<sizeof(buf))) 29 j++; 30 31 if(strcasecmp(method,"GET") && strcasecmp(method,"POST")) 32 { 33 Page_501(cli_fd); 34 return -1; 35 } 36 37 if(strcasecmp(method,"GET")==0) 38 { 39 cout<<"此次请求的方式是GET方法"<<endl; 40 cgi=0; 41 } 42 else if(strcasecmp(method,"POST")==0) 43 { 44 cout<<"此次请求的方式是POST方法"<<endl; 45 cgi=2; 46 } 47 48 //截取第二个单词 49 i=0; 50 int flag=0; 51 while(!isspace(buf[j]) && (i<sizeof(url)-1) && (j<sizeof(buf))) 52 { 53 if(buf[j]=='?') 54 { 55 flag=1; 56 j++; 57 i=0; 58 url[i]='\0'; 59 cgi=(cgi==0?1:2); 60 continue; 61 } 62 if(flag==0) 63 { 64 url[i]=buf[j]; 65 i++;j++; 66 } 67 else if(flag==1) 68 { 69 args[i]=buf[j]; 70 i++;j++; 71 } 72 } 73 if(flag==0) 74 url[i]='\0'; 75 else 76 args[i]='\0'; 77 78 sprintf(path,"www%s",url);//这个是web服务器的主目录,这个以后可以处理成读取配置文件,这里就先写固定的www目录 79 if(path[strlen(path)-1]=='/') 80 strcat(path,"index.html");//同上 81 82 //cout<<"============>此次请求的地址为:"<<path<<":"<<args<<endl; 83 84 //根据文件名,获取该文件的文件信息。如果为-1,表示获取该文件失败 85 if(stat(path,&st)==-1) 86 { 87 while((size>0) && strcmp("\n",buf))//去除掉多余的请求头信息 88 size=get_line(cli_fd,buf,sizeof(buf)); 89 Page_404(cli_fd); 90 } 91 else 92 { 93 if(S_ISDIR(st.st_mode))//判断url地址,如果是个目录,那么就访问该目录的index.html 94 { 95 strcat(path,"/index.html"); 96 //cout<<"此次请求的地址为:"<<path<<endl; 97 } 98 if(!S_ISDIR(st.st_mode)&&((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH)))//判断该url地址所对应的文件是否是可执行,并且是否有权限 99 { 100 cgi=2;//是一个cgi程序 101 } 102 if(cgi==0)//如果cgi为0,那么就表示该url所对应的文件不是cgi程序,而是一个简单的静态页面 103 { 104 pid = fork(); 105 if(pid==0) 106 { 107 ServerCatHttpPage(cli_fd,path,st.st_size); 108 } 109 } 110 else if(cgi==1)//get方法带参数 111 { 112 pid=fork(); 113 if(pid==0) 114 { 115 while((size>0) && strcmp("\n",buf))//去除掉多余的请求头信息 116 size=get_line(cli_fd,buf,sizeof(buf)); 117 ServerGetFunction(cli_fd,path,args); 118 } 119 } 120 else if(cgi==2)//post方法 121 { 122 pid=fork(); 123 if(pid==0) 124 { 125 int content_length=0; 126 while((size>0) && strcmp("\n",buf))//去除掉多余的请求头信息 127 { 128 size=get_line(cli_fd,buf,sizeof(buf)); 129 buf[15]='\0'; 130 if(strcasecmp(buf,"Content-Length:")==0) 131 { 132 content_length=atoi(&(buf[16])); 133 } 134 } 135 if(content_length==0) 136 { 137 Page_400(cli_fd); 138 return 0; 139 } 140 char c; 141 j=0; 142 for(int i=0;i<content_length;i++) 143 { 144 recv(cli_fd,&c,1,0); 145 args[j]=c; 146 j++; 147 } 148 args[j]=0; 149 ServerPostFunction(cli_fd,path,args); 150 } 151 } 152 } 153 close(cli_fd); 154 return 0; 155 }
增加的两个处理cgi程序的函数(下小节实现)
1 int WebServer::ServerGetFunction(int cli_fd,char *path,char *args) 2 { 3 cout<<"cli_fd:"<<cli_fd<<" path:"<<path<<" args:"<<args<<endl; 4 Page_200(cli_fd); 5 return 0; 6 } 7 int WebServer::ServerPostFunction(int cli_fd,char *path,char *args) 8 { 9 cout<<"cli_fd:"<<cli_fd<<" path:"<<path<<" args:"<<args<<endl; 10 Page_200(cli_fd); 11 return 0; 12 }
下面这个是运行的结果
从上图可以看出通过GET方法和POST方法都可以正常的获取到从表单那里传过去的参数。我们知道中文在url地址中的显示是显示成16进制的。这个是URL编码。接下来要实现对这个URL编码进行转换,这里使用网上别人提供的代码。
1 #include <ctype.h> 2 #include <string> 3 #include <string.h> 4 #include <stdio.h> 5 #include <iostream> 6 #include <stdlib.h> 7 using namespace std; 8 static int php_htoi(char *s) 9 { 10 int value; 11 int c; 12 13 c = ((unsigned char *)s)[0]; 14 if (isupper(c)) 15 c = tolower(c); 16 value = (c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10) * 16; 17 18 c = ((unsigned char *)s)[1]; 19 if (isupper(c)) 20 c = tolower(c); 21 value += c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10; 22 23 return (value); 24 } 25 /* 26 *URL解码,提取自PHP 5.2.17 27 *用法:string urldecode(string str_source) 28 *时间:2012-8-14 By Dewei 29 * */ 30 string urldecode(string &str_source) 31 { 32 char const *in_str = str_source.c_str(); 33 int in_str_len = strlen(in_str); 34 int out_str_len = 0; 35 string out_str; 36 char *str; 37 38 str = strdup(in_str); 39 char *dest = str; 40 char *data = str; 41 42 while (in_str_len--) { 43 if (*data == '+') { 44 *dest = ' '; 45 } 46 else if (*data == '%' && in_str_len >= 2 && isxdigit((int) *(data + 1)) 47 && isxdigit((int) *(data + 2))) { 48 *dest = (char) php_htoi(data + 1); 49 data += 2; 50 in_str_len -= 2; 51 } else { 52 *dest = *data; 53 } 54 data++; 55 dest++; 56 } 57 *dest = '\0'; 58 out_str_len = dest - str; 59 out_str = str; 60 free(str); 61 return out_str; 62 } 63 64 int main() 65 { 66 string str="username=%E7%99%BB%E5%BD%95"; 67 string out=urldecode(str); 68 cout<<out<<endl; 69 return 0; 70 }
我们通过在传参的时候调用urldecode进行解码就可以实现中文显示了。
这一节就到这里,下一小节将实现对cgi的支持。
参考资料: http://blog.csdn.net/xiaojianpitt/article/details/4389247
: http://dewei.iteye.com/blog/1630969 (关于URL编码解码代码)
作者:无脑仔的小明 出处:http://www.cnblogs.com/wunaozai/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 如果文中有什么错误,欢迎指出。以免更多的人被误导。有需要沟通的,可以站内私信,文章留言,或者关注“无脑仔的小明”公众号私信我。一定尽力回答。 |