WhyCan Forum(哇酷开发者社区)

我们习惯了"有问题百度一下", 感谢为中文互联网持续输出优质内容的各位老铁们。 QQ: 516333132, 微信: whycan_cn (哇酷网/挖坑网/填坑网) service@whycan.cn

您尚未登录。

#1 2019-09-21 10:44:30

歌以咏志
会员
注册时间: 2019-09-21
累计积分: 151

潜水很久了, 今天注册发个帖子说说 buildroot busybox 里面 添加 http 网页服务器

1. 修改 package/busybox/busybox.config

关于http部分改成这样:

CONFIG_HTTPD=y
# CONFIG_FEATURE_HTTPD_RANGES is not set
# CONFIG_FEATURE_HTTPD_SETUID is not set
CONFIG_FEATURE_HTTPD_BASIC_AUTH=y
CONFIG_FEATURE_HTTPD_AUTH_MD5=y
CONFIG_FEATURE_HTTPD_CGI=y
CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR=y
CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV=y
CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y
CONFIG_FEATURE_HTTPD_ERROR_PAGES=y

2. 在 buildroot 目录下执行 make, output/build/busybox-1.27.2/.config 会被上面的 busybox.config 替换掉,
    也会在 output/target/usr/bin 生成 http 软连接文件,指向的是  output/target/bin/busybox

3. 新建 output/target/etc/httpd2.conf 配置文件(也可以不要此文件),
    参考: https://openwrt.org/docs/guide-user/services/webserver/http.httpd

4. 建立 output/target/init.d/S80httpd 文件:

/usr/sbin/httpd -p 80 -h /www -c /etc/httpd.conf

5. 新建  output/target/www/test.html 文件

<html>
        <head>
                <meta charset="utf-8">
                <title> hello whycan.cn</title>
        </head>

        <body>
                <b>I am come from whycan.cn.  </b>

                <p />

                <font color="red">Run in 君正Ingenic X1000</font>
        </body>

</html>

6. 新建 output/target/www/cgi-bin/ 文件夹
    再建文件: output/target/www/cgi-bin/test 脚本文件:

#!/bin/sh
echo "Content-type: text/html"
echo ""
echo ""
echo "<html><head><meta charset=\"utf-8\"><title>Sample CGI Output @ whycan.cn</title></head><body>"
echo "<font color=\"red\">测试测试 红色</font>"
echo "<p />"
echo "<font color=\"blue\">测试测试 蓝色</font>"
echo "<p />"

echo "</body></html>"

7. 打包,烧录,运行.

最近编辑记录 歌以咏志 (2019-09-21 11:26:43)

离线

#2 2019-09-21 11:28:39

歌以咏志
会员
注册时间: 2019-09-21
累计积分: 151

Re: 潜水很久了, 今天注册发个帖子说说 buildroot busybox 里面 添加 http 网页服务器

2019-09-21_105746.png

上图是test.html的显示页面

2019-09-21_112626.png
上图是test cgi脚本的显示页面

第一次学习html标签,做得丑,请大家多多包涵。

离线

#3 2019-09-21 13:02:40

jiangming1399
会员
注册时间: 2018-06-14
累计积分: 110

Re: 潜水很久了, 今天注册发个帖子说说 buildroot busybox 里面 添加 http 网页服务器

这个选项可以在busybox的menuconfig里面开吧,手动改配置感觉有点不方便

离线

#4 2019-09-21 14:30:23

歌以咏志
会员
注册时间: 2019-09-21
累计积分: 151

Re: 潜水很久了, 今天注册发个帖子说说 buildroot busybox 里面 添加 http 网页服务器

jiangming1399 说:

这个选项可以在busybox的menuconfig里面开吧,手动改配置感觉有点不方便

还真有这个命令: make busybox-menuconfig

用上面的命令就可以配置了。

离线

#5 2019-09-21 17:42:27

歌以咏志
会员
注册时间: 2019-09-21
累计积分: 151

Re: 潜水很久了, 今天注册发个帖子说说 buildroot busybox 里面 添加 http 网页服务器

离线

#6 2019-09-24 16:15:13

歌以咏志
会员
注册时间: 2019-09-21
累计积分: 151

Re: 潜水很久了, 今天注册发个帖子说说 buildroot busybox 里面 添加 http 网页服务器

发现用上面的比较麻烦, 找到 libfcgi 库, 在 buildroot 可以直接开启: BR2_PACKAGE_LIBFCGI=y

