目录
一、项目需求
实现一个http 服务器项目,服务器启动后监听80端口的tcp 连接,当用户通过任意一款浏览器(IE、火狐和腾讯浏览器等)访问我们的http服务器,http服务器会查找用户访问的html页面是否存在,如果存在则通过http 协议响应客户端的请求,把页面返回给浏览器,浏览器显示html页面;如果页面不存在,则按照http 协议的规定,通知浏览器此页面不存在(404 NOT FOUND)
二、需求分析
1、何为Html页面
html
,全称Hypertext Markup Language,也就是“超文本链接标示语言”。HTML文本是由 HTML命令组成的描述性文本,HTML 命令可以说明文字、 图形、动画、声音、表格、链接等。 即平常上网所看到的的网页。
demo.html
<html lang=\”zh-CN\”> <head> <meta content=\”text/html; charset=utf-8\” http-equiv=\”Content-Type\”> <title>This is a test</title> </head> <body> <div align=center height=\”500px\” > <br/><br/><br/> <h2>This is a test!</h2><br/><br/> <form action=”commit” method=”post”> 尊姓大名: <input type=”text” name=”name” /> <br/>芳龄几何: <input type=”password” name=”age” /> <br/><br/><br/><input type=”submit” value=”提交” /> <input type=”reset” value=”重置” /> </form> </div> </body> </html> |
2、何为http协议
HTTP
协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。
请求格式:
客户端请求
客户端发送一个HTTP请求到服务器的请求消息包括以下格式:请求行(request line)、请求头部(header)、空行和请求数据四个部分组成,下图给出了请求报文的一般格式。
服务
端
响应
服务器响应客户端的HTTP响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。
|
响应代号 |
代号描述 |
服务器上存在请求的内容,并可以响应给客户端 |
200 |
OK |
客户端的请求有异常,方法有问题 |
501 |
Method Not Implemented |
服务器收到请求后,因为自生的问题没法响应 |
500 |
Internal Server Error |
请求的内容不存在 |
404 |
NOT FOUND |
客户端发送的请求格式有问题等 |
400 |
BAD REQUEST |
三、实现Mini型http服务器
1、接受http请求
1. 实现按行读取请求头部
因为服务器端接受到的请求为第二点中的格式,是一行行的
//返回值: -1 表示读取出错, 等于0表示读到一个空行, 大于0 表示成功读取一行
int get_line(int sock, char *buf, int size){
int count = 0;
char ch = '\0';
int len = 0;
while( (count<size - 1) && ch!='\n'){
len = read(sock, &ch, 1);
if(len == 1){
if(ch == '\r'){
continue;
}else if(ch == '\n'){
//buf[count] = '\0';
break;
}
//这里处理一般的字符
buf[count] = ch;
count++;
}else if( len == -1 ){//读取出错
perror("read failed");
count = -1;
break;
}else {// read 返回0,客户端关闭sock 连接.
fprintf(stderr, "client close.\n");
count = -1;
break;
}
}
if(count >= 0) buf[count] = '\0';
return count;
}
2. 如果碰到两个连续的回车换行,即,意味着请求头部结束
2、解析http请求
void do_http_request(int client_sock){
int len = 0;
char buf[256];
char method[64];
char url[256];
char path[256];
/*读取客户端发送的http 请求*/
//1.读取请求行
len = get_line(client_sock, buf, sizeof(buf));
if(len > 0){//读到了请求行
int i=0, j=0;
while(!isspace(buf[j]) && (i<sizeof(method)-1)){
method[i] = buf[j];
i++;
j++;
}
method[i] = '\0';
if(debug) printf("request method: %s\n", method);
if(strncasecmp(method, "GET", i)==0){ //只处理get请求
if(debug) printf("method = GET\n");
//获取url
while(isspace(buf[j++]));//跳过白空格
i = 0;
while(!isspace(buf[j]) && (i<sizeof(url)-1)){
url[i] = buf[j];
i++;
j++;
}
url[i] = '\0';
if(debug) printf("url: %s\n", url);
//继续读取http 头部
do{
len = get_line(client_sock, buf, sizeof(buf));
if(debug) printf("read: %s\n", buf);
}while(len>0);
//***定位服务器本地的html文件***
//处理url 中的?
{
char *pos = strchr(url, '?');
if(pos){
*pos = '\0';
printf("real url: %s\n", url);
}
}
sprintf(path, "./html_docs/%s", url);
if(debug) printf("path: %s\n", path);
//执行http 响应
}else {
//非get请求, 读取http 头部,并响应客户端 501 Method Not Implemented
fprintf(stderr, "warning! other request [%s]\n", method);
do{
len = get_line(client_sock, buf, sizeof(buf));
if(debug) printf("read: %s\n", buf);
}while(len>0);
//unimplemented(client_sock); //在响应时再实现
}
}else {//请求格式有问题,出错处理
//bad_request(client_sock); //在响应时再实现
}
}
3、响应http请求
void do_http_response(int client_sock){
const char *main_header = "HTTP/1.0 200 OK\r\nServer: Martin Server\r\nContent-Type: text/html\r\nConnection: Close\r\n";
const char * welcome_content = "\
<html lang=\"zh-CN\">\n\
<head>\n\
<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\n\
<title>This is a test</title>\n\
</head>\n\
<body>\n\
<div align=center height=\"500px\" >\n\
<br/><br/><br/>\n\
<h2>大家好,欢迎来到xhc的个人主页!</h2><br/><br/>\n\
<form action=\"commit\" method=\"post\">\n\
尊姓大名: <input type=\"text\" name=\"name\" />\n\
<br/>芳龄几何: <input type=\"password\" name=\"age\" />\n\
<br/><br/><br/><input type=\"submit\" value=\"提交\" />\n\
<input type=\"reset\" value=\"重置\" />\n\
</form>\n\
</div>\n\
</body>\n\
</html>";
char send_buf[64];
int wc_len = strlen(welcome_content);
int len = write(client_sock, main_header, strlen(main_header));
if(debug) fprintf(stdout, "... do_http_response...\n");
if(debug) fprintf(stdout, "write[%d]: %s", len, main_header);
len =snprintf(send_buf, 64,"Content-Length: %d\r\n\r\n", wc_len);
len = write(client_sock, send_buf, len);
if(debug) fprintf(stdout, "write[%d]: %s", len, send_buf);
len = write(client_sock, welcome_content, wc_len);
if(debug) fprintf(stdout, "write[%d]: %s", len, welcome_content);
}
该响应目前只是针对结果的一种编程,并没有主动的去打开文件响应,后续改进