编程实战:自己编写HTTP服务器(系列4:查看文件、下载等一般功能)

 系列入口:编程实战:自己编写HTTP服务器(系列1:概述和应答)-CSDN博客

        本文介绍各种功能的实现。大部分是特定内置入口。

目录

一、默认页

二、查看文件

三、关闭服务

四、下载页面


一、默认页

        前面在已经介绍过重定向,默认页可以通过重定向转到指定的页面,因为我这里的主要目的是提供嵌入功能,所以提供了一个内置的默认页:

编程实战:自己编写HTTP服务器(系列4:查看文件、下载等一般功能)_第1张图片

        如果资源名称是这“/default.asp”或“/default.htm”,就执行默认功能,HTML页面的框架已经用OnPageStart和OnPageEnd构建,专用的代码放在doPageDefault里面:

		bool ShowStopServer()
		{
			string str;

			//str="

Shell

\n"; //m_respond.AppendBody(str); str="

\n" "关闭服务器需要口令:
\n" "\n" "
\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;//原始start
			long 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("内存不足

"); break; } if(file.size()==0) { m_respond.AppendBody("没有文件名

"); break; } m_respond.AppendBody("文件名: "); m_respond.AppendBody(file); m_respond.AppendBody("
"); //打开文件 f.open(file.c_str(),ios::in); if(!f.good()) { m_respond.AppendBody("打开文件错误

"); break; } //获取长度 f.seekg(0,ios::end); len=f.tellg(); if(end>0) { sprintf(buf,"文件长度: %ld,开始位置:%ld,结束位置: %ld,请求的总长度:%ld
------------------------------------------

\n",len,start,end,end-start); len=end; } else { sprintf(buf,"文件长度: %ld,开始位置:%ld,请求的总长度:%ld
------------------------------------------

\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("

------------------------------------------

"); sprintf(buf,"剩余字节数: %ld",len-start-outcount); m_respond.AppendBody(buf); break; } if(!m_respond.Flush(m_s))return true; char buf2[10240]; sprintf(buf2, "

\n" "\n" "\n" "起点(从0开始,负值代表从文件尾倒数):
\n" "终点(从0开始,负值代表从文件尾倒数):
\n" "最大输出行数: \n" "\n" "
\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, "\n" "
\n" "%s
\n" ,(isAutoRefresh?"":"")); 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”为参数。实际功能入口和静态文件是一样的,区别是会设置头标指示是文件下载。

(这里是结束,但不是整个系列的结束)

你可能感兴趣的:(C++嵌入式HTTP服务器,服务器,HTTP,嵌入式)