我是靠谱客的博主 有魅力小海豚,最近开发中收集的这篇文章主要介绍基于libevent的http server,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

需求:实现http server,提供rest接口

实现:使用libevent实现http server,采用多线程处理,每个线程对应一个event base

代码:

1.socket_config.hpp

#ifndef SRC_SOCKET_CONFIG_HPP_
#define SRC_SOCKET_CONFIG_HPP_
#include <string>
using namespace std;
enum {
	TCP,
	UDP
};
class socket_config {
public:
	socket_config() {
		port_ = 7780;
		backlog_ = 1024;
		type_ = UDP;
	}
	virtual ~socket_config() = default;
public:
	int port_;
	int backlog_;
	unsigned char type_;
};

#endif /* SRC_SOCKET_CONFIG_HPP_ */

2.sockect_utility.hpp

#ifndef SRC_SOCKET_UTILITY_HPP_
#define SRC_SOCKET_UTILITY_HPP_
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
#include "socket_config.hpp"
class sockect_utility {
public:
	inline static sockect_utility &get_instance() {
		static sockect_utility obj;
		return obj;
	}
private:
	sockect_utility() = default;
	virtual ~sockect_utility() = default;
public:
	int bind_socket(const socket_config &config) {
		int sock_fd = init_socket(config.type_);
		if (-1 == sock_fd) {
			return -1;
		}
		struct sockaddr_in addr = {0};
		addr.sin_family = AF_INET;
		addr.sin_addr.s_addr = htonl(INADDR_ANY);
		addr.sin_port = htons(config.port_);
		if (bind(sock_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
			cerr << "socket bind failed on port = " << config.port_ << endl;
			return -1;
		}
		if (TCP == config.type_) {
			if (listen(sock_fd, config.backlog_) < 0) {
				cerr << "socket listen failed on backlog = " << config.backlog_ << endl;
				return -1;
			}
		}
		int flag = 0;
		flag = fcntl(sock_fd, F_GETFL, 0);
		if (flag < 0) {
			cerr << "fcntl F_GETFL on socket = " << sock_fd << " failed...!" << endl;
			return -1;
		}
		if (fcntl(sock_fd, F_SETFL, flag | O_NONBLOCK) < 0) {
			cerr << "fcntl F_SETFL non block on socket = " << sock_fd << " failed...!" << endl;
			return -1;
		}
		return sock_fd;
	}
	inline bool set_sock_broadcast(int sock_fd) {
		if (sock_fd < 0) {
			return false;
		}
		int so_broadcast = 1;
		return setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (char *)&so_broadcast, sizeof(so_broadcast)) >= 0;
	}
	bool close_socket(int &sock_fd) {
		if (sock_fd >= 0) {
			close(sock_fd);
			sock_fd = -1;
			return true;
		}
		return false;
	}
private:
	int init_socket(unsigned char type) {
		int sock_fd = -1;
		switch (type) {
		case UDP:
			sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
			if (false == set_sock_broadcast(sock_fd)) {
				return -1;
			}
			return sock_fd;
			break;
		case TCP:
			sock_fd = socket(AF_INET, SOCK_STREAM, 0);
			break;
		default:
			cerr << "unknown socket type...!" << endl;
			return -1;
		}
		if (sock_fd < 0) {
			cerr << "socket fd create failed...!" << endl;
			return -1;
		}
		int on = 1;
		if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) {
			cerr << "set socket to resue failed...!" << endl;
			return -1;
		}
		if (setsockopt(sock_fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0) {
			cerr << "set socket to keep alive failed...!" << endl;
			return -1;
		}
		return sock_fd;
	}
};

#endif /* SRC_SOCKET_UTILITY_HPP_ */

3.url_mapper.hpp

