目录

一、CGI(通用网关接口)是外部扩展应用程序与 Web 服务器交互的一个标准接口。... 1

二、web服务器配置... 3

三、Cgi编程(根据CGI标准,编写Web服务器运行时的外部扩展应用程序)可以对客户端浏览器输入的数据经web服务器和业务逻辑程序进程进行交互操作。... 3

1、通信架构... 4

2、应用场景数据处理流程举例:... 4

3、第一个CGI脚本... 5

4、Cgi获取本次请求中存在的所有环境变量值编程... 6

5、GET 与POST方法... 8

6、在CGI中使用Cookies编程... 8

1)浏览器触发设置Cookies. 9

2)浏览器触发获取Cookies. 10

7、cgi与html或js相互调用编程... 11

8、完成html 和 CGI 和 用户进程 的交互,实现数据上报到html端,在主页面index.html里使用iframe 嵌入子页面  <form action= "/cgi-bin/mycgi.cgi"  可以观察到只有嵌入的子页面部分进行了页面的绘制(数据更新)  12

9、CGI实现文件的上传和下载(界面触发)... 16

四、环境变量列表... 19

五、注意的问题... 20

六、参考... 20

 

一、CGI(通用网关接口)是外部扩展应用程序与 Web 服务器交互的一个标准接口。

 

 CGI脚本简单地讲是个运行在Web服务器上的程序, 由浏览器的输入触发. 这个脚本通常象服务器和系统中其他程序如数据库的桥梁。

 

CGI 脚本难道不是一个真正的脚本?按照你的服务器的支持, 他们可能是一个编译好的程序或者批命令文件或者其他可执行的东西. 为了简单起见,我们统称他们为脚本scripts. 

 

CGI脚本是怎样工作的?

  CGI脚本由服务器调用, 基于浏览器的数据输入. 其工作原理如下:

  1. 一个URL指向一个CGI脚本. 一个CGI脚本的URL能如普通的URL一样出现,区别于.htm/.html静态UR,CGI的URL是动态URL。如http://xxxx.com/cgiurl
  2. 服务器CGI接收浏览器的请求, 按照那个URL指向对应的脚本文件(注意文件的位置和扩展名),执行CGI脚本.
  3. CGI脚本执行基于输入数据的操作,包括查询数据库、计算数值或调用系统中其他程序.
  4. CGI脚本产生某种Web服务器能理解的输出结果.
  5. 服务器接收来自脚本的输出并且把它传回浏览器,让用户了解处理结果。

 

公共网关接口(Common Gateway InterfaceCGIWeb 服务器运行时外部程序的规范,按CGI 编写的程序可以扩展服务器功能。

CGI 应用程序能与浏览器进行交互,还可通过数据API与数据库服务器等外部数据源进行通信,从数据库服务器中获取数据。

格式化为HTML文档后,发送给浏览器,也可以将从浏览器获得的数据放到数据库中。

几乎所有服务器都支持CGI,可用任何语言编写CGI,包括流行的CC ++JavaVB Delphi

https://images2015.cnblogs.com/blog/928404/201609/928404-20160923132228434-672249865.png

https://i-blog.csdnimg.cn/blog_migrate/83a521b9b42b43ffa0095cde366e2fd4.png​​

CGI分为标准CGI和间接CGI两种。标准CGI使用命令行参数或环境变量表示服务器的详细请求,服务器与浏览器通信采用标准输入输出方式。间接CGI又称缓冲CGI,在CGI程序和CGI接口之间插入一个缓冲程序,缓冲程序与CGI接口间用标准输入输出进行通信 [1]  

公共网关接口 CGI 程序是存放在 HTTP 服务器上,为用户和HTTP服务器之外的其他应用程序提供互相交谈手段的软件

 HTTP 服务器与其他第三方应用程序之间,C遵循同一数据传输协议,G “连接件中件(Middle Ware) "I提供数据传输接口。

 

CGI可以为我们提供许多HTML无法做到的功能。比如 a.一个记数器 b.顾客信息表格的提交以及统计 c.搜索程序 d.WEB数据库,用Html是没有办法记住客户的任何信息的,就算用户愿意让你知道。用Html也是无法把信息记录到某一个特定文件里的。要把客户端的信息记录在服务器的硬盘上,就要用到CGI这是CGI最重要的作用,它补充了Html的不足。是的,仅仅是补充,不是替代。

使在网络服务器下运行外部分应用程序(或网关)成为可能。CGI-BIN 目录是存放CGI脚本的地方。这些脚本使Web服务器和浏览器能运行外部程序,而无需启动另一个程序。

它是运行在Web服务器上的一个程序,并由来自于浏览者的输入触发。CGI是在HTTP服务器下运行外部程序(或网关)的一个接口,它能让网络用户访问远程系统上的使用类型程序,就好像他们在实际使用那些远程计算机一样。

CGI能够让浏览者与服务器进行交互如果你曾经遇到过在网络上填表或者进行搜索,就很有可能就是用的CGI

尽管CGI易于使用,但是当大批人同时使用一个CGI应用程序是会反应较慢,网络服务器 速度也会受到很大 影响。CGI应用程序的优点是可以独立运行。

 

二、web服务器配置

Web服务组件boa的安装与配置

https://www.cnblogs.com/fxzq/p/12269736.html   

   在你着手写CGI程序之前,确保你的web服务器支持CGI程序并且配置成处理CGI程序。所有的能够被HTTP服务器执行的CGI程序都被存放在预先配 置好的目录下面,这个目录叫做CGI目录,并且按照约定命名为 /var/www/cgi-bin,并且约定CGI文件的后缀名为.cgi

 

CGI程序不是放在服务器上就能顺利运行,如果要想使其在服务器上顺利的运行并准确的处理用户的请求,则须对所使用的服务器进行必要的设置。

配置:根据所使用的服务器类型以及它的设置把CGI程序放在某一特定的目录中或使其带有特定的扩展名。

 

CGI目录除了可以跟网络文件放在同一目录中,也可以放在系统的其它目录中,但必须保证在你的系统中也具有同样的目录。在对服务器完成设置后,须重新启动服务器(除非HTTP服务器是用inetd启动的)。

 

三、Cgi编程(根据CGI标准,编写Web服务器运行时的外部扩展应用程序)可以对客户端浏览器输入的数据经web服务器和业务逻辑程序进程进行交互操作。

CGI可以用任何一种语言编写,只要这种语言具有标准输入、输出和环境变量对初学者来说,最好选用易于归档和能有效表示大量数据结构的语言。几乎所有服务器都支持CGI,可用任何语言编写CGI,包括流行的CC ++JavaVB Delphi 。由于C语言有较强的平台无关性,所以也是编写CGI程序的首选。例如UNIX环境中:PerlShellTcl· PHP

1、通信架构

https://images2015.cnblogs.com/blog/928404/201609/928404-20160923132228434-672249865.png

Web浏览器(get/post)<--http -àweb服务器ß--标准输入输出方式---àcgi编程(cgi接口编写Web服务器运行时的外部扩展应用程序)ß短连接传输-à业务逻辑服务程序。

 

CGI规范定义了Web服务器如何向扩展应用程序发送消息,在收到扩展应用程序的信息后又如何进行处理等内容。

 

根据Cgi规范编写的扩展应用程序处理的内容如下:

1)可以对客户端浏览器输入的数据,通过cgi接口从Web服务器获取到数据透传给业务逻辑程序进程放到数据库或写入文件或网络。

