前言:
我们在之前的TinyHttpd的精读(可以在首页去查看)中已经是基本的了解了显示一个网页的基本过程,那么我们学习后可以通过手写一个精简版的进行巩固下。
0.新工程的建立
我们也可以顺带复习下如何通过cmake在ubuntu下新建一个工程(记得提前下载cmake)。
1.新建文件夹My_Tinyhttpd。
2.新建文件:CMakeLists.txt,service_socket.cpp。
3.CMakeList.txt的内容:
cmake_minimum_required(VERSION 3.0.0)
project("Main")
add_executable(service service_socket.cpp)
4.service_socket.cpp:
#include <iostream>
int main(){std::cout<<"hello world\n";return 1;
}
5.执行cmake ./ 和 cmake。
如果没有报错则成果物会被正常的生成出来为service,然后我们执行 ./service 就可以看到控制台输出hello world了。我们的项目就基本构建完成了。接下来就是具体的内容。
1.main函数:
在这个main函数中我们的目的是新建一个socket链接,然后创建一个线程去接受信息并进行处理。最后当我们处理完信息后我们就关闭socket链接。具体代码如下:
void accept_request(int client_id){printf("Get Message...\n");
}
int main(){char buffer1[MAXNUM] = {0};int service_id,new_socket;struct sockaddr_in address;socklen_t addlen = sizeof(address);//socklen_t addlen;// service_id = start(address);// int service_id,new_socket;service_id = socket(AF_INET,SOCK_STREAM,0);//创建socketif(service_id == 0){printf("socket failed\n");return service_id;}address.sin_family = AF_INET;address.sin_addr.s_addr = INADDR_ANY;address.sin_port = htons(8008);printf("Bind...\n");//绑定if(bind(service_id, (struct sockaddr *)&address, sizeof(address)) < 0){printf("bind failed\n");printf("Error Message is %s\n",strerror(errno));return -1;}//监听printf("Listen...\n");if(listen(service_id,3) < 0){printf("listen error\n");return -1;}while(1){//接受链接:int new_socket = accept(service_id,(struct sockaddr*)&address,(socklen_t*)&addlen);if(new_socket <0){printf("accept client error\n");}std::thread m_thread(accept_request,new_socket);m_thread.detach();}printf("Close Socket...\n");close(service_id);return 1;
}
我们按照这个写完代码后,我们执行make然后运行下service(./service).然后打开浏览器输入
http://localhost:8008/.然后看log是否会打印Get Message...。如果可以的话那么基本上我们的这段代码就基本OK了。然后我们把sokcet通信部分的代码封装在start函数中。
void accept_request(int client_id){printf("Get Message...\n");
}int start(struct sockaddr_in& address){int service_id,new_socket;service_id = socket(AF_INET,SOCK_STREAM,0);//创建socketif(service_id == 0){printf("socket failed\n");return service_id;}address.sin_family = AF_INET;address.sin_addr.s_addr = INADDR_ANY;address.sin_port = htons(8008);printf("Bind...\n");//绑定if(bind(service_id, (struct sockaddr *)&address, sizeof(address)) < 0){printf("bind failed\n");printf("Error Message is %s\n",strerror(errno));return -1;}//监听printf("Listen...\n");if(listen(service_id,3) < 0){printf("listen error\n");return -1;}return service_id;
}int main(){char buffer1[MAXNUM] = {0};int service_id,new_socket;struct sockaddr_in address;socklen_t addlen = sizeof(address);//socklen_t addlen;service_id = start(address);while(1){//接受链接:int new_socket = accept(service_id,(struct sockaddr*)&address,(socklen_t*)&addlen);if(new_socket <0){printf("accept client error\n");}std::thread m_thread(accept_request,new_socket);m_thread.detach();}printf("Close Socket...\n");close(service_id);return 1;
}
然后我们同样可以去验证下看看是否会有Get Message...信息。
2.accept_reques函数
基本的sokcet通讯已经完成了我们接下来进行数据分析/处理函数的部分。这部分函数会涉及到别的函数,我们暂时先聚焦于这个函数的主体思路。
主体思路:从我们的socket中提出method方法,来判断我们是显示静态网页和动态网页。
基于以上思路,我们首先来看下我们从socket反馈中能获取到啥信息,这样才方便我们后面写代码的思路。
void print_buffer(char buffer[]){for(int i = 0;i < MAXNUM;i++){if(buffer[i] != '\0')std::cout<<buffer[i];}
}void accept_request(int client_id){char buffer_line[MAXNUM] = {0};char buffer[MAXNUM] = {0};char method[MAXNUM] = {0};char path[MAXNUM] = {0};int i = 0,j = 0;printf("Get Message...\n");auto getmessage = read(client_id,buffer,sizeof(buffer));if(getmessage <= 0){printf("wait client message\n");} else {std::cout<<"client say :";print_buffer(buffer);}
}
结果如下:
我们发现我们要获取的信息(GET OR POST)是在这个信息的第一行,所以我们第一步得先获取第一行,然后再这行信息中获取GET或者POST方法。
2.1get_line函数:
void get_line(char buffer1[],char buffer2[]){int i = 0;//buffer1 = {0};while(buffer2[i] != '\n'){buffer1[i] = buffer2[i];i++;}buffer1[i] = '\0';
}
思路:以换行符为行数的确定标记,来将第一行复制出来。
2.2获取method
当我们获取到第一行后我们就需要考虑当前我们是收到的GET还是POST方法呢?从我们的获取到的buffer信息中可以看到当前网页给的第一次是GET方法。那么从一行字符串中获取到一个子串的方法就不在详细赘述了,直接看代码即可。
void accept_request(int client_id){char buffer_line[MAXNUM] = {0};char buffer[MAXNUM] = {0};char method[MAXNUM] = {0};char path[MAXNUM] = {0};int i = 0,j = 0;printf("Get Message...\n");auto getmessage = read(client_id,buffer,sizeof(buffer));if(getmessage <= 0){printf("wait client message\n");} else {std::cout<<"client say :";print_buffer(buffer);}get_line(buffer_line,buffer);printf("accept_request:");print_buffer(buffer_line);// //get methodwhile(buffer_line[i] != ' '){method[i] = buffer_line[i];i++;}print_buffer(method);
}
2.3判断当前的method方法
这个就是直接判断是GET还是POST方法,如果是GET方法,我们就显示静态网页,如果是POST我们就显示动态网页。如果两者都不是我们就直接报错。
void accept_request(int client_id){char buffer_line[MAXNUM] = {0};char buffer[MAXNUM] = {0};char method[MAXNUM] = {0};char path[MAXNUM] = {0};int i = 0,j = 0;printf("Get Message...\n");auto getmessage = read(client_id,buffer,sizeof(buffer));if(getmessage <= 0){printf("wait client message\n");} else {std::cout<<"client say :";print_buffer(buffer);}get_line(buffer_line,buffer);printf("accept_request:");print_buffer(buffer_line);//get methodwhile(buffer_line[i] != ' '){method[i] = buffer_line[i];i++;}print_buffer(method);if(strcasecmp(method,"GET") && strcasecmp(method,"POST")){printf("error Method");}if(strcasecmp(method,"GET") == 0){headers(client_id);saver_file(client_id);}
}
至此,一个基本完整的accept_request函数就基本完成了。那么下一章我们就来处理网页的显示。