uIP的web服务

  • 程序使用了作者写的webserver的demo程序,添加了相应的注释。说一下整个web服务器实现的流程。
  • uip的web服务的回调函数是/webserver/httpd.c中httpd_appcall(void)。接着进入了handle_connection函数。
static void
handle_connection(struct httpd_state *s)
{
  handle_input(s);
  if(s->state == STATE_OUTPUT) {
    handle_output(s);
  }
}
  • handle_input(s)实际上是指PT_THREAD(handle_input(struct httpd_state *s)),其他还有一些类似的。这是一个轻量级的模拟的线程环境protothreads.详情请查看。handle_input(s)是解析浏览器向服务器请求的内容。输入ip地址的话,会输出http_index_html。如请求其他页面,会把/后面的字符作为文件名(等待handle_output(s)输出)。从这个函数可以看出uip只支持‘get’方法,get格式是:/(文件名)?(第一个变量名)=(填的值)&(第二个变量名)。。。如:192.168.0.2/file.html?num1=2&num2=2。接下来就可以按照这个格式得到浏览器往服务器发送了什么数据。有关html语言相关知识请查看。ps.如果要使用‘post’方法就需要按照格式改写这个函数了。
static
PT_THREAD(handle_input(struct httpd_state *s))
{
  PSOCK_BEGIN(&s->sin);

  PSOCK_READTO(&s->sin, ISO_space);   //等待空格符

  
  if(strncmp(s->inputbuf, http_get, 4) != 0) {  //不是GET就退出结束
    PSOCK_CLOSE_EXIT(&s->sin);
  }
  PSOCK_READTO(&s->sin, ISO_space); //等待空格符

  if(s->inputbuf[0] != ISO_slash) {//不是'/'就退出结束
    PSOCK_CLOSE_EXIT(&s->sin);
  }

  if(s->inputbuf[1] == ISO_space) { //如果是空格符就把index_html作为输出页
    strncpy(s->filename, http_index_html, sizeof(s->filename));
  } else {
    s->inputbuf[PSOCK_DATALEN(&s->sin) - 1] = 0;  //否则将后面的字符作为输出页
    strncpy(s->filename, &s->inputbuf[0], sizeof(s->filename));
  }
  //可以处理接收到的数据了

  /*  httpd_log_file(uip_conn->ripaddr, s->filename);*/
  
  s->state = STATE_OUTPUT;  //设置状态位可以输出

  while(1) {
    PSOCK_READTO(&s->sin, ISO_nl);

    if(strncmp(s->inputbuf, http_referer, 8) == 0) {
      s->inputbuf[PSOCK_DATALEN(&s->sin) - 2] = 0;
      /*      httpd_log(&s->inputbuf[9]);*/
    }
  }
  
  PSOCK_END(&s->sin);
}
  • s->state = STATE_OUTPUT之后就到handle_output(s)函数,用做输出文件的。如果之前得到请求的网页名在已存的网页中找不到,就会输出404(这个真的大名鼎鼎)。接着会判断一下请求的是否是动态网页(shtml为后缀,当然也可以自己定义cgi、asp等等),是的话就转去handle_script(s)。不是就直接输出网页。ps.网页都是先写好的,然后按字符转换成二进制码,放到httpd_fsdata.c中,注意该文件最下面的结构体是用来遍历全部网页文件的,另外添加文件需要更改这里。还有就是静态网页有个http头需要添加,原demo中有。
static
PT_THREAD(handle_output(struct httpd_state *s))
{
  char *ptr;
  
  PT_BEGIN(&s->outputpt);

  if(!httpd_fs_open(s->filename, &s->file)) {  //没有找到对应的页面
    httpd_fs_open(http_404_html, &s->file);
    strcpy(s->filename, http_404_html);
    PT_WAIT_THREAD(&s->outputpt,
             send_headers(s,
             http_header_404));
    PT_WAIT_THREAD(&s->outputpt,  //等待线程完成(404错误头和内容)打印404页面
             send_file(s));
  } else {
    PT_WAIT_THREAD(&s->outputpt,
             send_headers(s,
             http_header_200));    //等待输出200成功
    ptr = strchr(s->filename, ISO_period);
    if(ptr != NULL && strncmp(ptr, http_shtml, 6) == 0) {  //判断为shtml
      PT_INIT(&s->scriptpt);
      PT_WAIT_THREAD(&s->outputpt, handle_script(s));    //等待CGI处理动态页面
    } else {
      PT_WAIT_THREAD(&s->outputpt,        //不是的话输出页面
               send_file(s));
    }
  }
  PSOCK_CLOSE(&s->sout);
  PT_END(&s->outputpt);
}
  • 接下来就是动态网页的处理了,handle_script(s)是这样处理的:遍历网页的字符,碰到%!:(空格)就输出后面字符对应的文件。碰到%!(空格)就执行后面字符对应的函数(同时还可以加一个外部参数)如:%! analog num1.在这些参数完了之后都要加个回车符。动态网页与静态网页在设计的时候区别就是:后缀与%!:和%!的标识符。
