概述
需求:实现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所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复