自己开发简单web服务器一(C++开源库websocketpp实现)

简要

Web服务器主要处理的是HTTP请求(这里忽略HTTPS),HTTP协议建立在TCP上。如果自己实现,无非就是网络编程(socket接受、发送),数据解析(HTTP字段解析),返回HTTP协议字符串给客户端等。说起来简单,要做到跨平台和高效,不得不介绍几个有名的开源库。

Websocketpp,开源跨平台C++ web库,网络请求使用boost::asio实现(Windows上是IOCP完成端口),字符串请求构造都在hpp中实现,没有一个cpp文件,全部内联保证了代码执行的效率(IOCP的处理效率也是杠杠的,不知道百度去)。

具体介绍可见以前的一篇说明:http://blog.csdn.net/mfcing/article/details/50118591

Web服务器主要处理客户端的http请求(GET\POST),这里主要介绍页面请求、文件请求等基本的功能。

基本配置

设置工作线程数目,理论上线程数目越多处理请求越高效,实际中还要考虑线程间切换的问题,所以通常是线程数乘以2(用过IOCP完成端口的都知道这个)

size_t num_threads = 4;//使用4个线程来处理web请求
监听80端口,处理Web请求

testee_server.listen(port);

设置回调函数,所有的客户端请求都会进入这个回调函数中,回调函数在工作线程中执行

testee_server.set_http_handler(bind(&on_http, &testee_server, ::_1));

回调函数处理,一定要加上try.catch,对于Websocketpp中跑出的异常进行捕捉,避免程序崩溃退出。

void on_http(server* s, websocketpp::connection_hdl hdl)
{
	try
	{
		server::connection_ptr con = s->get_con_from_hdl(hdl);
		websocketpp::http::parser::request rt = con->get_request();
		const string& strUri = rt.get_uri();
		const string& strMethod = rt.get_method();
		const string& strBody = rt.get_body();	//只针对post时有数据
		const string& strHost = rt.get_header("host");
		const string& strVersion = rt.get_version();
		std::cout<<"接收到一个"<<strMethod.c_str()<<"请求:"<<strUri.c_str()<<"  线程ID="<<::GetCurrentThreadId()<<"  host = "<<strHost<<std::endl;
		if (strMethod.compare("POST") == 0)
		{//对于post提交的,直接返回跳转
			//websocketpp::http::parser::request r;
			con->set_status(websocketpp::http::status_code::value(websocketpp::http::status_code::found));
			con->append_header("location", "http://blog.csdn.net/mfcing");
		}
		else if (strMethod.compare("GET") == 0)
		{
			if (strUri.compare("/") == 0)
			{//请求主页
				con->set_body("Hello WebsocketPP!");
				con->set_status(websocketpp::http::status_code::value(websocketpp::http::status_code::ok));
			}
			else if (strUri.compare("/favicon.ico") == 0)
			{//请求网站图标,读取图标文件,直接返回二进制数据
				static const string strIcoPath = g_strRunPath + "server\\Update.ico";
				string strBuffer;
				if (ReadFileContent(strIcoPath.c_str(), "rb", strBuffer))
				{
					con->set_body(strBuffer);
				}
				con->set_status(websocketpp::http::status_code::value(websocketpp::http::status_code::ok));//HTTP返回码 OK
			}
			else if (strUri.compare("/test.html") == 0)
			{//请求一个页面,读取这个页面文件内容然后返回给浏览器
				static const string strHtmlPath = g_strRunPath + "server\\test.htm";
				string strBuffer;
				int code = websocketpp::http::status_code::ok;
				if (ReadFileContent(strHtmlPath.c_str(), "r", strBuffer))
				{
					con->set_body(strBuffer);
				}
				else
				{//页面不存在,返回404
					code = websocketpp::http::status_code::not_found;
					con->set_body("<html><body><div style=\"color:#F000FF;font:14px;font-family: 'Microsoft YaHei';\">温馨提示:</div><div style=\"padding-left: 80px;color:#333333;font:14px;font-family: 'Microsoft YaHei';\">页面被外星人带走啦!</div></body></html>");
				}
				con->set_status(websocketpp::http::status_code::value(code));//HTTP返回码
			}
			else if (strUri.compare("/server/test.jpg") == 0)
			{//上面的页面的HTML中配置了一张图片,因此浏览器回来服务器请求这张图
				static const string strImgPath = g_strRunPath + "server\\test.jpg";
				string strBuffer;
				int code = websocketpp::http::status_code::ok;
				if (ReadFileContent(strImgPath.c_str(), "rb", strBuffer))
				{
					con->set_body(strBuffer);
				}
				else
				{//页面不存在,返回404
					code = websocketpp::http::status_code::not_found;
				}
				con->set_status(websocketpp::http::status_code::value(code));//HTTP返回码
			}
			else
			{//其他未定义的页面,返回跳转
				con->set_status(websocketpp::http::status_code::value(websocketpp::http::status_code::found));
				con->append_header("location", "http://blog.csdn.net/mfcing");
			}
		}
	}
	catch (websocketpp::exception const & e) 
	{
		std::cout << "exception: " << e.what() << std::endl;
	}
	catch(std::exception &e)
	{
		std::cout << "exception: " << e.what() << std::endl;
	}
	catch(...)
	{

	}
}