#ifndef SRC_URL_MAPPER_HPP_
#define SRC_URL_MAPPER_HPP_
#include <string>
#include <unordered_map>
using namespace std;
enum  url_type {
	ZEG_ROBOT_LOCATION,
	UNKNOWN_ZEG_ROBOT_TYPE
};
class url_mapper {
public:
	inline static url_mapper &get() {
		static url_mapper mapper;
		return mapper;
	}
	url_type get_type(const string &url) {
		string tmp;
		auto pos = url.find('?');
		if (pos != string::npos) {
			tmp.assign(url, 0, pos);
		}
		else {
			tmp = url;
		}
		auto it = mapper_.find(tmp);
		if (end(mapper_) == it) {
			return UNKNOWN_ZEG_ROBOT_TYPE;
		}
		return it->second;
	}
private:
	url_mapper() {
		mapper_.insert(make_pair("/robot/location", ZEG_ROBOT_LOCATION));
	}
	virtual ~url_mapper() = default;
private:
	unordered_map<string, url_type>mapper_;			// key - url
};

#endif /* SRC_URL_MAPPER_HPP_ */

4.http_key.hpp

#ifndef SRC_HTTP_KEY_HPP_
#define SRC_HTTP_KEY_HPP_
#include "url_mapper.hpp"
enum rest_type {
	GET,
	POST,
	PUT,
	DELETE,
	UNKNOWN_REST_TYPE
};
class http_key {
public:
	explicit http_key(rest_type a, url_type b) : rest_type_(a), url_type_(b) {
	}
	virtual ~http_key() = default;
public:
	rest_type rest_type_;
	url_type url_type_;
};
class http_key_cmp {
public:
	inline bool operator () (const http_key &key0, const http_key &key1) {
		if (key0.rest_type_ !=  key1.rest_type_) {
			return key0.rest_type_ < key1.rest_type_;
		}
		return key0.url_type_ < key1.url_type_;
	}
};

#endif /* SRC_HTTP_KEY_HPP_ */

5.http_config.hpp

#ifndef SRC_HTTP_CONFIG_HPP_
#define SRC_HTTP_CONFIG_HPP_
#include "http_key.hpp"
class http_config {
public:
	http_config() {
		url_port = 0;
		response_code = 200;
		rest_type_ = UNKNOWN_REST_TYPE;
		url_type_ = UNKNOWN_ZEG_ROBOT_TYPE;
	}
	virtual ~http_config() = default;
public:
	rest_type rest_type_;
	url_type url_type_;
	int url_port;
	long response_code;
	string url;
	string url_ip;
	string url_path;
	string request_body;
	string response_body;
};

#endif /* SRC_HTTP_CONFIG_HPP_ */

6.response_handler.hpp

#ifndef SRC_RESPONSE_HANDLER_HPP_
#define SRC_RESPONSE_HANDLER_HPP_
#include "http_config.hpp"
enum http_response_code {
	HTTP_200 = 200,
	HTTP_201,
	HTTP_404 = 404,
	HTTP_501 = 501,
	HTTP_502 = 502
};
class response_handler {
public:
	response_handler() = default;
	virtual ~response_handler() = default;
	virtual void handle_request(http_config &) = 0;
};

#endif /* SRC_RESPONSE_HANDLER_HPP_ */

7.http_utility.hpp