2)将业务逻辑程序进程发过来的数据 格式化为HTML文档后通过cgi接口经Web服务器发送给浏览器。

 

2应用场景数据处理流程举例:

Get数据:1)如果要获取的数据是从本地xml或json配置文件中获取,cgi编写的扩展应用程序处理不用处理.直接通过web服务器获取本地配置文件数据自动导入到表单。

               2)如果要获取的数据是业务逻辑程序服务运行信息,cgi编程的扩展应用程序建立短连接和业务逻辑程序服务通信获取信息,将数据 格式化为HTML文档后(可嵌入动态控制页面js)通过cgi接口经Web服务器发送给浏览器。

     Post数据:1)通过cgi编程的扩展应用程序建立短连接和业务逻辑程序服务通信,传给业务逻辑程序服务进程放到数据库或写入文件或网络或执行命令,将执行反馈结果数据 格式化为HTML文档后(可嵌入动态控制页面js)通过cgi接口经Web服务器发送给浏览器。

 

对于许多静态的HTML网页无法实现的功能,通过 CGI可以实现,比如表单的处理、对数据库的访问、搜索引擎、基于Web的数据库访问等等。使用CGI实现客户端与服务器的交互有以下几个标准步骤,具体步骤如下:

1Web 客户端的浏览器将URL的第一部分解码与Web服务器相连。

2Web 浏览器将URL的其余部分提供给服务器

3Web 服务器将URL转换成路径和文件名。

4Web 服务器发送 HTML 和别的组成请求页面的文件给客户。一旦页面内容传送完,

这个连接自动断开。

5)在客户端,HTML脚本提示用户做动作或输入。当用户响应后,客户请求Web服务器建立一个新的连接。

6Web 服务器把这些信息和别的进程变量传送给由HTMLURL的形式指定CGI程序。

7CGI 根据输入作出响应,把响应结果传送给 Web 服务器。

8Web 服务器把响应的数据传给客户,完成后关闭连接。 [2]

 

服务器端 CGI 程序接收信息有三种途径:环境变量、命令行和标准输入。其中环境变量是指 CGI 定义一组环境变量,通过环境变量可传递数据。服务器收到来自浏览器的数据,调用 CGI 脚本,CGI 脚本将收到的数据转换成环境变量并从中取出所需要的内容。

<form>标签的 METHOD 属性来决定具体使用哪一种方法。

“METHOD=GET”时,向 CGI 传递表单编码信息的是通过命令来进行的。表单编码信息大多数是通过环境变量 QUERY_STRING 来传递的。若“METHOD=POST”,表单信息通过标准输入来读取。

还有一种不使用表单就可以向 CGI 传送信息的方法,那就是把信息直接附在 URL 地址后面,信息和URL 之间用问号(?)来进行分隔。GET 方法是对数据的一个请求,被用于获得静态文档。GET 方法通过将发送请求信息附加在 URL 后面的参数。 GET 方法被使用时,CGI 程序将会从环境变量 QUERY_STRING获取数据。为了正确的响应客户端发来的请求,CGI 必须对 QUERY_STRING 中的字符串进行分析。当用户需要从服务器获取数据,但服务器上的数据不得改变时,应该用 GET 方法;但是如果请求中的字符串超过了一定长度,通常是 1024 字节,那么这时,只能用 POST 方法。POST 方法:浏览器将通过填写表单将数据传给服务器时一般采用POST 方法。在发送的数据超过 1024 字节时必须采用 POST 方法。 POST 方法被使用时,Web 服务器向CGI 程序的标准输入 STDIN 传送数据。环境变量 CONTENT_LENGTH 存放着发送的数据长度。CGI 程序必须检查环境变量 REQUEST_METHOD 以确定有没有采用了 POST 方法,并决定是否要读取标准输入STDIN [3]  

 

 

3第一个CGI脚本

