示例代码下载: https://download.csdn.net/download/wanghaisheng/13091515
http协议的数据传输有两种方法,即POST和GET, 当然还好多, 但常用的就是这两种了。为了保证数据的解析正确定性,在数据传输之前要先对数据进行编码.
虽然在设置表单信息的传输方式时有POST和GET两种方法,但是不管采取哪种方法,浏览器采取的编码方式却是完全相同的。编码规则如下:
□ 变量之间使用“&”分开
□ 变量与其对应值之间使用“=”链接
□ 空格符使用“+”代替
□ 保留的控制字符则使用百分号接相应的十六进制ASCII代替
□ 空格是非法字符
□ 任意不可打印的ASCII 控制字符都为非法字符
□ 某些具有特殊意义的字符也用百分号接相应的十六进制ASCII代替
说明白点就是键值对的形式, 如 cmd=1&err_code=0&response_info=not surport&db_dur=1:40&db_heartbeat=115
但如果里面有特殊的字符就会造成解析出错, 所以就要进行编码, 有了编码就要有解码, 这在RFC里都有规定怎么编,不多说了, 我上代码:
int DecodeURI(const char* inSrc, int inSrcLen, char* ioDest, int inDestLen)
{
// return the number of chars written to ioDest
// or KH_BadURLFormat in the case of any error.
if( inSrcLen <= 0 || inSrc == NULL || ioDest == NULL || inDestLen <= 0 ) {
return -1;
}
//ASSERT(*inSrc == '/'); //For the purposes of '..' stripping, we assume first char is a /
int theLengthWritten = 0;
int tempChar = 0;
int numDotChars = 0;
while (inSrcLen > 0)
{
if (theLengthWritten == inDestLen) {
return -1;
}
if (*inSrc == '%')
{
if (inSrcLen < 3) {
return -1;
}
//if there is a special character in this URL, extract it
char tempbuff[3];
inSrc++;
if (!isxdigit(*inSrc)) {
return -1;
}
tempbuff[0] = *inSrc;
inSrc++;
if (!isxdigit(*inSrc)) {
return -1;
}
tempbuff[1] = *inSrc;
inSrc++;
tempbuff[2] = '\0';
sscanf(tempbuff, "%x", &tempChar);
if( tempChar >= 256 ) {
return -1;
}
//ASSERT(tempChar < 256);
inSrcLen -= 3;
}
else if (*inSrc == '\0') {
return -1;
}
else
{
// Any normal character just gets copied into the destination buffer
tempChar = *inSrc;
inSrcLen--;
inSrc++;
}
//
// If we are in a file system that uses a character besides '/' as a
// path delimiter, we should not allow this character to appear in the URL.
// In URLs, only '/' has control meaning.
//if ((tempChar == kPathDelimiterChar) && (kPathDelimiterChar != '/'))
if (tempChar == '\\') {
return -1;
}
// Check to see if this character is a path delimiter ('/')
// If so, we need to further check whether backup is required due to
// dot chars that need to be stripped
if ((tempChar == '/') && (numDotChars <= 2) && (numDotChars > 0))
{
if( theLengthWritten <= numDotChars ) {
return -1;
}
//ASSERT(theLengthWritten > numDotChars);
ioDest -= (numDotChars + 1);
theLengthWritten -= (numDotChars + 1);
}
*ioDest = tempChar;
// Note that because we are doing this dotchar check here, we catch dotchars
// even if they were encoded to begin with.
// If this is a . , check to see if it's one of those cases where we need to track
// how many '.'s in a row we've gotten, for stripping out later on
if (*ioDest == '.')
{
if( theLengthWritten <= 0 ) {
return -1;
}
//ASSERT(theLengthWritten > 0);//first char is always '/', right?
if ((numDotChars == 0) && (*(ioDest - 1) == '/'))
numDotChars++;
else if ((numDotChars > 0) && (*(ioDest - 1) == '.'))
numDotChars++;
}
// If this isn't a dot char, we don't care at all, reset this value to 0.
else
numDotChars = 0;
theLengthWritten++;
ioDest++;
}
// Before returning, "strip" any trailing "." or ".." by adjusting "theLengthWritten
// accordingly
if (numDotChars <= 2)
theLengthWritten -= numDotChars;
return theLengthWritten;
}
int EncodeURI(const char* inSrc, int inSrcLen, char* ioDest, int inDestLen)
{
// return the number of chars written to ioDest
int theLengthWritten = 0;
while (inSrcLen > 0)
{
if (theLengthWritten == inDestLen) {
return -1;
}
//
// Always encode 8-bit characters
if ((unsigned char)*inSrc > 127)
{
if (inDestLen - theLengthWritten < 3) {
return -1;
}
//char iChar = (char)inSrc[0];//(BYTE)*inSrc
sprintf(ioDest,"%%%X",(char)inSrc[0]);
ioDest += 3;
theLengthWritten += 3;
inSrc++;
inSrcLen--;
continue;
}
//
// Only encode certain 7-bit characters
switch (*inSrc)
{
// This is the URL RFC list of illegal characters.
case (' '):
case ('\r'):
case ('\n'):
case ('\t'):
case ('<'):
case ('>'):
case ('#'):
case ('%'):
case ('{'):
case ('}'):
case ('|'):
case ('\\'):
case ('^'):
case ('~'):
case ('['):
case (']'):
case ('`'):
case (';'):
case ('/'):
case ('?'):
case ('@'):
case ('='):
case ('&'):
case ('$'):
case ('"'):
{
if ((inDestLen - theLengthWritten) < 3) {
return -1;
}
//char iChar = (char)inSrc[0];//(BYTE)*inSrc
sprintf(ioDest,"%%%X",(char)inSrc[0]);
ioDest += 3;
theLengthWritten += 3;
break;
}
default:
{
*ioDest = *inSrc;
ioDest++;
theLengthWritten++;
}
}
inSrc++;
inSrcLen--;
}
return theLengthWritten;
}
CGI(Common Gateway Interface):是HTTP服务器与你的或其它机器上的程序进行“交谈”的一种工具,其程序须运行在网络服务器上。
2.1 CGI处理步骤:
通过Internet把用户请求送到服务器 ==> 服务器接收用户请求并交给CGI程序处理 ==> CGI程序把处理结果传送给服务器 ==> 服务器把结果送回到用户
2.2 CGI参数获取与结果传递
参数获取:在通过提前设置环境变量,或者使用管道的方式从标准输入中读取
结果传递:重定向标准输出到管道的输入端口,通过管道将数据发送给服务器
2.1 POST
如果在form表单中method使用POST方法,那么服务器会将会把从表单中填入的数据接收,并传给相应的CGI程序(就是action中指定的CGI程序),同时把REQUEST_METHOD环境变量设置为POST,而相应的CGI程序检查该环境变量,以确定其工作在POST接收数据方式,然后读取这个数据。注意使用POST这种方法传输数据时,Http在数据发送完后,并不会发送相应的数据传输完毕提示信息,所以Http服务器提供了另一个环境变量CONTENET_LENGTH,该环境变量记录了传输过来了多少个字节长度的数据(单位为字节),所以在编写CGI程序时,如果method为POST,就需要通过该变量来限定读取的数据的长度(如何实现,下面讲解)。
2.2 GET
基本上GET方法和POST方法相同,不同的是,使用GET方法时,数据被存储到一个叫做QUERY_STRING的环境变量中了,具体如何得到该变量里的内容,会在下面的例子中详细讲述。
说了这么多,通过实例看一下,具体实现时如何编写CGI程序。
int main(){
map
map
string strCmd ="";
kt_cmd_code e_cmd = cmd_invalid;
//---------------------------------
//解析部分 这部分已经完成了的数据的解析
//---------------------------------
char *lpMethod = getenv( "REQUEST_METHOD" );
if (!strcmp( lpMethod, "GET" ))
{
char * lpInputStr = getenv( "QUERY_STRING" );
mapInputKV = ParseString( lpInputStr );
}
else if (!strcmp( lpMethod, "POST" ))
{
char *lpLenstr = getenv( "CONTENT_LENGTH" );
if (lpLenstr == NULL){
//cout << "Error, CONTENT_LENGTH should be entered!" << "
";
//printf();
mapOutputKV[KEY_ERR] = ERR_INVALID_CONTENT_LENGTH;
mapOutputKV[KEY_RESPONE_INFO] = "无效的数据长度";
goto SEND;
}
else{
int iLen = atoi( lpLenstr );
char * lpPoststr = new char[iLen + 2];
memset( lpPoststr, 0, iLen + 2 );
fgets( lpPoststr, iLen + 1, stdin );
//cout << "poststr:" << poststr << "
";
mapInputKV = ParseString( lpPoststr );
//cout << "m * n = " << atoi( m )*atoi( n ) << "
";
}
}
else{
//其它的方法都不支持
mapOutputKV[KEY_ERR] = ERR_INVALID_METHOD;
mapOutputKV[KEY_RESPONE_INFO] = "无效的HTTP方法";
goto SEND;
}
//---------------------------------
//执行部分 把其它的处理过程都加上就可以, 可以把每个过程都放到一个函数里,让MAIN变短.
//---------------------------------
//
strCmd = mapInputKV[KEY_CMD];
e_cmd = strCmd.empty() ? cmd_invalid : (kt_cmd_code)(atoi( strCmd.c_str() ));
switch( e_cmd )
{
case cmd_get_dashboard_info:
{
mapOutputKV[KEY_ERR] = ERR_NOERR;
mapOutputKV[KEY_RESPONE_INFO] = "成功了, 用于客户端提示用户";
mapOutputKV[KEY_DASHBOARD_DURATION] = "1:40";
mapOutputKV[KEY_DASHBOARD_HEARTBEAT] = "115";
//.....添加其它的信息
break;
}
case cmd_get_network_info:
{
mapOutputKV[KEY_ERR] = ERR_NOERR;
mapOutputKV[KEY_RESPONE_INFO] = "成功了, 用于客户端提示用户";
//.....添加其它的信息
mapOutputKV[KEY_NETWORK_IP] = "192.168.1.15";
mapOutputKV[KEY_NETWORK_MASK] = "255.255.255.0";
mapOutputKV[KEY_NETWORK_GATEWAY] = "192.168.1.1";
mapOutputKV[KEY_NETWORK_DNS] = "202.106.0.20";
break;
}
case cmd_set_network_info:
{
//进行设置
string sIP = mapInputKV[KEY_NETWORK_IP];
string sMask = mapInputKV[KEY_NETWORK_MASK];
string sGate = mapInputKV[KEY_NETWORK_GATEWAY];
//根据执行情况设置返回信息
mapOutputKV[KEY_ERR] = ERR_NOERR;
mapOutputKV[KEY_RESPONE_INFO] = "成功了, 用于客户端提示用户";
break;
}
case cmd_get_ndi_list: //对于处理代码比较多的情况可以重新定义一个处理函数, 这样会使MAIN函数小一点好维护,这里就是一个示例
{
//ndi[1]=DESKTOP-KSLG886 (Test Pattern):192.168.1.191:5961 ndi[2]=DESKTOP-KSLG886 (vMix - External 2):192.168.1.191:5963 ndi[3]=DESKTOP-KSLG886 (vMix - Output):192.168.1.191:5962
if (ProGetNdiListCmd( mapInputKV, mapOutputKV ) != 0){
//输出日志
}
break;
}
//.................
default:
{
mapOutputKV[KEY_ERR] = ERR_INVALID_CMD;
mapOutputKV[KEY_RESPONE_INFO] = "失败, 不支持的命令请求";
break;
}
}
//---------------------------------
//输出部分 , 只要收到请求都是必须要回复的,这是遵循HTTP协议标准, 所以
//---------------------------------
SEND:
//这个函数已经完成的数据的发送, 这部分已经不用动了
if (ProResponse( mapOutputKV ) != 0){
//输出日志
}
return 0;
}
为了保证代码的简单好懂, 这里只是贴了主函数. 如何要全部代码请下载资源, 放心只要一分.