请拿笔记记下新域名360优化大师官方免费下载
系列入口:编程实战:自己编写HTTP服务器(系列1:概述和应答)-CSDN博客
本文介绍各种功能的实现。大部分是特定内置入口。
目录
一、默认页
二、查看文件
三、关闭服务
四、下载页面
一、默认页
前面在已经介绍过重定向,默认页可以通过重定向转到指定的页面,因为我这里的主要目的是提供嵌入功能,所以提供了一个内置的默认页:
如果资源名称是这“/default.asp”或“/default.htm”,就执行默认功能,HTML页面的框架已经用OnPageStart和OnPageEnd构建,专用的代码放在doPageDefault里面:
bool ShowStopServer(){string str;//str="<BR><BR><A href=\"/shell.asp?curdir="+m_root+"\" target=\"_blank\">Shell</A><P>\n";//m_respond.AppendBody(str);str="<BR><BR><FORM ACTION=\"/stopserver.asp\" METHOD=\"POST\" target=\"_blank\">\n""<CODE>关闭服务器需要口令:</CODE><BR><INPUT TYPE=\"password\" SIZE=\"30\" NAME=\"password\" >\n""<INPUT TYPE=SUBMIT VALUE=\"关闭服务器\" >\n""</FORM>\n";m_respond.AppendBody(str);return true;}bool doPageDefault(){ShowFunctionList(false);ShowStopServer();return true;}
ShowFunctionList()是显示功能列表,是真正面向嵌入的C++程序的部分,后面会详细介绍。
ShowStopServer()是显示关闭服务器的功能的用户入口,就是一个HTML表单(我用到的HTML功能都是基于HTML4的),你需要学习一下HTML,不过只需要最基本的就可以了。
看完这个应该就明白了,所谓嵌入式web服务器,就是用C++代码生成HTML给浏览器而已。
上面的代码中注释掉的一部分是另一个功能的入口,"/shell.asp"是执行后台命令的接口,因为执行权限很大,所以一般隐藏入口。
二、查看文件
查看文件的入口代码:
else if("/ViewFile.asp"==m_request.GetResource()){char buf[2048];sprintf(buf,"查看文件 %s ",m_request.GetParam("file").c_str());OnPageStart(buf);doPageViewFile();OnPageEnd();m_s.Close();//所有此类页面都可能无法预先确定输出长度isKeepAlive=false;}
以“file”为查询参数,也就是类似这样的请求:“http://ip:port/ViewFile.asp?file=文件名”。
输出是HTML,OnPageStart和OnPageEnd处理HTML文件的开始和结束。主体部分则由doPageViewFile()提供:
bool doPageViewFile(){ifstream f;char * buf;long bufsize=1024*1024;long count,len,outcount;string file=m_request.GetParam("file");string autorefresh=m_request.GetParam("autorefresh");long start=atol(m_request.GetParam("start").c_str());long end=atol(m_request.GetParam("end").c_str());long origin_start=start;//原始startlong maxcount=atol(m_request.GetParam("maxcount").c_str());if(0==maxcount)maxcount=500;//默认值if(maxcount<0 || maxcount>50000)maxcount=50000;//最大行数限制while(true){if(NULL==(buf=new char[bufsize])){m_respond.AppendBody("内存不足<P>");break;}if(file.size()==0){m_respond.AppendBody("没有文件名<P>");break;}m_respond.AppendBody("文件名: ");m_respond.AppendBody(file);m_respond.AppendBody("<BR>");//打开文件f.open(file.c_str(),ios::in);if(!f.good()){m_respond.AppendBody("打开文件错误<P>");break;}//获取长度f.seekg(0,ios::end);len=f.tellg();if(end>0){sprintf(buf,"文件长度: %ld,开始位置:%ld,结束位置: %ld,请求的总长度:%ld<BR>------------------------------------------<P>\n",len,start,end,end-start);len=end;}else{sprintf(buf,"文件长度: %ld,开始位置:%ld,请求的总长度:%ld<BR>------------------------------------------<P>\n",len,start,len-start);}m_respond.AppendBody(buf);if(!m_respond.Flush(m_s))return true;//读取内容if(start<0)start=len+start;//负值代表从结束开始往前if(start<0)start=0;//如果还是负的说明参数错误f.seekg(start,ios::beg);outcount=0;count=0;while(f.good()){if(count>=maxcount)break;//超过输出量限制f.getline(buf,bufsize-1);buf[bufsize-1]='\0';m_respond.AppendBody(LogToHtml(buf,false,true));++count;if(count%100==0){m_respond.AppendBodyHtmlScroll();if(!m_respond.Flush(m_s))break;}outcount=f.tellg()-start;if(f.tellg()>=len){break;}}m_respond.Flush(m_s);f.close();m_respond.AppendBody("<P>------------------------------------------<P>");sprintf(buf,"剩余字节数: %ld",len-start-outcount);m_respond.AppendBody(buf);break;}if(!m_respond.Flush(m_s))return true;char buf2[10240];sprintf(buf2,"<FORM METHOD=\"GET\" name=\"form_view\">\n""<INPUT TYPE=\"hidden\" NAME=\"file\" VALUE=\"%s\" >\n""<INPUT TYPE=\"hidden\" NAME=\"autorefresh\" VALUE=\"%s\" >\n""起点(从0开始,负值代表从文件尾倒数):<INPUT TYPE=\"text\" SIZE=\"30\" NAME=\"start\" VALUE=\"%ld\"><BR>\n""终点(从0开始,负值代表从文件尾倒数):<INPUT TYPE=\"text\" SIZE=\"30\" NAME=\"end\" VALUE=\"%ld\"><BR>\n""最大输出行数: <INPUT TYPE=\"text\" SIZE=\"30\" NAME=\"maxcount\" VALUE=\"%ld\">\n""<INPUT TYPE=SUBMIT VALUE=\"执行\" >\n""</FORM>\n",file.c_str(),autorefresh.c_str(),(origin_start<0?origin_start:start+outcount),end,maxcount);m_respond.AppendBody(buf2);bool isAutoRefresh=(m_request.GetParam("autorefresh")=="true");sprintf(buf2,"<BUTTON NAME=\"autorefresh\" onclick=\"timer=setTimeout('window.form_view.submit()',10000);window.form_view.autorefresh.value='true';window.autorefresh.disabled=true;window.stoprefresh.disabled=false;\">自动刷新</BUTTON>\n""<button NAME=\"stoprefresh\" disabled onclick=\"clearTimeout(timer);window.form_view.autorefresh.value='';window.autorefresh.disabled=false;window.stoprefresh.disabled=true;\">停止刷新</BUTTON><BR>\n""%s<BR>\n",(isAutoRefresh?"<script language=\"JavaScript\">window.autorefresh.onclick()</script>":""));m_respond.AppendBody(buf2);if(!m_respond.Flush(m_s))return true;delete[] buf;return true;}
嘿嘿,现在明白为什么需要一个专门的功能了吧。这个功能支持指定开始位置和结束位置,并且可以自动刷新、自动滚动,还给文件内容增加了格式处理(LogToHtml()函数给日志根据类型上色)
三、关闭服务
默认页提供了关闭服务的用户入口,用户入口会传到这个页面来:
else if("/stopserver.asp"==m_request.GetResource()){OnPageStart("stop server",true);if(NULL!=pfCheckAdmin && !pfCheckAdmin(m_request.GetParam("password").c_str())){m_respond.AppendBody("口令错误");OnPageEnd();}else{(*pShutDown) = true;m_respond.AppendBody("收到停止信号,服务正在停止......");m_respond.Flush(m_s);OnPageEnd();}isKeepAlive=false;m_s.Close();}
首先检查管理员密码对不对(普通用户不能关闭服务器),然后返回“服务正在停止”页面给用户,最后退出服务程序。
四、下载页面
前面已经介绍过下载的实际功能代码,这是入口点:
else if("/DownFile.asp"==m_request.GetResource()){if(doPageFile(m_request.GetParam("file").c_str())){m_respond.Flush(s);}else{m_respond.Flush(s);m_s.Close();//所有此类页面都可能无法预先确定输出长度isKeepAlive=false;}}
也是以“file”为参数。实际功能入口和静态文件是一样的,区别是会设置头标指示是文件下载。
(这里是结束,但不是整个系列的结束)