【小沐学C++】C++实现Web服务器(libevent,libcurl,libuv,poco)

🍺Web服务器系列相关文章编写如下🍺:

  1. 🎈【Web开发】Node.js实现Web服务器(http模块)🎈
  2. 🎈【Web开发】Node.js实现Web服务器(express模块)🎈
  3. 🎈【Web开发】Python实现Web服务器(Flask入门)🎈
  4. 🎈【Web开发】Python实现Web服务器(Flask测试)🎈
  5. 🎈【Web开发】Python实现Web服务器(Tornado入门)🎈
  6. 🎈【Web开发】Python实现Web服务器(Tornado+flask+nginx)🎈
  7. 🎈【Web开发】Python实现Web服务器(FastAPI)🎈
  8. 🎈【Web开发】Android手机上基于Termux实现Web服务器(Python、node.js)🎈
  9. 🎈【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)🎈

文章目录

  • 1、简介
    • 1.1 libevent
    • 1.2 libcurl
    • 1.3 openssl
    • 1.4 cJSON
  • 2、libevent
    • 2.1 下载
    • 2.2 VS2017编译
    • 2.3 VS2008编译
    • 2.4 代码测试(http服务端)
    • 2.5 代码测试(http客户端)
  • 3、libcurl
    • 3.1 下载
    • 3.2 编译
    • 3.3 代码测试(http客户端)
    • 3.4 命令行
  • 4、poco
    • 4.1 下载
    • 4.2 编译
    • 4.3 代码测试(http客户端)
    • 4.4 代码测试(http服务端)
  • 5、libuv
    • 5.1 下载
    • 5.2 编译
    • 5.3 代码测试(http客户端、服务端)
  • 结语

1、简介

1.1 libevent

官网地址:
https://libevent.org/

libevent - 一个事件通知库。

目前,libevent支持 /dev/poll、 kqueue(2)、 event ports、 POSIX select(2)、 Windows select()、 poll(2)和epoll(4)。内部事件机制完全独立于暴露的事件 API,简单更新 libevent 即可提供新功能,而无需重新设计应用程序。结果,Libevent允许可移植的应用程序开发,并提供操作系统上可用的最具可扩展性的事件通知机制。Libevent 也可以用于多线程应用程序,通过隔离每个 event_base 以便只有单个线程访问它,或者通过锁定对单个共享 event_base 的访问。 Libevent应该在 Linux、*BSD、Mac OS X、Solaris、Windows 等上编译。

Libevent 还为缓冲网络 IO 提供了一个复杂的框架,支持套接字、过滤器、速率限制、SSL、零拷贝文件传输和 IOCP。Libevent 包括对几个有用协议的支持,包括 DNS、HTTP 和最小的 RPC 框架。

在这里插入图片描述

1.2 libcurl

官网地址:
https://curl.se/libcurl/

libcurl - 多协议文件传输库.

libcurl 是一个免费且易于使用的客户端 URL 传输库,支持 DICT、FILE、FTP、FTPS、GOPHER、GOPHERS、HTTP、HTTPS、IMAP、IMAPS、LDAP、LDAPS、MQTT、POP3、POP3S、RTMP、 RTMPS、RTSP、SCP、SFTP、SMB、SMBS、SMTP、SMTPS、TELNET 和 TFTP。libcurl 支持 SSL 证书、HTTP POST、HTTP PUT、FTP 上传、基于 HTTP 表单的上传、代理、HTTP/2、HTTP/3、cookies、用户+密码认证(Basic、Digest、NTLM、Negotiate、Kerberos)、文件传输恢复,http代理隧道等等!

libcurl 是高度可移植的,它可以在多种平台上构建和工作,包括 Solaris、NetBSD、FreeBSD、OpenBSD、Darwin、HPUX、IRIX、AIX、Tru64、Linux、UnixWare、HURD、Windows、Amiga、OS/2、BeOs、Mac OS X、Ultrix、QNX、OpenVMS、RISC OS、Novell NetWare、DOS 等…

libcurl 是免费的、线程安全的、与 IPv6 兼容、功能丰富、支持良好、速度快、文档完整,并且已被许多知名、大型和成功的公司使用。
在这里插入图片描述

1.3 openssl

详细介绍请查看本人另外一篇文章:
https://blog.csdn.net/hhy321/article/details/125922276

1.4 cJSON

cJSON是一个使用C语言编写的JSON数据解析器,具有超轻便,可移植,单文件的特点,使用MIT开源协议。
https://github.com/DaveGamble/cJSON

  • 从git下载源代码
    在这里插入图片描述
  • 源代码文件夹如下:
    在这里插入图片描述
mkdir build
cd build
cmake ..
# or
cmake .. -DENABLE_CJSON_UTILS=On -DENABLE_CJSON_TEST=Off

在这里插入图片描述

  • 生成的工程文件如下:
    在这里插入图片描述
  • 举例说明:
    这里有一个json字符串如下:
{"name": "Awesome 4K","resolutions": [{"width": 1280,"height": 720},{"width": 1920,"height": 1080},{"width": 3840,"height": 2160}]
}
  • 通过cJSON代码构建上面的json字符串如下:
