栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

c++框架一 :tinyhttpd

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

c++框架一 :tinyhttpd

写代码从学习优秀的代码开始!

开始学习c++的框架,从最小型的开始,tinyhttpd最适合不过了,总共500来行代码。从socket服务建立,到多线程和创建子进程,并在父子进程间通过管道通信,同时又可以了解http的一些基本原理,可以说非常适合学习。

代码总体架构如下:

代码阅读顺序(为方便阅读,只写关键代码)

1、首先是main(),在main中调用startup()函数建立服务端监听,如果传参为0,则会自动选择一个可用端口,并用getsockname()获取服务信息,这里主要是获取监听的端口号。

while()循环等待客户端连接,每客户端到来,就新建线程处理。

int main(void)
{
 //startup()函数就是socket()、bind()、listen(),返回创建的fd
 server_sock = startup(&port);  

 while (1)
 {
  client_sock = accept(server_sock,(struct sockaddr *)&client_name,
                       &client_name_len);
 
 //每来一个客户端连接,就创建一个线程处理。
 pthread_create(&newthread , NULL, accept_request, client_sock);
 }
}

2、线程处理函数

读取浏览器发送的http协议行,分离出:GET/POST 和 请求的目录,

确定是读取index.html文件,还是执行cgi文件。

void accept_request(void *pfd)
{
 int client = *((int*)pfd);

 
 numchars = get_line(client, buf, sizeof(buf));

 while (!ISspace(buf[j]) && (i < sizeof(method) - 1))
 {
  method[i] = buf[j];
  i++; j++;
 }
 method[i] = '';
//此时mtthod = GET/POST

 if (strcasecmp(method, "POST") == 0)
  cgi = 1;

 i = 0;
 while (ISspace(buf[j]) && (j < sizeof(buf)))
  j++;
 while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf)))
 {
  url[i] = buf[j];
  i++; j++;
 }
 url[i] = '';
//此时url=/ 或 url=/color.cgi 或 url=/color.cgi?color=green


 if (strcasecmp(method, "GET") == 0)
 {
  query_string = url;
  while ((*query_string != '?') && (*query_string != ''))
   query_string++;
  if (*query_string == '?')
  {
   cgi = 1;
   *query_string = '';
   query_string++;
  }
 }


 sprintf(path, "htdocs%s", url);
 if (path[strlen(path) - 1] == '/')
  strcat(path, "index.html");
 
//如果文件存在,且有执行全新,cgi=1 ,index.html无执行权限,只有cgi文件有执行权限
if (stat(path, &st) == -1) 
  not_found(client);
 else
 {
  if ((st.st_mode & S_IXUSR) ||(st.st_mode & S_IXGRP) ||(st.st_mode & S_IXOTH) )
   cgi = 1;
 }
 
if (!cgi)
   serve_file(client, path);  //浏览器输ip:port,就读取index.html
  else
   execute_cgi(client, path, method, query_string); //post 或带参数的get走此处
 }

 close(client);
}

3、serve_file

把index.html文件内容发送给客户端。

void serve_file(int client, const char *filename)
{
 FILE *resource = NULL;
 int numchars = 1;
 char buf[1024];

//把缓冲区其他内容读出来,清空缓冲区,因为http除了第一行还要发好多行数据
 buf[0] = 'A'; buf[1] = '';
 while ((numchars > 0) && strcmp("n", buf))  
  numchars = get_line(client, buf, sizeof(buf));

//打开htdocs/index.html文件
 resource = fopen(filename, "r");


 headers(client, filename);

//发送文件内容
 cat(client, resource);

 fclose(resource);
}

4、execute_cgi

执行cgi文件走此处,包括post 和带参数的get ,

带参数的get如:127.0.0.1:8899/color.cgi?color=red

创建管道,创建子进程,父进程把cgi文件路径发给子进程,子进程执行完把结果发给父进程。

父进程再发给浏览器。

void execute_cgi(int client, const char *path,
                 const char *method, const char *query_string)
{



 if (strcasecmp(method, "GET") == 0)
  while ((numchars > 0) && strcmp("n", buf))  
   numchars = get_line(client, buf, sizeof(buf));
 

else    
 {
  numchars = get_line(client, buf, sizeof(buf));
  while ((numchars > 0) && strcmp("n", buf))
  {
   buf[15] = '';
   if (strcasecmp(buf, "Content-Length:") == 0)
    content_length = atoi(&(buf[16]));
   numchars = get_line(client, buf, sizeof(buf));
  }
  if (content_length == -1) {
   bad_request(client);
   return;
  }
 }


//先发送http头
 sprintf(buf, "HTTP/1.0 200 OKrn");
 send(client, buf, strlen(buf), 0);

//创建两个管道,即后续父子进程都拥有这俩管道
pipe(cgi_output);
pipe(cgi_input); 

 if ( (pid = fork()) < 0 ) {
  cannot_execute(client);
  return;
 }

//子进程
 if (pid == 0)  
 {
 
  //重定向管道文件描述符,并关闭不需要的管道端口
  dup2(cgi_output[1], 1);
  dup2(cgi_input[0], 0);
  close(cgi_output[0]);
  close(cgi_input[1]);

  //设置环境变量 执行cgi文件需要
  sprintf(meth_env, "REQUEST_METHOD=%s", method);
  putenv(meth_env);
  if (strcasecmp(method, "GET") == 0) 
 {
   sprintf(query_env, "QUERY_STRING=%s", query_string);
   putenv(query_env);
  }
  else   
  {   
   sprintf(length_env, "CONTENT_LENGTH=%d", content_length);
   putenv(length_env);
  }

  
  execl(path, path, NULL);
  exit(0);
 } 

//父进程
else    
{
  close(cgi_output[1]);
  close(cgi_input[0]);


  if (strcasecmp(method, "POST") == 0)
   for (i = 0; i < content_length; i++) {
    recv(client, &c, 1, 0);
    write(cgi_input[1], &c, 1);
   }


  while (read(cgi_output[0], &c, 1) > 0)
   send(client, &c, 1, 0);

  close(cgi_output[0]);
  close(cgi_input[1]);
  waitpid(pid, &status, 0);
 }
}

======================================================================

效果:

1、GET,浏览器输入127.0.0.1:36393

 2、POST,

输入内容如:red,点击Submit Query,此时请求url变为:127.0.0.1:36393/color.cgi

3、带参数get

浏览器直接输入:127.0.0.1:36393/color.cgi?color=green

 

 

 

 

 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/348156.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号