#include <iostream>
using namespace std;
int main ()
{
    
   cout << "Content-type:text/html\r\n\r\n";
   cout << "<html>\n";
   cout << "<head>\n";
   cout << "<title>Hello World - First CGI Program</title>\n";
   cout << "</head>\n";
   cout << "<body>\n";
   cout << "<h2>Hello World! This is my first CGI program</h2>\n";
   cout << "</body>\n";
   cout << "</html>\n";
   
   return 0;
}

   编译上述代码并且将二进制可执行文件命名为cplusplus.cgi,保存路径为/var/www/cgi-bin目录下,运行chmod 755 cplusplus.cgi 命令使得该文件为可执行的。现在,如果你点击cplusplus.cgi然后就会产生如下输出:

Hello World! This is my first CGI program

 

第一行代码:Content- type:text/html\r\n\r\n,这行被发送回浏览器,指明浏览器显示的文本类型。

下表中包含其他一些重要的HTTP报文信息,这些信息在CGI编程中经常会用到。

Header

Description

Content-type:

A MIME string defining the format of the file being returned. Example is Content-type:text/html

Expires: Date

The date the information becomes invalid. This should be used by the browser to decide when a page needs to be refreshed. A valid date string should be in the format 01 Jan 1998 12:00:00 GMT.

Location: URL

The URL that should be returned instead of the URL requested. You can use this filed to redirect a request to any file.

Last-modified: Date

The date of last modification of the resource.

Content-length: N

The length, in bytes, of the data being returned. The browser uses this value to report the estimated download time for a file.

Set-Cookie: String

Set the cookie passed through the string

 

4、Cgi获取本次请求中存在的所有环境变量值编程

#include <iostream>

using namespace std;

 

const string ENV[ 24 ] = {                

        "COMSPEC", "DOCUMENT_ROOT", "GATEWAY_INTERFACE",  

        "HTTP_ACCEPT", "HTTP_ACCEPT_ENCODING",            

        "HTTP_ACCEPT_LANGUAGE", "HTTP_CONNECTION",        

        "HTTP_HOST", "HTTP_USER_AGENT", "PATH",           

        "QUERY_STRING", "REMOTE_ADDR", "REMOTE_PORT",     

        "REQUEST_METHOD", "REQUEST_URI", "SCRIPT_FILENAME",

        "SCRIPT_NAME", "SERVER_ADDR", "SERVER_ADMIN",     

        "SERVER_NAME","SERVER_PORT","SERVER_PROTOCOL",    

        "SERVER_SIGNATURE","SERVER_SOFTWARE" };  

 

int main ()

{

   

   cout << "Content-type:text/html\r\n\r\n";

   cout << "<html>\n";

   cout << "<head>\n";

   cout << "<title>CGI Envrionment Variables</title>\n";

   cout << "</head>\n";

   cout << "<body>\n";

   cout << "<table border = \"0\" cellspacing = \"2\">";

 

   for ( int i = 0; i < 24; i++ )

   {

       cout << "<tr><td>" << ENV[ i ] << "</td><td>";

       // attempt to retrieve value of environment variable

       char *value = getenv( ENV[ i ].c_str() ); 

       if ( value != 0 ){

         cout << value;                                

       }else{

         cout << "Environment variable does not exist.";

       }

       cout << "</td></tr>\n";

   }

   cout << "</table><\n";

   cout << "</body>\n";

   cout << "</html>\n";

  

   return 0;

}

所有的CGI程序将会使用到下列的CGI环境变量,这些变量在CGI程序中起着重要的作用。

Variable Name

Description

CONTENT_TYPE

The data type of the content. Used when the client is sending attached content to the server. For example file upload etc.

CONTENT_LENGTH

The length of the query information. It's available only for POST requests

HTTP_COOKIE

Return the set cookies in the form of key & value pair.

HTTP_USER_AGENT

The User-Agent request-header field contains information about the user agent originating the request. Its name of the web browser.

PATH_INFO

The path for the CGI script.

QUERY_STRING

The URL-encoded information that is sent with GET method request.

REMOTE_ADDR

The IP address of the remote host making the request. This can be useful for logging or for authentication purpose.

REMOTE_HOST

The fully qualified name of the host making the request. If this information is not available then REMOTE_ADDR can be used to get IR address.

REQUEST_METHOD

The method used to make the request. The most common methods are GET and POST.

SCRIPT_FILENAME

The full path to the CGI script.

SCRIPT_NAME

The name of the CGI script.

SERVER_NAME

The server's hostname or IP Address

SERVER_SOFTWARE

The name and version of the software the server is running.

 

PATH_TRANSLATED

CGI程序的完整路径名

   

HTTP-REFERER:调用该CGI程序的网页的URL. 

 

5、GET 与POST方法

        当你需要从浏览器客户端传递信息至web服务器端并最终送至CGI程序的时候,你将必然会遇到很多的问题。大部分的浏览器使用两种方法发送信息至浏览器:GET方法和POST方法,进行过WEB开发的人应该对其很熟悉。

        1. 使用GET方法发送信息

       GET方法将编码过的用户信息附加在页面请求上发送,页面请求和这些编码信息使用?进行分割,如下所示:

       http://www.test.com/cgi-bin/cpp.cgi?key1=value1&key2=value2

        GET方法是浏览器发送信息之服务器端所采用的默认的方法,采用这种方法发送时,在你的浏览器地址栏上在URL后面会附加上一串字符串,如果你传输密码或 其他敏感信息至服务器端的时候不要使用GET方法,GET方法有长度限制,在一个请求字符串中,最多只能发送1024的字符。

当使用GET方法的时候,HTTP报文头采用QUERY_STRING发送信息,并且将通过QUERY_STRING环境变量进入你的CGI程序。

您能够使用简单的键-值组合附加在URL后传递信息,或者你也可使用HTML中的<FORM>标签通过使用GET方法来传递信息。

         2. 使用POST方法发送信息

        CGI程序中较为通用的且更为可靠地传递信息的方法是POST方法,POST传递的报文信息和GET方法没什么两样,但是跟GET方法的将字符串信息附加 于URL之后并且用?分隔有所区别的是,POST方法使用分离的报文段分别发送URL和要传输的信息。这些信息会被CGI脚本以标准输入的形式接收。