#ifndef SRC_HTTP_UTILITY_HPP_
#define SRC_HTTP_UTILITY_HPP_
#include <map>
#include "event.h"
#include "evhttp.h"
#include "event2/listener.h"
#include "response_handler.hpp"
class http_utility {
public:
	static inline http_utility &get() {
		static http_utility obj;
		return obj;
	}
	inline void set_http_utility(struct evhttp_request *req, http_config &config) {
		http_req_ = req;
		if (nullptr == http_req_) {
			return;
		}
		get_rest_type(config);
		get_url(config);
		get_url_type(config);
		get_request_body(config);
	}
	inline void registe_handler(const http_key &key, response_handler *handler) {
		mapper_.insert(make_pair(key, handler));
	}
	void get_rest_type(http_config &config) {
		switch (evhttp_request_get_command(http_req_)) {
		case EVHTTP_REQ_GET:
			config.rest_type_ =  GET;
			break;
		case EVHTTP_REQ_POST:
			config.rest_type_ =  POST;
			break;
		case EVHTTP_REQ_PUT:
			config.rest_type_ =  PUT;
			break;
		case EVHTTP_REQ_DELETE:
			config.rest_type_ =  DELETE;
			break;
		default:
			config.rest_type_ = UNKNOWN_REST_TYPE;
		}
	}
	void get_url(http_config &config) {
		const char *p = evhttp_request_uri(http_req_);
		if (nullptr == p) {
			return;
		}
		config.url = p;
		struct evhttp_uri *decoded = evhttp_uri_parse(p);
		if (nullptr == decoded) {
			return;
		}
		const char *path = evhttp_uri_get_path(decoded);
		if (nullptr == path || 0 == path[0]) {
			config.url_path = "/";
		}
		else {
			config.url_path = path;
		}
		evhttp_uri_free(decoded);
	}
	inline void get_url_type(http_config &config) {
		config.url_type_ = url_mapper::get().get_type(config.url);
	}
	inline void get_request_body(http_config &config) {
		const char *body = (const char *)EVBUFFER_DATA(http_req_->input_buffer);
		if (body != nullptr) {
			config.request_body = body;
		}
	}
	void handle_request(http_config &config) {
		http_key httpKey(config.rest_type_, config.url_type_);
		auto it = mapper_.find(httpKey);
		if (end(mapper_) == it) {
			return;
		}
		it->second->handle_request(config);
	}
	void reply_client(http_config &config) {
		struct evbuffer *buf = evbuffer_new();
		evhttp_add_header(http_req_->output_headers, "Content-Type", "application/json;charset=UTF-8");
		evbuffer_add_printf(buf, "%s", config.response_body.c_str());
		evhttp_send_reply(http_req_, config.response_code, "return", buf);
		evbuffer_free(buf);
	}
	void handle_http(http_config &config) {
		handle_request(config);
		reply_client(config);
	}
private:
	http_utility() {
		http_req_ = nullptr;
	}
	virtual ~http_utility() {
		http_req_ = nullptr;
	}
private:
	struct evhttp_request *http_req_;
	map<http_key, response_handler *, http_key_cmp>mapper_;
};

#endif /* SRC_HTTP_UTILITY_HPP_ */

8.http_thread.hpp

#ifndef SRC_HTTP_THREAD_HPP_
#define SRC_HTTP_THREAD_HPP_
#include "socket_utility.hpp"
#include "http_utility.hpp"
class http_thread {
public:
	http_thread() {
		sock_fd = -1;
		event_base_ptr_ = nullptr;
		event_http_ptr_ = nullptr;
	}
	http_thread(const http_thread &other) {
		if (&other != this) {
			sock_fd = other.sock_fd;
			event_base_ptr_ = other.event_base_ptr_;
			event_http_ptr_ = other.event_http_ptr_;
		}
	}
	http_thread & operator = (const http_thread &other) {
		if (&other != this) {
			sock_fd = other.sock_fd;
			event_base_ptr_ = other.event_base_ptr_;
			event_http_ptr_ = other.event_http_ptr_;
		}
		return *this;
	}
	~http_thread() {
		if (nullptr != event_base_ptr_) {
			event_base_loopexit(event_base_ptr_, nullptr);
		}
		if (nullptr != event_http_ptr_)
		{
			evhttp_free(event_http_ptr_);
			event_http_ptr_ = nullptr;
		}
		if (nullptr != event_base_ptr_) {
			event_base_free(event_base_ptr_);
			event_base_ptr_ = nullptr;
		}
	}
	bool init(int fd) {
		sock_fd = fd;
		if (sock_fd < 0) {
			return false;
		}
		event_base_ptr_ = event_base_new();
		if (nullptr == event_base_ptr_) {
			return false;
		}
		event_http_ptr_ = evhttp_new(event_base_ptr_);
		if (nullptr == event_http_ptr_) {
			return false;
		}
		if (evhttp_accept_socket(event_http_ptr_, sock_fd)) {
			return false;
		}
		evhttp_set_gencb(event_http_ptr_, &http_thread::http_call_back_fun, nullptr);
		return true;
	}
	inline void http_dispath_event() {
		if (event_base_ptr_ != nullptr) {
			event_base_dispatch(event_base_ptr_);
		}
	}
private:
	static void http_call_back_fun(struct evhttp_request *req, void *arg) {
		http_config config;
		http_utility::get().set_http_utility(req, config);
		http_utility::get().handle_http(config);
	}
private:
	int sock_fd;
	event_base *event_base_ptr_;
	evhttp *event_http_ptr_;
};

#endif /* SRC_HTTP_THREAD_HPP_ */