先进入目录 buildroot-2017.08.1/, 执行 make

再进入目录 buildroot-2017.08.1/output/build/libfcgi-2.4.0/examples, 执行 make

会生成好几个 cgi 程序, 拷贝到 rootfs 的 /www/cgi-bin/ 目录.

浏览器打开: http://192.168.1.33/cgi-bin/echo

2019-09-24_161345.png

一切 OK, 一个基本的 cgi 程序就跑起来了!!!

离线

#7 2019-09-24 17:36:41

歌以咏志
会员
注册时间: 2019-09-21
累计积分: 151

Re: 潜水很久了, 今天注册发个帖子说说 buildroot busybox 里面 添加 http 网页服务器

也可以用curl 命令测试这个 cgi 服务器:

curl -F "image=@echo.c"   http://192.168.1.33/cgi-bin/echo

curl -F "image=@echo.c"   -d "eeeeeeeee=ffffffff" http://192.168.1.33/cgi-bin/echo?aaaa=bbb\&ccc=dddddddddddddd

curl -d "eeeeeeeee=ffffffff&gggggg=hhhhh" http://192.168.1.33/cgi-bin/echo

离线

#8 2019-09-24 17:39:27

歌以咏志
会员
注册时间: 2019-09-21
累计积分: 151

Re: 潜水很久了, 今天注册发个帖子说说 buildroot busybox 里面 添加 http 网页服务器

#include <fcgi_stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <stdio.h>

int main()  
{  
    while(FCGI_Accept() >= 0)  
    {  
        char* strGetData = NULL;  
        int iContentLength = 0;  
        if (getenv("QUERY_STRING"))  
        {  
            strGetData = getenv("QUERY_STRING");  
        }  
        if (getenv("CONTENT_LENGTH"))  
        {  
            iContentLength = atoi(getenv("CONTENT_LENGTH"));  
        }  
        char* data = (char*)malloc(iContentLength + 1);  
        memset(data, 0, iContentLength + 1);  
        FCGI_fread(data, 1, iContentLength, FCGI_stdin);  
        FCGI_printf("Content-type:text/html\r\n\r\n");  
        FCGI_printf(data);
		
		FCGI_printf("iContentLength = %d\n", iContentLength);
#if 0		
		FILE* fp = fopen("tmp.txt", "w");
		fwrite(data, 1, iContentLength, fp);
		fclose(fp);
#endif
    }  
    return 0;  
}  

编译: /opt/buildroot-2017.08.1/output/host/bin/arm-linux-gcc  -o cgi_test cgi_test.c -lfcgi

curl测试(上传一个echo.c文件): curl -F "image=@echo.c"   http://192.168.1.33/cgi-bin/echo

离线

#9 2019-09-24 17:43:35

晕哥
管理员
注册时间: 2017-09-06
累计积分: 9,186

Re: 潜水很久了, 今天注册发个帖子说说 buildroot busybox 里面 添加 http 网页服务器

感谢分享, 一个微型嵌入式 cgi web 服务器诞生了, 楼主能不能再研究一下,把 lua 整合进去, 用lua脚本写网页服务器。

离线

#10 2019-10-15 17:51:51

歌以咏志
会员
注册时间: 2019-09-21
累计积分: 151

Re: 潜水很久了, 今天注册发个帖子说说 buildroot busybox 里面 添加 http 网页服务器

离线

#11 2019-10-16 15:09:33

歌以咏志
会员
注册时间: 2019-09-21
累计积分: 151

Re: 潜水很久了, 今天注册发个帖子说说 buildroot busybox 里面 添加 http 网页服务器

晕哥 说:

感谢分享, 一个微型嵌入式 cgi web 服务器诞生了, 楼主能不能再研究一下,把 lua 整合进去, 用lua脚本写网页服务器。

搞定cgi之后考虑整合一个 lua 进去,毕竟脚本编程方便太多。

离线

#12 2019-10-16 15:14:40

晕哥
管理员
注册时间: 2017-09-06
累计积分: 9,186

Re: 潜水很久了, 今天注册发个帖子说说 buildroot busybox 里面 添加 http 网页服务器

豆瓣这个博客不错: https://www.douban.com/note/541320499/

如何用c实现fastcgi文件上传 steven 2016-02-24 18:18:37