Web请求处理

首先是通过strMethod获取请求方式,POST请求主要用于提交数据,这里不作特别处理,仅仅是让浏览器跳转到我的博客首页中。HTTP协议得了解一点,返回的code=302时是跳转(enum value found = 302),跳转时需要带上location字段,指定你要跳转到哪去。

con->set_status(websocketpp::http::status_code::value(websocketpp::http::status_code::found));
			con->append_header("location", "http://blog.csdn.net/mfcing");

浏览器请求都是GET,我们重点关注的是这个。

请求主页

服务器开启后,在80端口上监听客户端的http请求。我们在浏览器上输入:127.0.0.1回车时,浏览器首先请求主页信息,然后请求页面的图标。

strUri是获取请求页面的内容

const string& strUri = rt.get_uri();
请求主页时,即strUri="/";请求图标时:strUri="/",/favicon.ico。

con->set_body("Hello WebsocketPP!");用于设置返回给Web客户端的内容,返回的内容可以是html文本,也可以是ico图标的二进制数据,还可以是jpg图片的二进制数据。这时候就需要使用http协议的Content-Type字段来告诉客户端,你返回的内容是那种形式的了。具体的文件格式对应表可以参考百度百科:http://baike.baidu.com/link?url=Xr5CO-N0S7yMEv5Hh3YcJugUk9zQsCvlhfvkTIh-dHBbN2hNFxDxlv4WRg6ceayMSvvwpew2npuH9MnLATj4Ki3HB-xA2fYf_OOmwH4TEGO

请求主页时,仅仅返回一段文字:Hello WebsocketPP!,浏览器会显示这段文字。请求图标时,读取本地的一个ico文件,返回二进制字符串,浏览器将会在标签上显示出这个图标。

请求html页面

请求页面http://127.0.0.1/test.html时,读取本地配置的一个html文件内容并返回,html文件内容如下

<html>
<head>
</head>
<body>
    <p style="align-items:center;">
    <label style="font:20px;font-family:'Microsoft YaHei';color:gold;align-self:center;text-align:center">This is the test page</label>
        </p>
    <p>
        <img src="http://127.0.0.1/server/test.jpg"/>
        </p>
    <div style="padding-left: 80px; font: 14px; font-family: 'Microsoft YaHei';font-weight:bold; color: #0094ff">打赏下
        </div>
    <div>
        <img src="https://img-blog.csdn.net/20161102162003256?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" width="200" height="200" />
        </div>
</body>
</html>


请求图片资源

里面配置了一张本地图片
<span style="font-size:14px;"><img src="http://127.0.0.1/server/test.jpg"/></span>

浏览器解析这个html后会接着请求服务器的这张图,也就是收到/server/test.jpg的请求,我们读取本地图片,返回二进制字符串给浏览器后,浏览器将会显示出这张图。

客户端请求的页面不存在时,我们可以返回404告知not_found = 404。

浏览器解析HTML展示图片后


注意事项

注意一点:对于所有的请求,一定要设置HTTP头,告知浏览器请求的结果(不存在:404,正常:200,跳转302……),否则浏览器会认为服务器不鸟他,显示打开页面失败。

set_status(websocketpp::http::status_code::value(websocketpp::http::status_code::ok));



demo程序下载

http://download.csdn.net/detail/mfcing/9673333



  • 5
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值