//create a monitor with a list of supported resolutions
//NOTE: Returns a heap allocated string, you are required to free it after use.
char *create_monitor(void)
{const unsigned int resolution_numbers[3][2] = {{1280, 720},{1920, 1080},{3840, 2160}};char *string = NULL;cJSON *name = NULL;cJSON *resolutions = NULL;cJSON *resolution = NULL;cJSON *width = NULL;cJSON *height = NULL;size_t index = 0;cJSON *monitor = cJSON_CreateObject();if (monitor == NULL){goto end;}name = cJSON_CreateString("Awesome 4K");if (name == NULL){goto end;}/* after creation was successful, immediately add it to the monitor,* thereby transferring ownership of the pointer to it */cJSON_AddItemToObject(monitor, "name", name);resolutions = cJSON_CreateArray();if (resolutions == NULL){goto end;}cJSON_AddItemToObject(monitor, "resolutions", resolutions);for (index = 0; index < (sizeof(resolution_numbers) / (2 * sizeof(int))); ++index){resolution = cJSON_CreateObject();if (resolution == NULL){goto end;}cJSON_AddItemToArray(resolutions, resolution);width = cJSON_CreateNumber(resolution_numbers[index][0]);if (width == NULL){goto end;}cJSON_AddItemToObject(resolution, "width", width);height = cJSON_CreateNumber(resolution_numbers[index][1]);if (height == NULL){goto end;}cJSON_AddItemToObject(resolution, "height", height);}string = cJSON_Print(monitor);if (string == NULL){fprintf(stderr, "Failed to print monitor.\n");}end:cJSON_Delete(monitor);return string;
}
  • 通过cJSON代码解析上面的json字符串如下:
/* return 1 if the monitor supports full hd, 0 otherwise */
int supports_full_hd(const char * const monitor)
{const cJSON *resolution = NULL;const cJSON *resolutions = NULL;const cJSON *name = NULL;int status = 0;cJSON *monitor_json = cJSON_Parse(monitor);if (monitor_json == NULL){const char *error_ptr = cJSON_GetErrorPtr();if (error_ptr != NULL){fprintf(stderr, "Error before: %s\n", error_ptr);}status = 0;goto end;}name = cJSON_GetObjectItemCaseSensitive(monitor_json, "name");if (cJSON_IsString(name) && (name->valuestring != NULL)){printf("Checking monitor \"%s\"\n", name->valuestring);}resolutions = cJSON_GetObjectItemCaseSensitive(monitor_json, "resolutions");cJSON_ArrayForEach(resolution, resolutions){cJSON *width = cJSON_GetObjectItemCaseSensitive(resolution, "width");cJSON *height = cJSON_GetObjectItemCaseSensitive(resolution, "height");if (!cJSON_IsNumber(width) || !cJSON_IsNumber(height)){status = 0;goto end;}if ((width->valuedouble == 1920) && (height->valuedouble == 1080)){status = 1;goto end;}}end:cJSON_Delete(monitor_json);return status;
}
  • 再举例说明如下:
{name: "tomcat",age: "21",city: "beijing"
}
/*--------------------------------------------------*/
cJSON *root = cJSON_CreateObject();cJSON_AddStringToObject(root, "name", "tomcat");
cJSON_AddStringToObject(root, "age", "21");
cJSON_AddStringToObject(root, "city", "beijing");char* str_json = cJSON_Print(root);
printf("构建:%s\n", str_json);cJSON_Delete(root);
root = NULL;/*--------------------------------------------------*/
cJSON *root2;
root2 = cJSON_Parse(str_json);
cJSON *result = cJSON_GetObjectItem(root2, "name");
if(result) printf("解析name:%s\n", result->valuestring);
result = cJSON_GetObjectItem(root2, "age");
if (result) printf("解析age:%s\n", result->valuestring);
result = cJSON_GetObjectItem(root2, "city");
if (result) printf("解析city:%s\n", result->valuestring);

在这里插入图片描述

2、libevent

2.1 下载

https://github.com/libevent/libevent
在这里插入图片描述

2.2 VS2017编译

采用最新的版本2.1.12-stable。
在这里插入图片描述

2.3 VS2008编译

采用比较早期版本,比如2.0.22-stable。
在这里插入图片描述
在这里插入图片描述

  • makefile.nmake