6、在CGI中使用Cookies编程

     服务器可能会以Cookies的形式发送数据给客户端浏览器上,浏览器也许会接收这些Cookies,并且会以简单文本的形式存储在用户的硬盘上,当用户 访问该web站点的另外页面的时候,这些Cookies就会有用处了,服务器就会据此知道用户记录了那些信息。

 Cookies信息格式包含如下5个变量:

     (1) Expires:包含Cookies的过期信息。如果变量值为空,当客户端关闭浏览器时,Cookies就会过期。

    (2)  Domain:web站点的域名信息。

    (3)  Path:设置Cookies的web页或目录的路径。如果想要从任何页面或目录获取Cookies信息,此变量设为空值。

    (4)  Secure:如果该字段设置为"secure",那么Cookies将只能被安全服务器获取,如果该字段为空,则没有该限制。

    (5)  Name=Value:Cookies以键-值对的形式设置或获取。

1)浏览器触发设置Cookies

       发送Cookies信息至浏览器是非常容易的,这些Cookies将会附加在在HTTP报文头的Content-type域前。假设你想要以Cookies的方式设置UserID和Password,那么简单的CGI设置脚本如下:  

#include <iostream>

using namespace std;

 

int main ()

{

 

   cout << "Set-Cookie:UserID=XYZ;\r\n";

   cout << "Set-Cookie:Password=XYZ123;\r\n";

   cout << "Set-Cookie:Domain=www.tutorialspoint.com;\r\n";

   cout << "Set-Cookie:Path=/perl;\n";

   cout << "Content-type:text/html\r\n\r\n";

 

   cout << "<html>\n";

   cout << "<head>\n";

   cout << "<title>Cookies in CGI</title>\n";

   cout << "</head>\n";

   cout << "<body>\n";

 

   cout << "Setting cookies" << endl; 

 

   cout << "<br/>\n";

   cout << "</body>\n";

   cout << "</html>\n";

  

   return 0;

}

从这个例子中你将了解怎么设置Cookies,那就是使用Set-Cookie来设置Cookies。

        设置Cookies属性的时候,Expires, Domain, and Path是可选的,值得注意的一点是Cookies的设置是在发送"Content-type:text/html\r\n\r\n”之前。运行/cgi-bin/setcookies.cgi将会在你的电脑上设置Cookies。

 

2)浏览器触发获取Cookies

    获取Cookies也非常简单,Cookies都存储在CGI的环境变量HTTP_COOKIE中,并且具有如下的格式:

      key1=value1;key2=value2;key3=value3....

     以下就是一段获取Cookies的简短的CGI代码:

#include <iostream>

#include <vector> 

#include <string> 

#include <stdio.h> 

#include <stdlib.h>

   

#include <cgicc/CgiDefs.h>

#include <cgicc/Cgicc.h>

#include <cgicc/HTTPHTMLHeader.h>

#include <cgicc/HTMLClasses.h>

 

using namespace std;

using namespace cgicc;

 

int main ()

{

   Cgicc cgi;

   const_cookie_iterator cci;

 

   cout << "Content-type:text/html\r\n\r\n";

   cout << "<html>\n";

   cout << "<head>\n";

   cout << "<title>Cookies in CGI</title>\n";

   cout << "</head>\n";

   cout << "<body>\n";

   cout << "<table border = \"0\" cellspacing = \"2\">";

  

   // get environment variables

   const CgiEnvironment& env = cgi.getEnvironment();

 

   for( cci = env.getCookieList().begin();

        cci != env.getCookieList().end();

        ++cci )

   {

      cout << "<tr><td>" << cci->getName() << "</td><td>";

      cout << cci->getValue();                                

      cout << "</td></tr>\n";

   }

   cout << "</table><\n";

 

   cout << "<br/>\n";

   cout << "</body>\n";

   cout << "</html>\n";

  

   return 0;

}

 

html中调用cgi。<form action="/cgi-bin/mult.cgi" method="get" target="_blank">

html中调用html:

<frameset rows="120,*" cols="*" frameborder="no" border="0" framespacing="0">
<frame src="tou.html" name="topFrame" scrolling="No" noresize="noresize" id="topFrame" title="topFrame" />

 

cgi中调用html

if( (strcmp(name,"root")+1) && (strcmp(pass,"password")+1) )
printf("<a href=\"../first.html\" target=\"mainFrame\">%s</a>",name);

 

cgi接受html信息:

date = getenv("QUERY_STRING");
if(date == NULL)
printf("<h1>no word input cgi<h1>");
if(sscanf(date,"fname=%[^&]&lname=%s",&name,&pass)!=2)
printf("<p>you input is wrong</p>");

 

cgi调用javascript

printf(“<script>”);/ script

printf(“window.loaction.herf=/”xm.html/”/n”);

printf(“</script>”);/ /script

xm.html部分你可以选择的跳转到任何部分。

 

 

javascript中调用cgi

function httpGet(index, callback)

{

    var xmlHttp = new XMLHttpRequest();

    xmlHttp.open( "GET", "./config.cgi?"+index, true );

    xmlHttp.setRequestHeader("If-Modified-Since","0");

    xmlHttp.send();

    xmlHttp.onreadystatechange=function()

    {

        //get request ready

        if (xmlHttp.readyState==4 && xmlHttp.status==200)

        {

            keyList.length = 0;

            valList.length = 0;

            callback(xmlHttp.responseText);

        }

    }

}

function xmlHttpPost(str)

