搭建簡單的http伺服器
阿新 • • 發佈:2019-01-06
開發語言C++,平臺為Linux。
主要流程為:伺服器獲得請求–>響應並處理請求–>返回結果。
這裡著重講怎麼處理請求。
主程式在獲得一個請求後會開闢一個執行緒來處理請求
流程圖如下。
hand_cgi函式流程圖
cgi程式流程圖。
程式碼:
#include"http.h"
int ret = 0;
void printf_log(string s){
//cout << s << endl;
}
int init_fd(int socketi, char* str){
sockaddr_in addr;
bzero(&addr, sizeof (addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(inet_addr(str));
addr.sin_port = htons(PORT);
int err = bind(socketi, (struct sockaddr*)&addr, sizeof(addr));
if (err < 0){
printf_log("bind error");
ret = -1;
}
int on = 0;
setsockopt( socketi, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on) );
err = listen(socketi, 5);
if (err < 0){
printf_log("listen error");
ret = -1;
}
return socketi;
}
static int get_line(int fd, char *buf){
char ch = 0;
char next = 0;
int i = 0;
int err = 0;
while (ch != '\n'){
int size = read(fd, &ch, 1 );
if (size <= 0){
ch = '\n';
}
if (ch != '\n' && ch != '\r'){
buf[i++] = ch;
}
else{
if (ch == '\r'){
err = recv(fd, &next, 1, MSG_PEEK);
if (err < 0){
printf_log("recv error");
return -1;
}
if (next == '\n'){
err = read(fd, &ch, 1);
if (err < 0){
printf_log("recv error");
return -1;
}
}//if
ch = '\n';
}
}//else
}//while
buf[i] = 0;
return 1;
}
static void clean_header(int fd){
char ch[100] = {0};
get_line(fd, ch);
while (strlen(ch) != 0){
get_line(fd, ch);
}
}
static int hand_cgi(int fd, char* path, char * argument){
char line[SIZE/2] = {0};
char length[10] = {0};
int i_length = 0;
char method[10] = {0};
if (strlen(argument) == 0){
sprintf(method, "METHOD=POST");
while (get_line(fd, line) > 0){
if (strncmp(line, "Content-Length", 16) == 0){
i_length = atoi(line + 16);
break;
}
}//while
clean_header(fd);
sprintf(length, "Content-Length=%d", i_length);
putenv(length);
}//if
else{
sprintf(method, "METHOD=GET");
char str[100] = {0};
sprintf(str, "QESTRING=%s", argument);
if (putenv(str) < 0){
printf_log("she zhi huai jing cuo wu");
ret = -1;
}
clean_header(fd);
}//else
putenv(method);
//reponse
char request[] = "HTTP/1.0 200 OK\r\n";
write(fd, request, strlen(request));
int _std[2] = {0};
int _std2[2] = {0};
if (pipe(_std) < 0){
printf_log("pipe error");
ret = -1;
}
if (pipe(_std2) < 0){
printf_log("pipe2 error");
ret = -1;
}
pid_t pid = fork();
if (pid < 0){
printf_log("fork error");
ret = -1;
return 0;
}
if (pid == 0){
close(_std[0]);
close(_std2[1]);
dup2(_std[1], 1);
dup2(_std2[0], 0);
execl(path,path, NULL);
cout << "error" << endl;
}
else{
close(_std[1]);
close(_std2[0]);
int ch = 0;
char string[SIZE/2] = {0};
for (int i = 0; i < i_length; i++){
read(fd, &ch, 1);
string[i] = ch;
}
char buf[SIZE/2] = {0};
write(_std2[1], string, strlen(string));
read(_std[2], buf, SIZE/2);
//返回輸出
write(fd, buf, strlen(buf));
}
close(_std[0]);
close(_std2[1]);
}
}
static void get(int fd){
char buf[2*SIZE];
char ch;
int i = 0;
while (read(fd, &ch, 1) > 0){
buf[i++] = ch;
}
// printf("%s\n", buf);
}
void *handler_in(void *arg){
pthread_detach(pthread_self());
char method[SIZE/2] = {0};
char buf[SIZE] = {0};
char line[SIZE/2] = {0};
char url[SIZE/2] = {0};
char goal[SIZE/2] = {0};
char argument[SIZE] = {0};
int err = 0;
int cgi = 0;
int i = 0;
char request[] = "HTTP/1.1 200 OK\r\n";
//cout << 123 << endl;
err = get_line((int)arg, line);
if (err < 0){
ret = 1;
goto end;
}
for (i = 0; line[i] != ' '; i++){
method[i] = line[i];
}
i++;
for (int j = 0; line[i] != ' '&&line[i] != '?'; i++){
url[j++] = line[i];
}
//printf("%s\n" ,method);
if (line[i] == '?'){
i++;
for (int j = 0; line[i] != ' '&&line[i] != '?'; i++){
argument[j++] = line[i];
}
}
if (strcasecmp(method, "POSE") != 0 && strcasecmp(method, "GET") != 0){
printf_log("method");
ret = 2;
goto end;
}
if (strcasecmp(method, "POSE") == 0)
cgi = 1;
if (strcasecmp(method, "GET") == 0 && argument[0] != 0)
cgi = 1;
if (strlen(url) == 1 && strcasecmp(url , "/") == 0)
sprintf(goal, "wwwroot/root.html");
else sprintf(goal, "wwwroot%s", url);
//判斷檔案
struct stat file;
err = stat(goal, &file);
if (err < 0){
ret = 3;
printf_log("file not found");
goto end;
}
if (file.st_mode & S_IFDIR){
//
}
if (file.st_mode & S_IXUSR || file.st_mode & S_IXGRP || file.st_mode & S_IXOTH){
if (0 == cgi){
ret = 4;
printf_log("檔案型別與cgi不匹配");
goto end;
}
}
if (1 == cgi){
err = hand_cgi((int)arg, goal, argument);
}//cgi if
else{
clean_header((int)arg);
write((int)arg, request, strlen(request));
write((int)arg, "\r\n", 2);
int fd = open(goal, O_RDONLY);
err = sendfile((int)arg, fd, 0, file.st_size);
if (err < 0){
ret = 5;
printf_log("sendfile error");
printf("%d, %s\n", errno, strerror(errno));
}
}//cgi else
end: close((int)arg);
}
cgi程式示例(2數相加):
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define SIZE 1024
using namespace std;
int hand(char *q){
char *num = NULL;
char *num_2 = NULL;
int num1 = 0;
int num2 = 0;
while (*q != '=')
q++;
q++;
num = q;
while (*q != '&')
q++;
*q = 0;
num1 = atoi(num);
while (*q != '=')
q++;
q++;
num_2 = q;
num2 = atoi(num_2);
printf("<html>\n");
printf("<head><h2>%d + %d = %d</h2></head>\r\n", num1, num2, num1+num2);
printf("</html>\n");
}
int main(){
printf("Content-Type:text/html;charset=ISO-8859-1\n\n");
char *p = NULL;
int i_length = 0;
char query_string[SIZE/2] = {0};
char *length = NULL;
p = getenv("METHOD");
if (strcmp(p, "GET") == 0){
char *s = getenv("QESTRING");
memcpy(query_string, s, strlen(s));
}//if
else{
length = getenv("Content-Length");
i_length = atoi(length);
char ch = 0;
for (int i = 0; i < i_length; i++){
ch = getc(0);
query_string[i] = ch;
}
}
hand(query_string);
printf("\n");
return 0;
}
執行:
傳送請求
返回結果:
此外,我們可再寫一個可以連線mysql資料庫的cgi程式,這樣我們就可以遠端管理資料庫了。
cgi程式:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<mysql.h>
#include<cstring>
using namespace std;
const int SIZE = 100;
//把值賦給相應的引數
void static handing(char* &str, char* &query){
while (*query != '=')
query++;
str = ++query;
while (*query != '&'&& *query != 0)
query++;
*query = 0;
query++;
}
//取值
static void hand_query(int &mod, char* &name, int &age, char *&where, char *query){
char *str = NULL;
handing(str, query);
mod = atoi(str);
handing(where, query);
handing(name, query);
if (3 == mod)
return;
handing(str, query);
age = atoi(str);
}
void find(MYSQL *conn, char *name, char *where){
MYSQL_RES *result;
MYSQL_ROW row;
char buf[100] = {0};
sprintf(buf, "SELECT * FROM %s", where);
mysql_query(conn, buf);
result = mysql_store_result(conn);
if (NULL == result)
cout << "NULL" << endl;
int num_fields = mysql_num_fields(result);
while ((row = mysql_fetch_row(result))){
if (strcmp(row[0], name) == 0)
for (int i = 0; i < num_fields; i++){
printf("%s ", row[i] ? row[i] : "NULL");
}
}
mysql_free_result(result);
}
int hand(char *query){
int mod = 0;
char *name = NULL;
char buf[100] ={0};
int age = 0;
char *where = NULL;
hand_query(mod, name, age, where, query);
MYSQL *conn = new MYSQL;
conn = mysql_init(NULL);
if (conn == NULL){
printf("ERROR %u: %s\n", mysql_errno(conn), mysql_error(conn));
exit(1);
}
if (mysql_real_connect(conn, "localhost", "root",
NULL, "tq", 0, NULL, 0)==NULL){
printf("ERROR %u:%s\n", mysql_errno(conn), mysql_error(conn));
exit(1);
}
//這裡只列舉最簡單的find操作
switch(mod){
case 1:creat(conn, name, age, where);
break;
case 2:insert(conn, name, age,where);
break;
case 3:find(conn, name,where);
break;
case 4:modifier(conn, name, age, where);
break;
case 5:my_delete(conn, name, where);
default:
break;
}
mysql_close(conn);
}
int main(){
printf("Content-Type:text/html;charset=ISO-8859-1\n\n");
char *p = NULL;
int i_length = 0;
char query_string[SIZE/2] = {0};
char *length = NULL;
p = getenv("METHOD");
if (strcmp(p, "GET") == 0){
char *s = getenv("QESTRING");
memcpy(query_string, s, strlen(s));
}//if
else{
length = getenv("Content-Length");
i_length = atoi(length);
char ch = 0;
for (int i = 0; i < i_length; i++){
ch = getc(0);
query_string[i] = ch;
}
}
hand(query_string);
printf("\n");
return 0;
}
mod表示操作,where是哪個·表,name是人名。
tbq這個表如下:
處理請求的結果:
前端知識知之甚少,勿怪啊。。
在這裡一個簡單的http伺服器就搭建完成了,但是我們還可以做很多事。比如,當請求過多時效率太低怎麼辦?再者,我們可以寫一個爬蟲程式來提高。除此之外,我們可以在深入調研nginx伺服器。
原始碼地址:https://github.com/fengasdf/-http-(由於整合了協程,python爬蟲,以及其他程式,可能有點亂)
http伺服器運用協程:
http://blog.csdn.net/fengasdfgh/article/details/76688440