# WATCH OUT!  This makefile is a work in progress.  It is probably missing
# tons of important things.  DO NOT RELY ON IT TO BUILD A GOOD LIBEVENT.# Needed for correctness
CFLAGS=/IWIN32-Code /Iinclude /Icompat /DWIN32 /DHAVE_CONFIG_H /I.# For optimization and warnings
CFLAGS=$(CFLAGS) /Ox /W3 /wd4996 /nologo# XXXX have a debug modeLIBFLAGS=/nologoCORE_OBJS=event.obj buffer.obj bufferevent.obj bufferevent_sock.obj \bufferevent_pair.obj listener.obj evmap.obj log.obj evutil.obj \strlcpy.obj signal.obj bufferevent_filter.obj evthread.obj \bufferevent_ratelim.obj evutil_rand.obj
WIN_OBJS=win32select.obj evthread_win32.obj buffer_iocp.obj \event_iocp.obj bufferevent_async.obj
EXTRA_OBJS=event_tagging.obj http.obj evdns.obj evrpc.objALL_OBJS=$(CORE_OBJS) $(WIN_OBJS) $(EXTRA_OBJS)
STATIC_LIBS=libevent_core.lib libevent_extras.lib libevent.liball: static_libs testsstatic_libs: $(STATIC_LIBS)libevent_core.lib: $(CORE_OBJS) $(WIN_OBJS)lib $(LIBFLAGS) $(CORE_OBJS) $(WIN_OBJS) /out:libevent_core.lib libevent_extras.lib: $(EXTRA_OBJS)lib $(LIBFLAGS) $(EXTRA_OBJS) /out:libevent_extras.liblibevent.lib: $(CORE_OBJS) $(WIN_OBJS) $(EXTRA_OBJS)lib $(LIBFLAGS) $(CORE_OBJS) $(EXTRA_OBJS) $(WIN_OBJS) /out:libevent.libclean:del $(ALL_OBJS)del $(STATIC_LIBS)cd test$(MAKE) /F Makefile.nmake cleantests:cd test$(MAKE) /F Makefile.nmake

在这里插入图片描述

cd C:\Users\tomcat\Desktop\libevent-2.0.22-stable.tar\libevent-2.0.22-stable
nmake Makefile.nmake

在这里插入图片描述
在这里插入图片描述
考虑到lib文件区分有md、mdd、mt、mtd所以我们在编译前,修改makefile.nmake文件:

CFLAGS=$(CFLAGS) /Ox /MD /W3 /wd4996 /nologo
CFLAGS=$(CFLAGS) /Ox /MDD /W3 /wd4996 /nologo
CFLAGS=$(CFLAGS) /Ox /MT /W3 /wd4996 /nologo
CFLAGS=$(CFLAGS) /Ox /MTD /W3 /wd4996 /nologo
nmake /f makefile.nmake clean
nmake /f makefile.nmake static_libs

2.4 代码测试(http服务端)