{

    var xmlHttp = new XMLHttpRequest();

    xmlHttp.open( "POST", "./config.cgi", true );

//  xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");

    xmlHttp.setRequestHeader("If-Modified-Since","0");

    xmlHttp.send(str);

    xmlHttp.onreadystatechange=function()

    {

        //get request ready

        if (xmlHttp.readyState==4 && xmlHttp.status==200)

        {

            //alert(xmlHttp.responseText);

        }

    }

}

#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include <stdio.h>
#include "cgic.h"
#include <string.h>
#include <stdlib.h>

static int cgiStrEqNc(char *s1, char *s2) {
	while(1) {
		if (!(*s1)) {
			if (!(*s2)) {
				return 1;
			} else {
				return 0;
			}
		} else if (!(*s2)) {
			return 0;
		}
		if (isalpha(*s1)) {
			if (tolower(*s1) != tolower(*s2)) {
				return 0;
			}
		} else if ((*s1) != (*s2)) {
			return 0;
		}
		s1++;
		s2++;
	}
}

int cgiMain() {

	int sfd = dup(STDOUT_FILENO);
	int ttyfd = open("/dev/tty",O_RDWR);

	dup2(ttyfd,STDOUT_FILENO); 

	printf("call third lib\n\n");
	
		  
	dup2(sfd,STDOUT_FILENO); 
	close(ttyfd);
 
   printf("Content-Type:application/json\n\n");
   if (cgiStrEqNc(cgiRequestMethod, "post")) {
	   if (cgiStrEqNc(cgiContentType, "application/json")) {	

			char *input;
			if (!cgiContentLength) {
				return 1;
			}
			input = (char *) malloc(cgiContentLength);
			if (!input) {
				return 2;	
			}
			if (((int) fread(input, 1, cgiContentLength, cgiIn)) 
				!= cgiContentLength) 
			{
				return 3;
			}	

			printf("%s\n\n",input);  //input, cgiContentLength
			free(input);
	   
	   
	   }
   }else if (cgiStrEqNc(cgiRequestMethod, "get")) {	
			
			char*  formjson = " {\"profileActive\":1,\"profileName\":\"profile2\",\"sipServer\":\"192.168.1.113\" ,\"failoverSipServer\":\"192.168.1.22\",\"preferPrimarySipServer\":1,\"outboundProxy\":\"192.168.1.34\",\"voiceMailAccessNumber\":\"966\" } ";

			printf("%s\n\n",formjson);  //input, cgiContentLength

   }

	return 0;
}
<form   id="form1" >   

<script src="./js/jquery-3.5.1.min.js"></script>
<script src="./js/callManager.js"></script> 
<script>

 $(function () {

 
	 $("#DectSipAccountSettingsBtn").click(function () {  //save              post	
	        console.log("DectSipAccountSettingsBtn");
			var obj =  $("form").serializeJson(); //表单转换json对象数据
			//var jsonstr11 = JSON.stringify(obj);// json对象格式化成字符串
           // 使用JQuery将前端form表单数据转换为JSON字符串传递到后台cgi处理
			$.ajax({
				type: "post",
				url: "/cgi-bin/sgw.cgi?setDectSipAccountSettings", 
				async: true,
				processData:false, //布尔值,规定通过请求发送的数据是否转换为查询字符串。默认是 true。
				contentType: 'application/json',   //传递的就是json字符串
				dataType:  'text', //返回数据格式
			  //data:$('#ff').serialize(),//这两种方式都不能直接将表单数据转换为json格式
			  //data:$('#ff').serializeArray(), //这两种方式都不能直接将表单数据转换为json格式
				data: JSON.stringify(obj),//将对象转为json字符串 jsonstr11  //这里是要传递的参数,格式为 data: "{paraName:paraValue}"
				success: function(result) {   //回调函数,result是json对象 如obj格式,返回值
					 console.log(result);

				}
				
			});
				  
	});
			
			
 })
 </script>




#include <stdio.h>
#include "cgic.h"
#include <string.h>
#include <stdlib.h>


static int cgiStrEqNc(char *s1, char *s2) {
	while(1) {
		if (!(*s1)) {
			if (!(*s2)) {
				return 1;
			} else {
				return 0;
			}
		} else if (!(*s2)) {
			return 0;
		}
		if (isalpha(*s1)) {
			if (tolower(*s1) != tolower(*s2)) {
				return 0;
			}
		} else if ((*s1) != (*s2)) {
			return 0;
		}
		s1++;
		s2++;
	}
}


int cgiMain() {
	

   if (cgiStrEqNc(cgiRequestMethod, "post")) {
	   if (cgiStrEqNc(cgiContentType, "application/json")) {	
	   
	   
 
			char *input;
			if (!cgiContentLength) {
				return 1;
			}
			input = (char *) malloc(cgiContentLength);
			if (!input) {
				return 2;	
			}
			if (((int) fread(input, 1, cgiContentLength, cgiIn)) 
				!= cgiContentLength) 
			{
				return 3;
			}	
			
			printf("Content-Type:text/html\n\n");
			if (cgiStrEqNc(cgiQueryString, "setDectSipAccountSettings")) {
				
               printf(" cgi receive %s  %s\n\n",cgiQueryString,input);
					#if 0

					struct json_object *my_object = json_tokener_parse(input); //由str里的JSON字符串生成JSON对象
					/*遍历json对象集合*/

					json_object_object_foreach(my_object, key, val) {

					      printf("\t%s: %s\n", key, json_object_to_json_string(val));

					}

					printf("my_object.to_string()=%s\n", json_object_to_json_string(my_object));

					json_object_put(my_object); // 减少对象引用次数一次,当减少到0就释放(free)资源

					#endif
		
				
			}	
			else if (cgiStrEqNc(cgiQueryString, "setProfilesGeneralSettings")) {
				
                     printf("cgi receive  %s  %s\n\n",cgiQueryString,input);
				
			}


		//   printf("Content-Type:application/json\n\n");
		//	printf("%s\n\n",input);  //input, cgiContentLength
			free(input);
	   
	   
	   }
   }  
   
 	return 0;
}
  
  
  
 
#if  0 //json-c开发指南  https://www.cnblogs.com/qingergege/p/5997762.html
// JSON对象的生成
struct json_object * json_object_new_object();
说明: 创建个空的json_type_object类型JSON对象