在现在web端中,fastcgi算是比较古老的方式,很多公司依然在用fastcgi可能是出于历史架构的原因,Fastcgi确实在后端能和C完美地结合。
用c + fastcgi来处理文件上传比较繁琐,需要处理MIME, 文件内容boundary问题等一些问题。
所以要实现c + fastcgi 文件上传只需要掌握以下2点就可以了.

1. 从 stdin 读上传的文件内容.
2. 处理第1点中文件boundary问题.

第1点比较容易,fread直接搞定
第2点关于boundary问题,根据 rfc1341 (https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html)
描述,所有从web端POST过来的数据都是用 boudnary分隔的, 比如上传文件内容:

------WebKitFormBoundarycKuywINaoWuIidOR\r\n
Content-Disposition: form-data; name="file"; filename="test.png"\r\n
Content-Type: application/x-gzip\r\n
\r\n
\r\b
文件内容
------WebKitFormBoundarycKuywINaoWuIidOR\r\n

实现代码:
https://github.com/pengxiaojun/Fastcgi_utils/blob/master/upload.cpp

#include "upload.h"
#include "logger.h"
#include <fcgi_stdio.h>

#define DEAL_BUF_LEN 1024
#define SIGN_CODE_LEN 100
#define FILE_NAME_LEN 64

enum
{
    STATE_START,
    STATE_GET_SIGN_CODE,
    STATE_GET_FILE_NAME,
    STATE_GET_FILE_START,
    STATE_GET_FILE_CONTENT,
    STATE_CHECK_END,
    STATE_END
};

int upload_file_save_as(const char *saveas)
{
    FILE *fp; 
    int getState = STATE_START;
    int contentLength; /* uploaded file content length */
    int nowReadLen;
    int signCodeLen;
    int tmpLen;
    char *nowReadP;
    char *nowWriteP;
    char dealBuf[DEAL_BUF_LEN];
    char signCode[SIGN_CODE_LEN]; /* http boundary */
    char tmpSignCode[SIGN_CODE_LEN];
    char fileName[FILE_NAME_LEN];
    memset(dealBuf, 0, DEAL_BUF_LEN);
    memset(signCode, 0, SIGN_CODE_LEN);
    memset(fileName, 0, FILE_NAME_LEN);
    nowReadLen = 0;
    if((char *)getenv("CONTENT_LENGTH")!=NULL)
    {
        contentLength = atoi((char *)getenv("CONTENT_LENGTH"));
    }
    else
    {
        return EUPLOAD_NO_DATA;
    }

    while(contentLength > 0)
    {
        if(contentLength >= DEAL_BUF_LEN)
        {
            nowReadLen = DEAL_BUF_LEN;
        }
        else
        {
            nowReadLen = contentLength;
        }
        contentLength -= nowReadLen;
        if(fread(dealBuf, sizeof(char), nowReadLen, FCGI_stdin) != (size_t)nowReadLen)
        {
            log_error("read error %d", nowReadLen);
            return EUPLOAD_READ;
        }
        nowReadP = dealBuf;
        while(nowReadLen > 0)
        {
            switch (getState)
            {
                case STATE_START:
                    nowWriteP = signCode;
                    getState = STATE_GET_SIGN_CODE;
                case STATE_GET_SIGN_CODE:
                    if(strncmp(nowReadP, "\r\n", 2) == 0)
                    {
                        signCodeLen = nowWriteP - signCode;
                        nowReadP++;
                        nowReadLen--;
                        *nowWriteP = 0;
                        getState = STATE_GET_FILE_NAME;
                    }
                    else
                    {
                        *nowWriteP = *nowReadP;
                        nowWriteP++;
                    }
                    break;
                case STATE_GET_FILE_NAME:
                    if(strncmp(nowReadP,"filename=",strlen("filename=")) == 0)
                    {
                        nowReadP += strlen("filename=");
                        nowReadLen -= strlen("filename=");
                        nowWriteP = fileName + strlen(saveas);
                        while(*nowReadP != '\r')
                        {
                            if(*nowReadP == '\\' || *nowReadP == '/')
                            {
                                nowWriteP = fileName + strlen(saveas);
                            }
                            else if(*nowReadP != '\"')
                            {
                                *nowWriteP = *nowReadP;
                                nowWriteP++;
                            }
                            nowReadP++;
                            nowReadLen--;
                        }
                        *nowWriteP = 0;
                        nowReadP++;
                        nowReadLen--;
                        getState = STATE_GET_FILE_START;
                        memcpy(fileName, saveas, strlen(saveas));
                        if((fp = fopen(fileName, "w")) == NULL)
                        {
                            log_error("open file %s error %d", fileName, errno);
                            return EUPLOAD_WRITE;
                        }
                    }
                    break;
                case STATE_GET_FILE_START:
                    if(strncmp(nowReadP, "\r\n\r\n", 4) == 0)
                    {
                        nowReadP += 3;
                        nowReadLen -= 3;
                        getState = STATE_GET_FILE_CONTENT;
                    }
                    break;
                case STATE_GET_FILE_CONTENT:
                    if(*nowReadP != '\r')
                    {
                        fputc(*nowReadP,fp);
                    }
                    else
                    {
                        if(nowReadLen >= (signCodeLen + 2)) //\r\n=2
                        {
                            if(strncmp(nowReadP + 2, signCode, signCodeLen) == 0)
                            {
                                getState = STATE_END;
                                nowReadLen = 1;
                            }
                            else
                            {
                                fputc(*nowReadP,fp);
                            }
                        }
                        else
                        {
                            getState = STATE_CHECK_END;
                            nowWriteP = tmpSignCode;
                            *nowWriteP = *nowReadP;
                            nowWriteP++;
                            tmpLen = 1;
                        }
                    }
                    break;
                case STATE_CHECK_END:
                    if(*nowReadP != '\r')
                    {
                        if(tmpLen < signCodeLen + 2)
                        {
                            *nowWriteP = *nowReadP;
                            nowWriteP++;
                            tmpLen++;
                            if(tmpLen == signCodeLen + 2)
                            {
                                *nowWriteP = 0;
                                if((tmpSignCode[1] == '\n')&&(strncmp(tmpSignCode + 2,signCode,signCodeLen) == 0))
                                {
                                    getState = STATE_END;
                                    nowReadLen = 1;
                                }
                                else
                                {
                                    //fprintf(fp,tmpSignCode);
                                    fwrite(tmpSignCode,sizeof(char),tmpLen,fp);
                                    getState = STATE_GET_FILE_CONTENT;
                                }
                            }
                        }
                    }
                    else
                    {
                        *nowWriteP = 0;
                        //fprintf(fp,tmpSignCode);
                        fwrite(tmpSignCode,sizeof(char),tmpLen,fp);
                        nowWriteP = tmpSignCode;
                        *nowWriteP = *nowReadP;
                        nowWriteP++;
                        tmpLen = 1;
                    }
                    break;
                case STATE_END:
                    nowReadLen = 1;
                    break;
                default:break;
            }
            nowReadLen--;
            nowReadP++;
        }
    }
    if(fp != NULL)
    {
        fclose(fp);
    }
    return EUPLOAD_SUCCESS;
}

