cpp-netlib笔记二:实现http文件服务功能

这篇博客介绍了如何使用cpp-netlib库构建一个简单的HTTP文件服务。通过展示主程序`main.cpp`和依赖的源文件,如`fileHandler.hpp/cpp`、`mime_types.hpp/cpp`及`LocalFileReadManager.h/cpp`,博主详细讲解了配置头文件和库文件搜索路径的过程,并提供了测试项目的Debug模式路径。测试中使用了`a.html`作为示例文件。
Title:在cpp-netlib基础上实现http文件服务功能
Author:Kagula
Date:2017-06-01
Environment:
[1]Boost 1.64
本机安装目录 D:\SDK\boost_1_64_0
[2]Windows10、VS2017 Update2
[3]OpenCL 1.0.2k
本机安装目录 D:\SDK\OpenSSL_1_0_2k_vc2007
[4]CMake 3.8.1
[5]cpp-netlib-0.12.0-final
本机安装目录 D:\SDK\cpp-netlib-0.12.0-final
[6]Python 3.6.1
[7]ActivePerl 5.16


Introduction:

   在hello world server例子上增加了文件服务功能,直接贴代码。
  
测试

测试效果图


a.html

<h1>测试测试<h1>
<img src="/temp/22.jpg"  alt=""/>

主程序的源代码

测试项目的Debug模式头文件搜索路径

D:\SDK\cpp-netlib-0.12.0-final\deps\asio\asio\include;D:\SDK\cpp-netlib-0.12.0-final;D:\SDK\boost_1_64_0;

测试项目的Debug模式库文件搜索路径

D:\SDK\cpp-netlib-0.12.0-final\vs2017\libs\network\src\Debug;D:\SDK\boost_1_64_0\vc2017\lib;


main.cpp

#include <boost/network/protocol/http/server.hpp>
#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <vector>

#include "HttpProcess\fileHandler.hpp"

namespace http = boost::network::http;
using namespace std;

struct hello_world;
typedef http::server<hello_world> server;

#ifdef _WIN32
#pragma comment(lib, "cppnetlib-server-parsers.lib")
#endif

struct hello_world {
	void operator()(server::request const &request, server::connection_ptr connection) {
		server::string_type ip = source(request);
		unsigned int port = request.source_port;

		string url = request.destination;//什么都不跟为"/",否则"/mypath".
		string method = request.method;//可能的值"GET"

		string content_type, content_length;
		for (const auto& header : request.headers) {
			if (header.name == "Content-Type") content_type = header.value;
			if (header.name == "Content-Length") content_length = header.value;
			if (!content_type.empty() && !content_length.empty()) break;
		}

		server::response_header headers[] = { { "Connection", "close" },
		{ "Content-Type", "	image/jpeg" },
		{ "Content-Length", "0" } };

		
		if (content_type.empty() && 
			content_length.empty())// && url.find_last_of('.') != std::string::npos
		{
			std::string contentType;
			std::string content;
			if (GetFile(url,contentType,content)==0)
			{
				//成功返回文件
				headers[1].value = contentType;
				headers[2].value = std::to_string(content.size());
				connection->set_status(server::connection::ok);
				connection->set_headers(boost::make_iterator_range(headers, headers + 2));
				connection->write(content);

				return;
			}
		}

		//提示文件没有找到
		std::string strError = "404 File Not Found!";
		headers[1].value = "text/plain";
		headers[2].value = std::to_string(strError.size());
		connection->set_status(server::connection::not_found);
		connection->set_headers(boost::make_iterator_range(headers, headers + 2));
		connection->write(strError);
	}
};

int main(int argc, char** argv)
{
	cout << "测试cpp-netlib文件服务实现!" << endl;

	if (argc != 3) {
		std::cerr << "Usage: " << argv[0] << " address port" << std::endl;
		return 1; 
	}
	std::cout << "IP:" << argv[1] << ",Port:" << argv[2] << std::endl;

	try {
		/*<< Creates the request handler. >>*/
		hello_world handler;
		/*<< Creates the server. >>*/
		server::options options(handler);
		server server_(options.address(argv[1]).port(argv[2]));
		/*<< Runs the server. >>*/
		server_.run();
	}
	catch (std::exception &e) {
		std::cerr << e.what() << std::endl;
		return 1;
	}

	return 0;
}



依赖的源文件

共有六个源文件具体如下:

fileHander.hpp

#ifndef  _FILEHANDLER_H_
#define _FILEHANDLER_H_

#include <string>

int GetFile(const std::string &path, std::string &contentType,std::string &content);

#endif //  _FILEHANDLER_H_


fileHander.cpp

#include "fileHandler.hpp"
#include "mime_types.hpp"

#include "LocalFileReadManager.h"

#include <boost/filesystem.hpp>  

const char g_documentRoot[] = { "d:" };//因为是做测试,先随便填一个路径。
kagula::LocalFileReadManager g_localFileReadManager;