struct json_object * json_tokener_parse(char *str);
说明:由str里的JSON字符串生成JSON对象,str是json_object_to_json_string() 生成的。
参数:str – json字符串


struct json_object * json_object_object_get(struct json_object * json,char *name);

说明:从json中按名字取一个对象。

参数:

    json – json对象

    name -  json域名字
	
	
	
struct json_object* json_object_new_boolean(Boolean b);

说明:创建个json_type_boolean值类型json对象


Boolean json_object_get_boolean(struct json_object *obj);
说明: 从json对象中boolean值类型得到boolean值

      	

//JSON对象的释放

struct json_object * * json_object_get(struct json_object * this)
说明:增加对象引用计数。使用c库最关心的是内存谁来分配, 谁来释放. jsonc的内存管理方式, 是基于引用计数的内存树(链), 如果把一个struct json_object 对象a, add到另一个对象b上, 就不用显式的释放(json_object_put) a了, 相当于把a挂到了b的对象树上, 释放b的时候, 就会释放a. 当a既add到b上, 又add到对象c上时会导致a被释放两次(double free), 这时可以增加a的引用计数(调用函数json_object_get(a)), 这时如果先释放b, 后释放c, 当释放b时, 并不会真正的释放a, 而是减少a的引用计数为1, 然后释放c时, 才真正释放a.

参数:this – json对象


       
Void json_object_put(struct json_object * this)

说明:减少对象引用次数一次,当减少到0就释放(free)资源
   
参数:this – json对象

       

 



//JSON对象的操作

Int json_object_is_type(struct json_object * this, enum json_type type)

说明: 检查json_object是json的某个类型

参数: this: json_object 实例

       type: json_type_boolean,json_type_double, json_type_int, json_type_object, json_type_array, json_type_string

 

enum json_type json_object_get_type(struct json_object * this )

说明:得到json_object的类型。
  
参数: this – json对象


char * json_object_to_json_string(struct json_object * this)

说明:将json_object内容转换json格式字符串,其中可能含有转义符。
       
参数: this – json对象

返回值:Json格式字符串


void json_object_object_add(struct json_object* obj, char *key, struct json_object *val);

说明:添加个对象域到json对象中
参数:

       Obj – json对象

       key – 域名字

       val – json值对象
	   
	   
void json_object_object_del(struct json_object* obj, char *key);

说明:删除key值json对象  

参数:

       ob j – json对象

       key – 域名字	   
	   
	   
int json_object_array_length(struct json_object *obj);

说明:得到json对象数组的长度。  

参数: ob j – json数组值对象	  

       
       
extern int json_object_array_add(struct json_object *obj,

                             struct json_object *val);

说明:添加一元素在json对象数组末端
    
参数:

ob j – json数组值对象

val – json值对象	



int json_object_array_put_idx(struct json_object *obj, int idx,

                                 struct json_object *val);

说明: 在指定的json对象数组下标插入或替换一个json对象元素。

   
参数:

obj – json数组值对象

val – json值对象

idx – 数组下标
   
	   
struct json_object * json_object_array_get_idx(struct json_object * json_array,int i);

说明:从数组中,按下标取JSON值对象。

参数:

       json_array – json 数组类型对象

       i – 数组下标位置	   
	   



定义宏 json_object_object_foreach(obj,key,val)

说明:遍历json对象的key和值 (key, val默认参数不变)

       


#endif 
  

8完成html CGI 用户进程 的交互,实现数据上报到html在主页面index.html里使用iframe 嵌入子页面  <form action= "/cgi-bin/mycgi.cgi"  可以观察到只有嵌入的子页面部分进行了页面的绘制(数据更新)

https://blog.csdn.net/Set_Mode/article/details/95074636

 

在基于嵌入式平台开发基于Boa服务器的过程中,遇到以下两个问题:

1、Boa中经常使用CGI对页面进行输出,但某些页面每次只需改变其中某一部分,其余部分(如菜单栏、标题栏等)无需变更,若每次均将整个页面重新调用CGI输出,其工作量太大,且代码上不易于阅读和理解。

2、不同功能对应不同的子页面,但在访问不同子页面时地址栏会显示对应的文件名称,从一定程度上来说这是不安全的。

解决方法::

可以考虑使用一个页面作为框架,带有无需经常变动的菜单栏、标题栏等,然后在这个主页面嵌入子页面,每次从嵌入式平台读取到新的数据需要更新至页面时,只需使用CGI输出至子页面即可。

 

Cgi C程序将值返回到主HTML并显示结果

如何实现这些吗?如何在不打开新的html站点的情况下显示用户列表?同样在上面的C代码中,我必须调用链接" http:// localhost:8000 / newuser /" ,该链接将返回成功或失败值。如何将其返回到父表单? 

您可以将iframe添加到html中:

 < iframe id =" theiframe" name =" theiframe">< / iframe>

 

然后将表单的目标设置为iframe:

 < form action =" /cgi-bin/mycgi.cgi" name ="创建用户" method =" get" target =" theiframe">

 

比如我打算在主页面index.html里使用iframe 嵌入子页面  <form action= "/cgi-bin/mycgi.cgi"  并且可以观察到不管子页面中进行何种跳转,浏览器地址栏显示的地址都是主页面的地址

可以观察到只有嵌入的子页面部分进行了页面的绘制(数据更新)

<html>

<head><title>Home</title></head>

<body>

<h1>REGISTER</h1>

 < iframe id =" theiframe" name =" theiframe">< / iframe>

