系列入口:编程实战:自己编写HTTP服务器(系列1:概述和应答)-CSDN博客
本文讲解如何将原有C++代码接入到WEB服务里。
目录
一、概述
二、框架入口
三、执行用户功能
3.1 代码
3.2 入口参数
3.3 系统检查和初始化
3.4 执行
四、接口
一、概述
做嵌入式服务器的主要目的当然不是提供静态文件,而是将程序里的功能和浏览器结合起来,让浏览器成为程序的用户界面。用浏览器直接调用程序功能显然比查日志文件或者数据库友好多了。用浏览器直接操作程序也比修改配置文件再重启程序方便很多倍。
命令接口最基础的就是一个函数指针或者虚函数,提前配置好函数指针或虚函数,框架只需要调用函数指针或虚函数即可。主要的工作是制定合理的输入输出参数。
二、框架入口
框架中根据请求的资源目录和扩展名来确定是否是用户功能:
从上面的代码可以看出来,资源是“/bin/”或者“/admin/”目录(虚拟目录)下的就执行用户功能。扩展名为“asp”、“aspx”、“asmx”的当作内置页面(显然这是我在模仿IIS,这些后缀名与它们通常的意义毫无关系),其余当作CGI,当然了CGI已经没人写了,所以可以无视。
doPageFunction()的代码是这样的:
首先查找了命令, 然后根据命令的属性做了一些区别,实质的执行代码是_doPageFunction()。如果有错误就返回404或500。
其余代码不用关心。你当然也会在自己的程序里加入很多特定功能。
三、执行用户功能
3.1 代码
bool _doPageFunction(web_command_private_struce * pCmdInfo)
{
long i_cmd = pCmdInfo->seq;
CWebCommand * pCmd = pCmdInfo->pWebCommand;
//处理用户系统内部检查
string err;
if (NULL != this->pfCheckSys && !pfCheckSys(err))
{
//用户系统检查未通过,不执行用户函数
OnPageStart("500 Internal Server Error");
m_respond.AppendBody(CHtmlDoc::HTMLEncode(err));
OnPageEnd();
return false;
}
++m_pServerDatas->m_web_command_data_s[i_cmd].count;//执行计数
if (!pCmdInfo->isInited)
{
if (!pCmd->InitWebFunction())
{
LOG << pCmd->command_id << " 初始化失败" << ENDE;
OnPageStart("500 Internal Server Error");
m_respond.AppendBody(CHtmlDoc::HTMLEncode(err));
OnPageEnd();
return false;
}
else
{
LOG << pCmd->command_id << " 初始化成功" << ENDI;
pCmdInfo->isInited = true;
}
}
if (pCmd->NotStdPage)
{
if (!pCmd->doWebFunction(&this->m_request, this->m_s, &this->m_respond))
{
++m_pServerDatas->m_web_command_data_s[i_cmd].count_err;//出错计数
}
}
else
{
//文件头
OnPageStart(pCmd->name.c_str());
LOG << "发送应答头" << ENDI;
if (!m_respond.Flush(m_s))return true;
if (!CallCommand(pCmd))
{
++m_pServerDatas->m_web_command_data_s[i_cmd].count_err;//出错计数
}
//文件尾
m_respond.AppendBody("<P><HR></HR>");
m_respond.AppendBody(pCmd->name);
m_respond.AppendBody("<BR>");
m_respond.AppendBody(pCmd->note);
m_respond.AppendBody("<BR>");
if (!pCmd->hide)
{
pCmd->SetParamValueFromRequest(m_request);
m_respond.AppendBody(pCmd->toHtmlFormInput(false, false, &m_request));
m_respond.AppendBody("<INPUT TYPE=\"hidden\" NAME=\"autorefresh\" >\n");
m_respond.AppendBody(pCmd->toHtmlFormSubmit(true));
if (pCmd->AutoRefresh)
{
char buf2[2048];
bool isAutoRefresh = (m_request.GetParam("autorefresh") == "true");
long refreshtimeout = atol(m_request.GetParam("refreshtimeout").c_str());
if (refreshtimeout <= 0)refreshtimeout = 10;
sprintf(buf2,
"<BUTTON NAME=\"autorefresh\" onclick=\"timer=setTimeout('window.%s.submit()',%ld);window.%s.autorefresh.value='true';window.autorefresh.disabled=true;window.stoprefresh.disabled=false;\">自动刷新</BUTTON>\n"
"<button NAME=\"stoprefresh\" disabled onclick=\"clearTimeout(timer);window.%s.autorefresh.value='';window.autorefresh.disabled=false;window.stoprefresh.disabled=true;\">停止刷新</BUTTON><BR>\n"
"%s<BR>\n"
, pCmd->GetFormName().c_str(), refreshtimeout * 1000, pCmd->GetFormName().c_str(), pCmd->GetFormName().c_str(), (isAutoRefresh ? "<script language=\"JavaScript\">window.autorefresh.onclick()</script>" : ""));
m_respond.AppendBody(buf2);
}
}
OnPageEnd();
}
return true;
}
3.2 入口参数
入口参数web_command_private_struce里面主要就是接口对象,一个函数指针或者虚函数。
请求对象和应答对象都是类成员,在类里面可以随时使用,但是用户功能是外部的,所以要在调用时作为参数传递。
3.3 系统检查和初始化
调用外部功之前执行了外部提供的系统检查pfCheckSys,如果失败就不能执行用户功能。
每个用户功能可能需要初始化。所以第一调用要先初始化。
如果功能够简单,这两步都可以忽略。
3.4 执行
执行分两种,一种是嵌入在标准HTML页面里面,用户功能只提供主体输出,另一种是用户功能完全控制输出(比如一张动态刷新页面内容的无限网页,后面我会介绍如何实现)。
标准页面由框架生成HTML的首尾部分,可以具有相同的辅助功能,中间调用CallCommand输出内容,纯自定义输出则执行“pCmd->doWebFunction(&this->m_request, this->m_s, &this->m_respond)”,CallCommand其实也是执行这个,只不过略加一点控制。
剩下的核心就是doWebFunction了。
四、接口
待续。
(这里是结束,但不是整个系列的结束)