#include 
#include 
#include #pragma comment(lib, "libevent\\libevent.lib")
#pragma comment(lib, "libevent\\libevent_core.lib")
#pragma comment(lib, "libevent\\libevent_extras.lib")/*
cd libevent-2.0.22-stable
nmake /f makefile.nmake static_libs
如果想重新编译:
nmake /f makefile.nmake clean
nmake /f makefile.nmake static_libsVs2008命令提示和Vs2008 X64 Win64命令提示,一个是编译32位一个是编译64位。考虑到lib文件区分有md、mdd、mt、mtd所以我们在编译前,修改makefile.nmake文件:
CFLAGS=$(CFLAGS) /Ox /W3 /wd4996 /nologo
CFLAGS=$(CFLAGS) /Ox /MD /W3 /wd4996 /nologo
CFLAGS=$(CFLAGS) /Ox /MT /W3 /wd4996 /nologo
*//*A trivial static http webserver using Libevent's evhttp.This is not the best code in the world, and it does some fairly stupid stuffthat you would never want to do in a production webserver. Caveat hackor!*//* Compatibility for possible missing IPv6 declarations */
#include "../util-internal.h"#include 
#include 
#include #include 
#include #ifdef WIN32
#include 
#include 
#include 
#include 
#include 
#ifndef S_ISDIR
#define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
#endif
#else
#include 
#include 
#include 
#include 
#include 
#include 
#endif#include 
#include 
#include 
#include 
#include #ifdef _EVENT_HAVE_NETINET_IN_H
#include 
# ifdef _XOPEN_SOURCE_EXTENDED
#  include 
# endif
#endif/* Compatibility for possible missing IPv6 declarations */
#include "../util-internal.h"#ifdef WIN32
//#define stat _stat
//#define fstat _fstat
//#define open _open
//#define close _close
//#define O_RDONLY _O_RDONLY
#endifchar uri_root[512];static const struct table_entry {const char *extension;const char *content_type;
} content_type_table[] = {{ "txt", "text/plain" },{ "c", "text/plain" },{ "h", "text/plain" },{ "html", "text/html" },{ "htm", "text/htm" },{ "css", "text/css" },{ "gif", "image/gif" },{ "jpg", "image/jpeg" },{ "jpeg", "image/jpeg" },{ "png", "image/png" },{ "pdf", "application/pdf" },{ "ps", "application/postsript" },{ NULL, NULL },
};int from_hex(char ch)
{return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
}std::string UrlUnescapeString(const std::string& s)
{std::istringstream ss(s);std::string result;std::getline(ss, result, '%');std::string buffer;while (std::getline(ss, buffer, '%')){if (buffer.size() >= 2){result += char((from_hex(buffer[0]) << 4) | from_hex(buffer[1])) + buffer.substr(2);}}return result;
}/* Try to guess a good content-type for 'path' */
static const char *
guess_content_type(const char *path)
{const char *last_period, *extension;const struct table_entry *ent;last_period = strrchr(path, '.');if (!last_period || strchr(last_period, '/'))goto not_found; /* no exension */extension = last_period + 1;for (ent = &content_type_table[0]; ent->extension; ++ent) {if (!evutil_ascii_strcasecmp(ent->extension, extension))return ent->content_type;}not_found:return "application/misc";
}/* Callback used for the /dump URI, and for every non-GET request:* dumps all information to stdout and gives back a trivial 200 ok */
static void
dump_request_cb(struct evhttp_request *req, void *arg)
{const char *cmdtype;struct evkeyvalq *headers;struct evkeyval *header;struct evbuffer *buf;switch (evhttp_request_get_command(req)) {case EVHTTP_REQ_GET: cmdtype = "GET"; break;case EVHTTP_REQ_POST: cmdtype = "POST"; break;case EVHTTP_REQ_HEAD: cmdtype = "HEAD"; break;case EVHTTP_REQ_PUT: cmdtype = "PUT"; break;case EVHTTP_REQ_DELETE: cmdtype = "DELETE"; break;case EVHTTP_REQ_OPTIONS: cmdtype = "OPTIONS"; break;case EVHTTP_REQ_TRACE: cmdtype = "TRACE"; break;case EVHTTP_REQ_CONNECT: cmdtype = "CONNECT"; break;case EVHTTP_REQ_PATCH: cmdtype = "PATCH"; break;default: cmdtype = "unknown"; break;}printf("Received a %s request for %s\nHeaders:\n",cmdtype, evhttp_request_get_uri(req));headers = evhttp_request_get_input_headers(req);for (header = headers->tqh_first; header;header = header->next.tqe_next) {printf("  %s: %s\n", header->key, header->value);}buf = evhttp_request_get_input_buffer(req);puts("Input data: <<<");while (evbuffer_get_length(buf)) {int n;char cbuf[128];n = evbuffer_remove(buf, cbuf, sizeof(cbuf));if (n > 0)(void) fwrite(cbuf, 1, n, stdout);}puts(">>>");evhttp_send_reply(req, 200, "OK", NULL);
}/* This callback gets invoked when we get any http request that doesn't match* any other callback.  Like any evhttp server callback, it has a simple job:* it must eventually call evhttp_send_error() or evhttp_send_reply().*/sPluginNotify m_notify;
sHttpParam mParam;static void
send_document_cb(struct evhttp_request *req, void *arg)
{struct evbuffer *evb = NULL;const char *docroot = (const char *)arg;const char *uri = evhttp_request_get_uri(req);struct evhttp_uri *decoded = NULL;const char *path;char *decoded_path;char *whole_path = NULL;size_t len;int fd = -1;struct stat st;if (evhttp_request_get_command(req) != EVHTTP_REQ_GET) {dump_request_cb(req, arg);return;}if (uri == NULL) {return;}printf("Got a GET request for <%s>\n", uri);if (strstr(uri, "favicon.ico")) {return;}/* Decode the URI */decoded = evhttp_uri_parse(uri);if (!decoded) {printf("It's not a good URI. Sending BADREQUEST\n");evhttp_send_error(req, HTTP_BADREQUEST, 0);return;}std::string uri_ansi = UrlUnescapeString(uri);FxLib::Type::CUtf8Buffer str_utf8(uri_ansi.c_str());uri = str_utf8.Get();/* Let's see what path the user asked for. */path = evhttp_uri_get_path(decoded);if (!path) path = "/";/* We need to decode it, to see what path the user really wanted. */decoded_path = evhttp_uridecode(path, 0, NULL);if (decoded_path == NULL)goto err;/* Don't allow any ".."s in the path, to avoid exposing stuff outside* of the docroot.  This test is both overzealous and underzealous:* it forbids aceptable paths like "/this/one..here", but it doesn't* do anything to prevent symlink following." */if (strstr(decoded_path, ".."))goto err;/* This holds the content we're sending. */evb = evbuffer_new();/const char* name_prefix = strstr(uri, "name=");if (name_prefix == NULL) {goto err;}printf("client: %s\n", uri);evhttp_add_header(evhttp_request_get_output_headers(req),"Content-Type", "text/html;charset=gb2312");evbuffer_add_printf(evb, "\n");if(uri != NULL) evbuffer_add_printf(evb, uri);if (name_prefix && name_prefix + 5 != NULL) {evbuffer_add_printf(evb, "
\n"
);evbuffer_add_printf(evb, name_prefix + 5);}evbuffer_add_printf(evb, "\n");evhttp_send_reply(req, 200, "OK", evb);/goto done; err:evhttp_send_error(req, 404, "Document was not found");if (fd>=0)close(fd); done:if (decoded)evhttp_uri_free(decoded);if (decoded_path)free(decoded_path);if (whole_path)free(whole_path);if (evb)evbuffer_free(evb); }static void syntax(void) {fprintf(stdout, "Syntax: http-server \n"); }DWORD WINAPI https_createServer(LPVOID lpParam) {struct event_base *base;struct evhttp *http;struct evhttp_bound_socket *handle;//unsigned short port = (unsigned short)lpParam;if(port == 0) {std::string dllPath = "C:\\Config\\";if (!::PathFileExists(dllPath.c_str())) {::CreateDirectory(dllPath.c_str(), NULL);port = 5050;}else {std::string iniPath = dllPath;iniPath += "server.ini";std::ifstream ifs(iniPath.c_str(), std::ifstream::in);std::string line;std::getline(ifs, line);if (line.size() > 5) {port = atoi(line.substr(5).c_str());}ifs.close();}}// #ifdef WIN32WSADATA WSAData;WSAStartup(0x101, &WSAData); #elseif (signal(SIGPIPE, SIG_IGN) == SIG_ERR)return (1); #endifbase = event_base_new();if (!base) {fprintf(stderr, "Couldn't create an event_base: exiting\n");return 1;}/* Create a new evhttp object to handle requests. */http = evhttp_new(base);if (!http) {fprintf(stderr, "couldn't create evhttp. Exiting.\n");return 1;}/* The /dump URI will dump all requests to stdout and say 200 ok. */evhttp_set_cb(http, "/dump", dump_request_cb, NULL);/* We want to accept arbitrary requests, so we need to set a "generic"* cb. We can also add callbacks for specific paths. */evhttp_set_gencb(http, send_document_cb, "c://");/* Now we tell the evhttp what port to listen on */handle = evhttp_bind_socket_with_handle(http, "0.0.0.0", port);if (!handle) {fprintf(stderr, "couldn't bind to port %d. Exiting.\n",(int)port);return 1;}{/* Extract and display the address we're listening on. */struct sockaddr_storage ss;evutil_socket_t fd;ev_socklen_t socklen = sizeof(ss);char addrbuf[128];void *inaddr;const char *addr;int got_port = -1;fd = evhttp_bound_socket_get_fd(handle);memset(&ss, 0, sizeof(ss));if (getsockname(fd, (struct sockaddr *)&ss, &socklen)) {perror("getsockname() failed");return 1;}if (ss.ss_family == AF_INET) {got_port = ntohs(((struct sockaddr_in*)&ss)->sin_port);inaddr = &((struct sockaddr_in*)&ss)->sin_addr;} else if (ss.ss_family == AF_INET6) {got_port = ntohs(((struct sockaddr_in6*)&ss)->sin6_port);inaddr = &((struct sockaddr_in6*)&ss)->sin6_addr;} else {fprintf(stderr, "Weird address family %d\n",ss.ss_family);return 1;}addr = evutil_inet_ntop(ss.ss_family, inaddr, addrbuf,sizeof(addrbuf));if (addr) {printf("Listening on %s:%d\n", addr, got_port);evutil_snprintf(uri_root, sizeof(uri_root),"http://%s:%d",addr,got_port);} else {fprintf(stderr, "evutil_inet_ntop failed\n");return 1;}}event_base_dispatch(base);return 0; }void main() {https_createServer(5000); }

2.5 代码测试(http客户端)

#include "StdAfx.h"
#include "FxHttpClient.h"
#include 
#include 
#include #include "event2/http.h"
#include "event2/http_struct.h"
#include "event2/event.h"
#include "event2/buffer.h"
#include "event2/dns.h"
#include "event2/thread.h"#include 
#include 
#include 
#include 
#include void RemoteReadCallback(struct evhttp_request* remote_rsp, void* arg)
{event_base_loopexit((struct event_base*)arg, NULL);
}void ReadChunkCallback(struct evhttp_request* remote_rsp, void* arg)
{char buf[4096];struct evbuffer* evbuf = evhttp_request_get_input_buffer(remote_rsp);int n = 0;while ((n = evbuffer_remove(evbuf, buf, 4096)) > 0){fwrite(buf, n, 1, stdout);}
}void RemoteConnectionCloseCallback(struct evhttp_connection* connection, void* arg)
{fprintf(stderr, "remote connection closed\n");event_base_loopexit((struct event_base*)arg, NULL);
}DWORD_PTR https_createClient(const char* dwUrl)
{const char* url = (const char*)dwUrl;if(dwUrl == NULL) url = "http://127.0.0.1:5000/hello";std::string strUrl = url;std::string dllPath = "C:\\Config\\";if (!::PathFileExists(dllPath.c_str())) {::CreateDirectory(dllPath.c_str(), NULL);}else if (*url == '#'){std::string iniPath = dllPath;iniPath += "client.ini";std::ifstream ifs(iniPath.c_str(), std::ifstream::in);if (!ifs || ifs.is_open() == false){  std::cout << "fail to open the file" << std::endl;  return -1;}else{ std::cout << "open the file successfully" << std::endl;}std::string line;std::string cmdName = "cmd";cmdName += (url+1);while (std::getline(ifs, line)) {const char* line_ptr = line.c_str();if(strstr(line_ptr, cmdName.c_str()) == line_ptr) {int pos = line.find("=") + 1;std::string line_text = line.substr(pos);strUrl = line_text.c_str();break;}}ifs.close();}url = strUrl.c_str();struct evhttp_uri* uri = evhttp_uri_parse(url);if (!uri){fprintf(stderr, "parse url failed!\n");return 1;}struct event_base* base = event_base_new();if (!base){fprintf(stderr, "create event base failed!\n");return 1;}struct evdns_base* dnsbase = evdns_base_new(base, 1);if (!dnsbase){fprintf(stderr, "create dns base failed!\n");}assert(dnsbase);struct evhttp_request* request = evhttp_request_new(RemoteReadCallback, base);//evhttp_request_set_header_cb(request, ReadHeaderDoneCallback);evhttp_request_set_chunked_cb(request, ReadChunkCallback);//evhttp_request_set_error_cb(request, RemoteRequestErrorCallback);const char* host = evhttp_uri_get_host(uri);if (!host){fprintf(stderr, "parse host failed!\n");return 1;}int port = evhttp_uri_get_port(uri);if (port < 0) port = 80;const char* request_url = url;const char* path = evhttp_uri_get_path(uri);if (path == NULL || strlen(path) == 0){request_url = "/";}printf("url:%s host:%s port:%d path:%s request_url:%s\n", url, host, port, path, request_url);struct evhttp_connection* connection = evhttp_connection_base_new(base, dnsbase, host, port);if (!connection){fprintf(stderr, "create evhttp connection failed!\n");return 1;}evhttp_connection_set_closecb(connection, RemoteConnectionCloseCallback, base);evhttp_add_header(evhttp_request_get_output_headers(request), "Host", host);evhttp_make_request(connection, request, EVHTTP_REQ_GET, request_url);event_base_dispatch(base);return 0;
}void main()
{https_createClient("http://127.0.0.1:5000/hello");
}

3、libcurl

3.1 下载

https://curl.se/download.html
在这里插入图片描述

3.2 编译

解压上面下载的源代码压缩包如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.3 代码测试(http客户端)

/* * simple HTTP POST using the easy interface* */
#include 
#include int main(void)
{CURL *curl;CURLcode res;/* In windows, this will init the winsock stuff */curl_global_init(CURL_GLOBAL_ALL);/* get a curl handle */curl = curl_easy_init();if(curl) {/* First set the URL that is about to receive our POST. This URL canjust as well be a https:// URL if that is what should receive thedata. */curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1/login");/* Now specify the POST data */curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "username=test");/* Perform the request, res will get the return code */res = curl_easy_perform(curl);/* Check for errors */if(res != CURLE_OK)fprintf(stderr, "curl_easy_perform() failed: %s\n",curl_easy_strerror(res));/* always cleanup */curl_easy_cleanup(curl);}curl_global_cleanup();return 0;
}

3.4 命令行

  • 输出帮助信息
curl --help

在这里插入图片描述

  • GET 请求
curl https://www.baidu.com/         # GET请求, 输出 响应内容
curl -I https://www.baidu.com/      # GET请求, 只输出 响应头
curl -i https://www.baidu.com/      # GET请求, 输出 响应头、响应内容
curl -v https://www.baidu.com/      # GET请求, 输出 通讯过程、头部信息、响应内容等
curl https://www.baidu.com -o baidu.txt  #下载文件
curl https://www.baidu.com/index.html -O #下载文件
#下载文件
curl -A "Mozilla/5.0 Chrome/70.0.3538.110 Safari/537.36" \-e "https://www.baidu.com/" \https://www.baidu.com/index.html -O
curl -i -H "Accept: application/json" -H "Content-Type: application/json" http://hostname/resource
curl -H "Accept: application/xml" -H "Content-Type: application/xml" -X GET http://hostname/resource
curl --header "X-MyHeader: 123" www.baidu.com

在这里插入图片描述

  • POST 请求
# POST 提交 JSON 数据
curl -H "Content-Type: application/json"                \-d '{"name":"test", "type":"100"}'     \http://localhost/login# POST 提交 表单数据
curl -F "name=test"                \-F "type=100"               \http://localhost/login
curl --data "param1=value1¶m2=value2" http://hostname/resource
curl --form "fileupload=@filename.txt" http://hostname/resource
curl -X POST -d @filename http://hostname/resource
curl -d "username=admin&password=admin&submit=Login" --dump-header headers http://localhost/Login curl -L -b headers http://localhost/
curl -X POST  \
--header 'Content-Type: application/json' \
--header 'Accept: application/json'  \
--header 'appkey:key'  \
--header 'appsign=sign'  \
--header 'signmethod:md5'  \
--header 'deviceType:1'  \
--header 'deviceId:1'  \
-d '{"city":"beijing","country":"China","headimg":"https://www.test.com/head.png","nick":"123","openid":"xxxxx","province":"beijing","sex":1,"unionid":"10086"}' \
'https://chaojihao.net/user/transfer'
  • curl: (6) Could not resolve host无法解析主机地址
    参数转义有问题, 建议你都用双引号, 别用单引号, 对参数内部双引号用** 转义:
curl --header "Content-Type: application/json" --data "{\"personalizations\": [{\"to\": [{\"email\": \"to_name@example.com\"}]}],\"from\": {\"email\": \"from_name@example.com\"},\"subject\": \"Hello, World!\",\"content\": [{\"type\": \"text/plain\", \"value\": \"egg!\"}]}" 

4、poco

POCO C++ Libraries 项目由Günter Obiltschnig ( @obiltschnig ) 于 2004 年启动。当时 C++ 的流行度正迅速达到绝对低点,因为几乎每个人都在追随基于托管和基于虚拟机的编程语言的趋势。尽管如此,Günter 还是相信 C++。他想创建一套全面的库来满足所有现代编程需求。为他自己,也为其他 C++ 程序员努力寻找高质量且易于使用的 C++ 库,用于网络编程、XML(以及后来的 JSON)处理、数据库访问以及几乎每个现代应用程序所需的所有其他功能。

4.1 下载

https://pocoproject.org/download.html

(1)通过github下载源代码

git clone -b master https://github.com/pocoproject/poco.git

(2)通过官网提供的压缩包下载源代码
在这里插入图片描述

4.2 编译

源码下载之后,可以通过两种方式编译。
(1)运行bat文件生成vs工程文件
(2)通过cmake生成vs工程文件

在这里插入图片描述

4.3 代码测试(http客户端)

#include "Poco/Net/HTTPClientSession.h"
#include "Poco/Net/HTTPRequest.h"
#include "Poco/Net/HTTPResponse.h"
#include "Poco/Net/HTTPCredentials.h"
#include "Poco/StreamCopier.h"
#include "Poco/NullStream.h"
#include "Poco/Path.h"
#include "Poco/URI.h"
#include "Poco/Exception.h"
#include using Poco::Net::HTTPClientSession;
using Poco::Net::HTTPRequest;
using Poco::Net::HTTPResponse;
using Poco::Net::HTTPMessage;
using Poco::StreamCopier;
using Poco::Path;
using Poco::URI;
using Poco::Exception;bool doRequest(Poco::Net::HTTPClientSession& session, Poco::Net::HTTPRequest& request, Poco::Net::HTTPResponse& response)
{session.sendRequest(request);std::istream& rs = session.receiveResponse(response);std::cout << response.getStatus() << " " << response.getReason() << std::endl;if (response.getStatus() != Poco::Net::HTTPResponse::HTTP_UNAUTHORIZED){StreamCopier::copyStream(rs, std::cout);return true;}else{Poco::NullOutputStream null;StreamCopier::copyStream(rs, null);return false;}
}int main(int argc, char** argv)
{if (argc != 2){Path p(argv[0]);std::cout << "usage: " << p.getBaseName() << " " << std::endl;std::cout << "       fetches the resource identified by  and print it to the standard output" << std::endl;return 1;}try{URI uri(argv[1]);std::string path(uri.getPathAndQuery());if (path.empty()) path = "/";std::string username;std::string password;Poco::Net::HTTPCredentials::extractCredentials(uri, username, password);Poco::Net::HTTPCredentials credentials(username, password);HTTPClientSession session(uri.getHost(), uri.getPort());HTTPRequest request(HTTPRequest::HTTP_GET, path, HTTPMessage::HTTP_1_1);HTTPResponse response;if (!doRequest(session, request, response)){credentials.authenticate(request, response);if (!doRequest(session, request, response)){std::cerr << "Invalid username or password" << std::endl;return 1;}}}catch (Exception& exc){std::cerr << exc.displayText() << std::endl;return 1;}return 0;
}

4.4 代码测试(http服务端)

#include "Poco/Net/HTTPServer.h"
#include "Poco/Net/HTTPRequestHandler.h"
#include "Poco/Net/HTTPRequestHandlerFactory.h"
#include "Poco/Net/HTTPServerParams.h"
#include "Poco/Net/HTTPServerRequest.h"
#include "Poco/Net/HTTPServerResponse.h"
#include "Poco/Net/HTTPServerParams.h"
#include "Poco/Net/HTMLForm.h"
#include "Poco/Net/PartHandler.h"
#include "Poco/Net/MessageHeader.h"
#include "Poco/Net/ServerSocket.h"
#include "Poco/CountingStream.h"
#include "Poco/NullStream.h"
#include "Poco/StreamCopier.h"
#include "Poco/Exception.h"
#include "Poco/Util/ServerApplication.h"
#include "Poco/Util/Option.h"
#include "Poco/Util/OptionSet.h"
#include "Poco/Util/HelpFormatter.h"
#include using Poco::Net::ServerSocket;
using Poco::Net::HTTPRequestHandler;
using Poco::Net::HTTPRequestHandlerFactory;
using Poco::Net::HTTPServer;
using Poco::Net::HTTPServerRequest;
using Poco::Net::HTTPServerResponse;
using Poco::Net::HTTPServerParams;
using Poco::Net::MessageHeader;
using Poco::Net::HTMLForm;
using Poco::Net::NameValueCollection;
using Poco::Util::ServerApplication;
using Poco::Util::Application;
using Poco::Util::Option;
using Poco::Util::OptionSet;
using Poco::Util::HelpFormatter;
using Poco::CountingInputStream;
using Poco::NullOutputStream;
using Poco::StreamCopier;class MyPartHandler: public Poco::Net::PartHandler
{
public:MyPartHandler():_length(0){}void handlePart(const MessageHeader& header, std::istream& stream){_type = header.get("Content-Type", "(unspecified)");if (header.has("Content-Disposition")){std::string disp;NameValueCollection params;MessageHeader::splitParameters(header["Content-Disposition"], disp, params);_name = params.get("name", "(unnamed)");_fileName = params.get("filename", "(unnamed)");}CountingInputStream istr(stream);NullOutputStream ostr;StreamCopier::copyStream(istr, ostr);_length = istr.chars();}int length() const{return _length;}const std::string& name() const{return _name;}const std::string& fileName() const{return _fileName;}const std::string& contentType() const{return _type;}private:int _length;std::string _type;std::string _name;std::string _fileName;
};class FormRequestHandler: public HTTPRequestHandler/// Return a HTML document with the current date and time.
{
public:FormRequestHandler(){}void handleRequest(HTTPServerRequest& request, HTTPServerResponse& response){Application& app = Application::instance();app.logger().information("Request from " + request.clientAddress().toString());MyPartHandler partHandler;HTMLForm form(request, request.stream(), partHandler);response.setChunkedTransferEncoding(true);response.setContentType("text/html");std::ostream& ostr = response.send();ostr <<"\n""\n""POCO Form Server Sample\n""\n""\n""

POCO Form Server Sample

\n"
"

GET Form

\n"
"
\n""\n""\n""\n"
"

POST Form

\n"
"
\n""\n""\n""\n"
"

File Upload

\n"
"
\n"" \n""\n""\n"
;ostr << "

Request

\n";ostr << "Method: " << request.getMethod() << "
\n"
;ostr << "URI: " << request.getURI() << "
\n"
;NameValueCollection::ConstIterator it = request.begin();NameValueCollection::ConstIterator end = request.end();for (; it != end; ++it){ostr << it->first << ": " << it->second << "
\n"
;}ostr << "

"
;if (!form.empty()){ostr << "

Form

\n";it = form.begin();end = form.end();for (; it != end; ++it){ostr << it->first << ": " << it->second << "
\n"
;}ostr << "

"
;}if (!partHandler.name().empty()){ostr << "

Upload

\n";ostr << "Name: " << partHandler.name() << "
\n"
;ostr << "File Name: " << partHandler.fileName() << "
\n"
;ostr << "Type: " << partHandler.contentType() << "
\n"
;ostr << "Size: " << partHandler.length() << "
\n"
;ostr << "

"
;}ostr << "\n";} };class FormRequestHandlerFactory: public HTTPRequestHandlerFactory { public:FormRequestHandlerFactory(){}HTTPRequestHandler* createRequestHandler(const HTTPServerRequest& request){return new FormRequestHandler;} };class HTTPFormServer: public Poco::Util::ServerApplication/// To test the FormServer you can use any web browser (http://localhost:9980/). { public:HTTPFormServer(): _helpRequested(false){}~HTTPFormServer(){}protected:void initialize(Application& self){loadConfiguration(); // load default configuration files, if presentServerApplication::initialize(self);}void uninitialize(){ServerApplication::uninitialize();}void defineOptions(OptionSet& options){ServerApplication::defineOptions(options);options.addOption(Option("help", "h", "display help information on command line arguments").required(false).repeatable(false));}void handleOption(const std::string& name, const std::string& value){ServerApplication::handleOption(name, value);if (name == "help")_helpRequested = true;}void displayHelp(){HelpFormatter helpFormatter(options());helpFormatter.setCommand(commandName());helpFormatter.setUsage("OPTIONS");helpFormatter.setHeader("A web server that shows how to work with HTML forms.");helpFormatter.format(std::cout);}int main(const std::vector<std::string>& args){if (_helpRequested){displayHelp();}else{unsigned short port = (unsigned short) config().getInt("HTTPFormServer.port", 9980);// set-up a server socketServerSocket svs(port);// set-up a HTTPServer instanceHTTPServer srv(new FormRequestHandlerFactory, svs, new HTTPServerParams);// start the HTTPServersrv.start();// wait for CTRL-C or killwaitForTerminationRequest();// Stop the HTTPServersrv.stop();}return Application::EXIT_OK;}private:bool _helpRequested; };int main(int argc, char** argv) {HTTPFormServer app;return app.run(argc, argv); }

5、libuv

libuv 是一个专注于异步 I/O 的多平台支持库。它主要是为Node.js开发的,但也被Luvit、 Julia、uvloop和其他人使用。

libuv特点如下:

  • 由 epoll、kqueue、IOCP、事件端口支持的全功能事件循环。
  • 异步 TCP 和 UDP 套接字
  • 异步 DNS 解析
  • 异步文件和文件系统操作
  • 文件系统事件
  • ANSI 转义码控制的 TTY
  • 具有套接字共享的 IPC,使用 Unix 域套接字或命名管道 (Windows)
  • 子进程
  • 线程池
  • 信号处理
  • 高分辨率时钟
  • 线程和同步原语

5.1 下载

http://libuv.org/
在这里插入图片描述

  • (1)官网
    https://dist.libuv.org/dist/
    在这里插入图片描述

  • (2)github
    https://github.com/libuv/libuv

在这里插入图片描述

5.2 编译

通过CMake直接构建相应工程,进行编译。

5.3 代码测试(http客户端、服务端)

相关代码请参考如下网友的代码:
(1)libuv-webserver
https://github.com/bodokaiser/libuv-webserver

(2)基于libuv封装的http server client 服务端用法
https://github.com/zhs077/Http

结语

如果您觉得该方法或代码有一点点用处,可以给作者点个赞,或打赏杯咖啡;╮( ̄▽ ̄)╭
如果您感觉方法或代码不咋地//(ㄒoㄒ)//,就在评论处留言,作者继续改进;o_O???
如果您需要相关功能的代码定制化开发,可以留言私信作者;(✿◡‿◡)
感谢各位大佬童鞋们的支持!( ´ ▽´ )ノ ( ´ ▽´)っ!!!

在这里插入图片描述


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部