<form action= "/cgi-bin/mycgi.cgi" name ="create user" method ="get"  target =" theiframe">

Enter name:<input type="text" name="user">

<br>

<input type="submit" value="add">

</form>

 <FORM action="http://localhost:8000/getusers/" method="get">

    <P>

    <input value="Display Users" type="submit">

    </P>

 </FORM>

</body>

 

 

 

CGI完成对HTML的同步更新

 

#include <stdio.h>

#include <stdlib.h>

#include "cgic.h"

 

int cgiMain()

{

      cgiHeaderContentType("text/html");

      fprintf(cgiOut, "<head><meta http-equiv=\"refresh\" content=\"1\"><style><!--body{line-height:50%}--></style> </head>");

      fprintf(cgiOut, "<HTML>\n");

      fprintf(cgiOut, "<BODY bgcolor=\"#666666\">\n");

//fprintf(cgiOut, "<h1><font color=\"#FF0000\">HOME_ID #%d:</font></H2>\n ", shm_buf->shm_status);

      if (shm_buf->shm_status == 1)

      {

           fprintf(cgiOut, "<script>function show(){var date =new Date(); var now = \"\"; now = date.getFullYear()+\"\"; now = now + (date.getMonth()+1)+\"\"; \ now = now + date.getDate()+\"\"; now = now + date.getHours()+\"\"; now = now + date.getMinutes()+\"\";now = now + date.getSeconds()+\"\"; document.getElementById(\"nowDiv\").innerHTML = now; setTimeout(\"show()\",1000);} </script> \n ");  

           fprintf(cgiOut, "<h2><font face=\"Broadway\"><font color=\"#00FAF0\">Home1 Real-time Environment Info:</font></font></H2>\n ");

           fprintf(cgiOut, "<h2 align=center><font color=\"#cc0033\"><body οnlοad=\"show()\"> <div id=\"nowDiv\"></div></font></h2> \n ");

           fprintf(cgiOut, "<h4>ZIGBEE数据显示部分</H4>\n ");

           fprintf(cgiOut, "<h4>Temperature:\t%0.2f</H4>\n ", shm_buf->sm_all_env_info.monitor_no[shm_buf->shm_status].zigbee_info.temperature );

           fprintf(cgiOut, "<h4>Humidity:\t%0.2f</H4>\n ", shm_buf->sm_all_env_info.monitor_no[shm_buf->shm_status].zigbee_info.humidity);

           fprintf(cgiOut, "<h4>A9数据显示部分</H4>\n ");

           fprintf(cgiOut, "<h4>Adc:\t%0.2f</H4>\n ", shm_buf->sm_all_env_info.monitor_no[shm_buf->shm_status].a9_info.adc);

           fprintf(cgiOut, "<h4>GYROX:\t%d</H4>\n ", shm_buf->sm_all_env_info.monitor_no[shm_buf->shm_status].a9_info.gyrox);

           fprintf(cgiOut, "<h4>A9-RESERVED[1]:\t%d</H4>\n ", shm_buf->sm_all_env_info.monitor_no[shm_buf->shm_status].a9_info.reserved[1]);

           fprintf(cgiOut, "<h4>STM32数据显示部分</H4>\n ");

           fprintf(cgiOut, "<h4>......</H4>\n ");

      }

      else

      {

           fprintf(cgiOut, "<h2><font face=\"Broadway\"><font color=\"#FFFAF0\">Close!</font></font></H2>\n ");

      }

//    fprintf(cgiOut, "<h3>:</H3>\n ");

      fprintf(cgiOut, "</BODY></HTML>\n");        

      sem_v (semid, 0);

      return 0;

}

 

 

 

9、CGI实现文件的上传和下载(界面触发)

 

1.文件上传到服务器

借助HTMLform标签和input标签实现,如 

<form action="file-up/download.cgi" enctype="multipart/form-data" method="post">

   <input type="file" /> 用于文件上传。

</form>

 

2.从服务器下载文件

借助HTML <a> download 属性--只有 Firefox Chrome 支持 download 属性

,如:<a href="filepathdownload="filename"> download </a>,则打开浏览器点击链接即可实现文件下载。

注:其中download属性可避免直接打开文件,进而执行下载任务;该方式的优点是在静态html界面即可实现文件下载,缺点是暴露了文件及其路径。

实例

点击此链接来下载log

<a href="/log /run.log" download="log">

 

界面form代码

<form action="/cgi-bin/file-up/download.cgi" enctype="multipart/form-data" method="post">    <!-- 上传文件必须设置为post + multipart/form-data-->

    <table>                            

        <tbody>

        <tr>

            <td> 上传文件</td>  <!-- 上传文件到服务器-->

            <td ><input type="file" name="uploadfile" value=""></td>

            <td colspan="3"><input type="submit" name="upload" value="导入"></td>

        </tr>

       

        <tr>

            <td>下载文件 </td> <!--将服务器中指定的目录下的文件下载 只有 Firefox Chrome 支持 download 属性, a标签的-->

            <td ><a href="/log/run.log" download="run.log">浏览log</a></td>

        <td colspan="3" ><input type="button" value="导出" οnclick="location.href='/cgi-bin/file-up/download.cgi'"></td>

        </tr>

        </tbody>

    </table>

  </form>

 

download.cgi  UploadFile执行函数代码

enum ErrLog

{

    ErrSucceed,

    ErrOpenFile,

    ErrNoFile

};

enum ErrLog UploadFile()

{

    cgiFilePtr file;

    FILE *fd;

    char name[512];

    char path[128];

    char contentType[1024];

    int size = 0;

    int got = 0;

    int t = 0;

    char *tmp = NULL;

 

    if (cgiFormFileName("uploadfile", name, sizeof(name)) != cgiFormSuccess) //获取客户端pathname