static
PT_THREAD(handle_script(struct httpd_state *s))
{
  char *ptr;
  
  PT_BEGIN(&s->scriptpt);


  while(s->file.len > 0) {

    /* Check if we should start executing a script. */
    if(*s->file.data == ISO_percent &&
       *(s->file.data + 1) == ISO_bang) {
      s->scriptptr = s->file.data + 3;
      s->scriptlen = s->file.len - 3;
      if(*(s->scriptptr - 1) == ISO_colon) {  //如果是%!: 打开后面对应的文件并输出
     httpd_fs_open(s->scriptptr + 1, &s->file);
     PT_WAIT_THREAD(&s->scriptpt, send_file(s));
      } else {
     PT_WAIT_THREAD(&s->scriptpt,
                 httpd_cgi(s->scriptptr)(s, s->scriptptr)); //如果是%! 转去后面对应的程序
      }                                                                   // s->scriptptr为%!:后面的字符
      next_scriptstate(s);
      
      /* The script is over, so we reset the pointers and continue
     sending the rest of the file. */
      s->file.data = s->scriptptr;
      s->file.len = s->scriptlen;
    } else {
      /* See if we find the start of script marker in the block of HTML
     to be sent. */

      if(s->file.len > uip_mss()) {
     s->len = uip_mss();
      } else {
     s->len = s->file.len;
      }

      if(*s->file.data == ISO_percent) {
     ptr = strchr(s->file.data + 1, ISO_percent);
      } else {
     ptr = strchr(s->file.data, ISO_percent);
      }
      if(ptr != NULL &&
     ptr != s->file.data) {
     s->len = (int)(ptr - s->file.data);
     if(s->len >= uip_mss()) {
       s->len = uip_mss();
     }
      }
      PT_WAIT_THREAD(&s->scriptpt, send_part_of_file(s));   //等待打印没有变量部分的页面
      s->file.data += s->len;
      s->file.len -= s->len;
      
    }
  }
  
  PT_END(&s->scriptpt);
}
  • 基本差不多了,然后就是到底怎么处理然后把结果动态输出到网页上的了,这些在http-cgi.c文件里。这个函数也要先定义好,如下:
HTTPD_CGI_CALL(file, "file-stats", file_stats);               //指针,Web数据里的名字,回调函数的名字
HTTPD_CGI_CALL(tcp, "tcp-connections", tcp_stats);
HTTPD_CGI_CALL(net, "net-stats", net_stats);
static const struct httpd_cgi_call *calls[] = { &file, &tcp, &net, NULL };
  • 然后遍历查找函数名,看是否与请求的一致。接着执行PSOCK_GENERATOR_SEND(&s->sout, #,*)#是指向下个函数的指针。*是给下个函数的参数,如果在设计网页的时候预留个外部参数就可以让*=strchr(ptr, ' ') + 1(空格后面,所以在设计的时候要注意)。处理完了之后就改把动态结果输出了。snprintf((char *)uip_appdata, UIP_APPDATA_SIZE, "%8.3f", Analog_Count(f));用的是这个函数,其中analo_count是我自定义的函数。OK!基本上简单的web动态网页已经没有问题了,还有一些不明白的地方可以看工程里的注释。点击下载如果使用HG 只要clone https://zouw96@bitbucket.org/zouw96/uip_http。有关移植等问题见上一篇文章
static unsigned short
generate_file_stats(void *arg)
{
  char *f = (char *)arg;                                     //arg 为后面的外部命令参数
  return snprintf((char *)uip_appdata, UIP_APPDATA_SIZE, "%8.3f", Analog_Count(f));
}
/*---------------------------------------------------------------------------*/
static
PT_THREAD(file_stats(struct httpd_state *s, char *ptr))
{
  PSOCK_BEGIN(&s->sout);

  PSOCK_GENERATOR_SEND(&s->sout, generate_file_stats, strchr(ptr, ' ') + 1);  //指针指向空格后面的外部命令参数arg
  
  PSOCK_END(&s->sout);
}
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 18
    评论
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值