离线

#13 2019-10-26 17:31:08

歌以咏志
会员
注册时间: 2019-09-21
累计积分: 151

Re: 潜水很久了, 今天注册发个帖子说说 buildroot busybox 里面 添加 http 网页服务器

用楼上@晕哥的代码保存文件OK了, 接下来要用 libcurl 做一个客户端.

代码在此: https://curl.haxx.se/libcurl/c/postit2.html

/***************************************************************************
 *                                  _   _ ____  _
 *  Project                     ___| | | |  _ \| |
 *                             / __| | | | |_) | |
 *                            | (__| |_| |  _ <| |___
 *                             \___|\___/|_| \_\_____|
 *
 * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution. The terms
 * are also available at https://curl.haxx.se/docs/copyright.html.
 *
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
 * copies of the Software, and permit persons to whom the Software is
 * furnished to do so, under the terms of the COPYING file.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ***************************************************************************/
/* <DESC>
 * HTTP Multipart formpost with file upload and two additional parts.
 * </DESC>
 */
/* Example code that uploads a file name 'foo' to a remote script that accepts
 * "HTML form based" (as described in RFC1738) uploads using HTTP POST.
 *
 * The imaginary form we'll fill in looks like:
 *
 * <form method="post" enctype="multipart/form-data" action="examplepost.cgi">
 * Enter file: <input type="file" name="sendfile" size="40">
 * Enter file name: <input type="text" name="filename" size="30">
 * <input type="submit" value="send" name="submit">
 * </form>
 *
 * This exact source code has not been verified to work.
 */

#include <stdio.h>
#include <string.h>

#include <curl/curl.h>