    {

        printf("<p> No file was uploaded. </p>\n");

        return ErrNoFile;

    }

    fprintf(cgiOut, "The filename submitted was: ");

    cgiHtmlEscape(name);

    fprintf(cgiOut, "<br>\n");

       

    cgiFormFileSize("uploadfile", &size);

    fprintf(cgiOut, "The file size was: %d bytes<br>\n", size);

       

    cgiFormFileContentType("uploadfile", contentType, sizeof(contentType));

    fprintf(cgiOut, "The alleged content type of the file was: ");

    cgiHtmlEscape(contentType);

    fprintf(cgiOut, "<br>\n");

 

    if (cgiFormFileOpen("uploadfile", &file) != cgiFormSuccess)  //尝试打开上传的,并存放在系统中的临时文件

    {

        fprintf(cgiOut, "<p> Could not open the file. </p>\n");

        return ErrOpenFile;

    }

 

    t = -1;

    while (1)

    {

        tmp = strstr(name+t+1, "\\");  // pathname解析出filename

        if (NULL == tmp)

        {

            tmp = strstr(name+t+1, "/");

        }

        if (NULL != tmp)

        {

            t = (int)(tmp-name);

        }

        else

        {

            break;

        }

    }

    tmp = (char *)malloc(size * sizeof(char)); // 在底层建立新文件

    strcpy(path, "/usr/local/boa/data/");

    strcat(path, name+t+1); 

    fd = fopen(path, "w+");

    if (fd == NULL)

    {

        return ErrOpenFile;

    }

 

    while (cgiFormFileRead(file, tmp, size, &got) == cgiFormSuccess) // 从临时文件读出content

    {

        fwrite(tmp, size, sizeof(char), fd);  //把读出的content写入新文件

    }

    fprintf(cgiOut, "<p> Upload File Success. </p>\n");

 

    cgiFormFileClose(file);

    free(tmp);

    fclose(fd);

    return ErrSucceed;

}

 

 

download.cgi'  download执行代码

void download(char *filename)

{

    FIFE *fp;

    char buff[SIZE];

    struct stat s;

    time_t date;

    int n;

 

    date = time(NULL);

    stat(filename, &s);

    printf(“Content Disposition:filename=\“%s\” date=%s\n”, filename, ctime(&date));

    printf(“Content Length:size==%d\n”, s.st_size);

    if (fp = fopen(filename, “r”))

    {

        while ((n = fread(buff, sizeof(char), sizeof(buff), fp)) > 0)

        {

            fwrite(buff, n, 1, stdout)

        }

        fclose(fp);

    }

}

 

 

四、环境变量列表

SERVER_NAME:运行CGI序为机器名或IP地址。

SERVER_INTERFACEWWW服务器的类型,如:CERN型或NCSA型。

SERVER_PROTOCOL:通信协议,应当是HTTP/1.0

SERVER_PORTTCP端口,一般说来web端口是80

HTTP_ACCEPTHTTP定义的浏览器能够接受的数据类型。

HTTP_REFERER发送表单的文件URL。(并非所有的浏览器都传送这一变量)

HTTP_USER-AGENT:发送表单的浏览的有关信息。

GETWAY_INTERFACECGI程序的版本,在UNIX下为 CGI/1.1

PATH_TRANSLATEDPATH_INFO中包含的实际路径名。

PATH_INFO:浏览器用GET方式发送数据时的附加路径。

SCRIPT_NAMECGI程序的路径名。

QUERY_STRING表单输入的数据,URL中问号后的内容。

REMOTE_HOST:发送程序的主机名,不能确定该值。

REMOTE_ADDR:发送程序的机器的IP地址。

REMOTE_USER:发送程序的人名。

CONTENT_TYPEPOST发送,一般为application/xwww-form-urlencoded

CONTENT_LENGTHPOST方法输入的数据的字节数

五、注意的问题

CGI应用程序运行在浏览器可以请求的服务器系统上,执行时需要使用服务器CPU时间和内存。如果有成千上万的这种程序会同时运行,那会对服务器系统提出极高的要求。你要慎重考虑这个问题,以防止服务器系统崩溃

不完善的CGI应用程序可能成为别人非法进人服务器系统的通道,有可能导致重要的资料被删除或外泄。CGI应用程序主要的用途有以下几种:

根据浏览者填写的HTML表单发送定制的答复

创建可单击的图像缩小图;

创建一个浏览者可以搜索内容的数据库;

提供服务器与数据库的接口,并把结果转换成HTML文档

制作动态HTML文挡。

如果一个CGI脚本可以在每台计算机上做同样的事情;编写脚本就会变的很容易。不幸的是,CGI脚本依赖于服务器的操作系统,因此,对于非UNIX服务器来说,PrlUNIX下编写脚本的一个常用工具)脚本毫无用处。所以,你必须定制安装你的CGI脚本。

大多数服务器都提供CGI-BIN目录,但是这还不够。因为你应该拥有自己的CGI-BIN。这样,你就能运行自己的脚本(而不是让自己的系统去适应已存在于系统上的脚本)。因此,你的提供商应安装CGI-BIN,且能够帮助你编写脚本

 

六、参考

CGI(通用网关接口)_百度百科https://baike.baidu.com/item/CGI/607810?fr=aladdin

CGI编程https://www.cnblogs.com/sjxbg/p/5897558.html

protobuf / json 数据转换

protobuf 3.0 版本支持 protobuf json 数据相互转换。

https://wenfh2020.com/2020/10/28/protobuf-convert-json/

https://www.coder.work/article/121306

使用Protobuf 3.3.0,它确实具有内置的JSON序列化器和解析器。您可以使用google/protobuf/util/json_util.h中的2个函数MessageToJsonString()JsonStringToMessage()来分别使C++生成的Message对象进入JSON和从JSON进入。

 

 

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