int GetFile(const std::string &path, std::string &contentType, std::string &content)
{
	std::string fullPath(g_documentRoot);
	fullPath.append(path);

	//是不是支持的Minetype?
	boost::filesystem::path dir(fullPath);
	std::string extension = dir.extension().string();
	if (extension.size() > 0)
		extension = extension.substr(1,extension.size()-1);//去掉“.”符号。
	contentType = extension_to_type(extension);
	if (contentType.length()<1)
	{
		return -1;
	}

	//读文件
	if (g_localFileReadManager.getFileContent(fullPath,content) == false)
	{
		return -3;
	}

	return 0;
}


mime_types.hpp

//
// mime_types.hpp
// ~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#ifndef HTTP_SERVER4_MIME_TYPES_HPP
#define HTTP_SERVER4_MIME_TYPES_HPP

#include <string>

/// Convert a file extension into a MIME type.
const char* extension_to_type(const std::string& extension);

#endif // HTTP_SERVER4_MIME_TYPES_HPP


mime_types.cpp

//
// mime_types.cpp
// ~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#include "mime_types.hpp"

struct mapping
{
  const char* extension;
  const char* mime_type;
} mappings[] =
{
  { "gif", "image/gif" },
  { "htm", "text/html" },
  { "html","text/html" },
  { "jpg", "image/jpeg"},
  { "png", "image/png" },
  { "css", "text/css"  },
  { "json","text/json" },
  { "txt", "text/plain" },
  { 0, 0 } // Marks end of list.
};

const char* extension_to_type(const std::string& extension)
{
  for (mapping* m = mappings; m->extension; ++m)
  {
    if (m->extension == extension)
    {
      return m->mime_type;
    }
  }

  return "";
}


LocalFileReadManager.h

#ifndef _LocalFileRWManager_H_
#define _LocalFileRWManager_H_

#include <boost/thread/mutex.hpp>

#include <string>
#include <map>

/*
Title: 文件内容读取管理
Purpose: 把常用的文件缓存在内存里,
免得下次又要重新读一遍,主要用在编写http服务端程序中。
Author: kagula
Date: 2016-12-28
Environment:
[1]VS2013 Update5
Note:
为了提高读文件速度,以后考虑不检查文件的最后修改时间。
*/

namespace kagula
{
	struct LocalFileInfo
	{
		std::string content;
		unsigned long long timeStamp;
	};

	class LocalFileReadManager
	{
	public:
		bool getFileContent(const std::string &fullPath, std::string &content);
	protected:
		unsigned long long _sizeBuffer = 0;
		const unsigned long long _sizeMAXBuffer = 64 * 1024 * 1024;//64MB
		std::map<std::string, LocalFileInfo> _mapFile;

		bool _getFileContent(const std::string &full_path, std::string &content);
		void addFile2Buffer(const std::string &full_path, const std::string &content, const long long timeStamp);

		boost::mutex _mutexRW;
	};
}

#endif


LocalFileReadManager.cpp

#include "LocalFileReadManager.h"

#include <boost/thread/locks.hpp>

#include <istream>
#include <fstream>
#include <sys/types.h>
#include <sys/stat.h>

namespace kagula
{
	bool GetFileInfo(const std::string &full_path, unsigned long long &fileSize, unsigned long long &timeStamp)
	{
		struct stat buf;
		int result;
		result = stat(full_path.c_str(), &buf);

		if (result != 0)
			return false;

		fileSize = buf.st_size;
		timeStamp = buf.st_mtime;
		return true;
	}//function

	bool LocalFileReadManager::getFileContent(const std::string &fullPath, std::string &content)
	{
		unsigned long long fileSize;
		unsigned long long timeStamp;
		if (!GetFileInfo(fullPath, fileSize, timeStamp))
			return false;

		{
			boost::lock_guard<boost::mutex> l(_mutexRW);

			if (_mapFile.find(fullPath) != _mapFile.end())
			{
				if (timeStamp == _mapFile[fullPath].timeStamp)
				{
					content = _mapFile[fullPath].content;
					return true;
				}//if
			}//if
		}

		content.clear();
		if (!_getFileContent(fullPath, content))
			return false;

		//Not cache big file.
		if (fileSize > _sizeMAXBuffer / 10)
			return true;

		addFile2Buffer(fullPath, content, timeStamp);
		return true;
	}//function

	bool LocalFileReadManager::_getFileContent(const std::string &full_path, std::string &content)
	{
		std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary);
		if (!is)
		{
			return false;
		}//if

		char buf[4 * 1024];
		while (is.read(buf, sizeof(buf)).gcount() > 0)
			content.append(buf, is.gcount());
		return true;
	}//function

	void LocalFileReadManager::addFile2Buffer(const std::string &full_path, const std::string &content, const long long timeStamp)
	{
		boost::lock_guard<boost::mutex> l(_mutexRW);

		//
		while (_sizeBuffer + content.size() > _sizeMAXBuffer)
		{
			std::map<std::string, LocalFileInfo>::iterator iter = _mapFile.begin();
			_sizeBuffer -= iter->second.content.size();
			_mapFile.erase(iter);
		}//while

		 //
		_mapFile[full_path].content = content;
		_mapFile[full_path].timeStamp = timeStamp;
		_sizeBuffer += content.size();
	}//function
}//namespace


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kagula086

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值