int main(int argc, char *argv[])
{
  CURL *curl;
  CURLcode res;

  curl_mime *form = NULL;
  curl_mimepart *field = NULL;
  struct curl_slist *headerlist = NULL;
  static const char buf[] = "Expect:";

  curl_global_init(CURL_GLOBAL_ALL);

  curl = curl_easy_init();
  if(curl) {
    /* Create the form */
    form = curl_mime_init(curl);

    /* Fill in the file upload field */
    field = curl_mime_addpart(form);
    curl_mime_name(field, "sendfile");
    curl_mime_filedata(field, "postit2.c");

    /* Fill in the filename field */
    field = curl_mime_addpart(form);
    curl_mime_name(field, "filename");
    curl_mime_data(field, "postit2.c", CURL_ZERO_TERMINATED);

    /* Fill in the submit field too, even if this is rarely needed */
    field = curl_mime_addpart(form);
    curl_mime_name(field, "submit");
    curl_mime_data(field, "send", CURL_ZERO_TERMINATED);

    /* initialize custom header list (stating that Expect: 100-continue is not
       wanted */
    headerlist = curl_slist_append(headerlist, buf);
    /* what URL that receives this POST */
    curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/examplepost.cgi");
    if((argc == 2) && (!strcmp(argv[1], "noexpectheader")))
      /* only disable 100-continue header if explicitly requested */
      curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
    curl_easy_setopt(curl, CURLOPT_MIMEPOST, form);

    /* Perform the request, res will get the return code */
    res = curl_easy_perform(curl);
    /* Check for errors */
    if(res != CURLE_OK)
      fprintf(stderr, "curl_easy_perform() failed: %s\n",
              curl_easy_strerror(res));

    /* always cleanup */
    curl_easy_cleanup(curl);

    /* then cleanup the form */
    curl_mime_free(form);
    /* free slist */
    curl_slist_free_all(headerlist);
  }
  return 0;
}

安装软件包: sudo apt-get install libcurl4-openssl-dev -y

编译: gcc -o test postit2.c -lcurl -I/usr/include

然后执行: ./test

就可以通过 http 协议上传文件到 嵌入式设备了, 就像浏览器一样方便。

离线

#14 2019-10-26 18:01:05

kekemuyu
会员
注册时间: 2018-12-13
累计积分: 492

Re: 潜水很久了, 今天注册发个帖子说说 buildroot busybox 里面 添加 http 网页服务器

文件服务器用go搭建不会超过10行代码,有时间在f1c100s试一下

离线

#15 2019-10-28 08:33:23

歌以咏志
会员
注册时间: 2019-09-21
累计积分: 151

Re: 潜水很久了, 今天注册发个帖子说说 buildroot busybox 里面 添加 http 网页服务器

kekemuyu 说:

文件服务器用go搭建不会超过10行代码,有时间在f1c100s试一下

go 编译出来的体积有点大吧, 还有我这个是要用浏览器交互一些数据,不只是文件上传。

请教 go 如何实现一个文件服务器呢?愿闻其详。

离线

#16 2019-10-28 08:42:13

kekemuyu
会员
注册时间: 2018-12-13
累计积分: 492

Re: 潜水很久了, 今天注册发个帖子说说 buildroot busybox 里面 添加 http 网页服务器

歌以咏志 说:
kekemuyu 说:

文件服务器用go搭建不会超过10行代码,有时间在f1c100s试一下

go 编译出来的体积有点大吧, 还有我这个是要用浏览器交互一些数据,不只是文件上传。

请教 go 如何实现一个文件服务器呢?愿闻其详。

以下是实现文件浏览和下载功能的代码,上传的话可能需更多代码,需要做个页面和添加相应服务。总之。只要是涉及到网络的需求,go都能非常优雅的完成。

package main

import (
    "fmt"
    "net/http"
)


func main() {
     mux := http.NewServeMux()
    mux.Handle("/files/", http.StripPrefix("/files/", http.FileServer(http.Dir("../files"))))
    if err := http.ListenAndServe(":3000", mux); err != nil {
        log.Fatal(err)
    }
}

最近编辑记录 kekemuyu (2019-10-28 08:49:25)

离线

#17 2019-10-28 08:44:43

我思故我在
会员
注册时间: 2019-09-03
累计积分: 159

Re: 潜水很久了, 今天注册发个帖子说说 buildroot busybox 里面 添加 http 网页服务器

go的库真全。

离线

页脚

工信部备案:粤ICP备20025096号-1 Powered by FluxBB