9.http_server.hpp

#ifndef SRC_HTTP_SERVER_HPP_
#define SRC_HTTP_SERVER_HPP_
#include <vector>
#include <thread>
#include "http_thread.hpp"
class http_server {
public:
	http_server() = default;
	~http_server() {
		for (auto &it : threads_) {
			if (it.joinable()) {
				it.join();
			}
		}
	}
	bool init(const socket_config &config, int network_size = thread::hardware_concurrency()) {
		if (network_size <= 0 || network_size > NETWORKCAPACITY) {
			return false;
		}
		int sock_fd = sockect_utility::get_instance().bind_socket(config);
		if (sock_fd < 0) {
			return false;
		}
		http_threads_.resize(network_size);
		for (auto &it : http_threads_) {
			if (!it.init(sock_fd)) {
				return false;
			}
		}
		threads_.resize(network_size);
		for (int i = 0;i < network_size;i++) {
			try {
				threads_[i] = thread(bind(&http_server::start_http_thread, this, i));
			}
			catch (...) {
				return false;
			}
		}
		return true;
	}
private:
	inline void start_http_thread(int index) {
		http_threads_[index].http_dispath_event();
	}
private:
	static const int NETWORKCAPACITY = 128;
private:
	vector<http_thread>http_threads_;
	vector<thread>threads_;
};




#endif /* SRC_HTTP_SERVER_HPP_ */

10.zeg_robot_location_query_response_handler.hpp

#ifndef SRC_ZEG_ROBOT_LOCATION_QUERY_RESPONSE_HANDLER_HPP_
#define SRC_ZEG_ROBOT_LOCATION_QUERY_RESPONSE_HANDLER_HPP_
#include "response_handler.hpp"
namespace zeg_robot_scheduler_communication {
class zeg_robot_location_query_response_handler: public response_handler
{
public:
	zeg_robot_location_query_response_handler() = default;
	virtual ~zeg_robot_location_query_response_handler() = default;
	virtual void handle_request(http_config &config) override {
		string str = "{"id":"007","pointId":"uuid"}";
		config.response_body = str;
		config.response_code = HTTP_200;
	}
};
}

#endif /* SRC_ZEG_ROBOT_LOCATION_QUERY_RESPONSE_HANDLER_HPP_ */

11.zeg_robot_response_handler_info.hpp

#ifndef SRC_ZEG_ROBOT_RESPONSE_HANDLER_INFO_HPP_
#define SRC_ZEG_ROBOT_RESPONSE_HANDLER_INFO_HPP_
#include "http_utility.hpp"
#include "zeg_robot_location_query_response_handler.hpp"
namespace zeg_robot_scheduler_communication {
class zeg_robot_response_handler_info {
public:
	static inline zeg_robot_response_handler_info &get() {
		static zeg_robot_response_handler_info obj;
		return obj;
	}
	void init() {
		init_get_robot_location_handler();
	}
private:
	zeg_robot_response_handler_info() = default;
	virtual ~zeg_robot_response_handler_info() = default;
private:
	void init_get_robot_location_handler() {
		http_key key(GET, ZEG_ROBOT_LOCATION);
		http_utility::get().registe_handler(key, &robot_location_query_handler);
	}
private:
	zeg_robot_location_query_response_handler robot_location_query_handler;
};
}

#endif /* SRC_ZEG_ROBOT_RESPONSE_HANDLER_INFO_HPP_ */

12.main.cpp

#include "zeg_robot_response_handler_info.hpp"
#include "http_server.hpp"
using namespace zeg_robot_scheduler_communication;
http_server G_HTTP_SERVER;

bool start_http_server() {
	zeg_robot_response_handler_info::get().init();
	socket_config config;
	config.port_ = 8888;
	config.type_ = TCP;
	if (false == G_HTTP_SERVER.init(config)) {
		cerr << "http server init failed...!";
		return false;
	}
	return true;
}
int main() {
	if (false == start_http_server()) {
		return -1;
	}

	return 0;
}

 

最后

以上就是有魅力小海豚为你收集整理的基于libevent的http server的全部内容,希望文章能够帮你解决基于libevent的http server所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(72)

评论列表共有 0 条评论

立即
投稿
返回
顶部