🍺Web服务器系列相关文章编写如下🍺:
- 🎈【Web开发】Node.js实现Web服务器(http模块)🎈
- 🎈【Web开发】Node.js实现Web服务器(express模块)🎈
- 🎈【Web开发】Python实现Web服务器(Flask入门)🎈
- 🎈【Web开发】Python实现Web服务器(Flask测试)🎈
- 🎈【Web开发】Python实现Web服务器(Tornado入门)🎈
- 🎈【Web开发】Python实现Web服务器(Tornado+flask+nginx)🎈
- 🎈【Web开发】Python实现Web服务器(FastAPI)🎈
- 🎈【Web开发】Android手机上基于Termux实现Web服务器(Python、node.js)🎈
- 🎈【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
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
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
更多信息,请参见我的另一篇文章:
[En]
For more information, please see my other article:
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 ..
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字符串如下:
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;
}
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字符串如下:
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
CFLAGS=/IWIN32-Code /Iinclude /Icompat /DWIN32 /DHAVE_CONFIG_H /I.
CFLAGS=$(CFLAGS) /Ox /W3 /wd4996 /nologo
LIBFLAGS=/nologo
CORE_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.obj
ALL_OBJS=$(CORE_OBJS) $(WIN_OBJS) $(EXTRA_OBJS)
STATIC_LIBS=libevent_core.lib libevent_extras.lib libevent.lib
all: static_libs tests
static_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.lib
libevent.lib: $(CORE_OBJS) $(WIN_OBJS) $(EXTRA_OBJS)
lib $(LIBFLAGS) $(CORE_OBJS) $(EXTRA_OBJS) $(WIN_OBJS) /out:libevent.lib
clean:
del $(ALL_OBJS)
del $(STATIC_LIBS)
cd test
$(MAKE) /F Makefile.nmake clean
tests:
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")
#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
#include "../util-internal.h"
#ifdef WIN32
#endif
char 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;
}
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;
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";
}
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);
}
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 \n", uri);
if (strstr(uri, "favicon.ico")) {
return;
}
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();
path = evhttp_uri_get_path(decoded);
if (!path) path = "/";
decoded_path = evhttp_uridecode(path, 0, NULL);
if (decoded_path == NULL)
goto err;
if (strstr(decoded_path, ".."))
goto err;
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 WIN32
WSADATA WSAData;
WSAStartup(0x101, &WSAData);
#else
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
return (1);
#endif
base = event_base_new();
if (!base) {
fprintf(stderr, "Couldn't create an event_base: exiting\n");
return 1;
}
http = evhttp_new(base);
if (!http) {
fprintf(stderr, "couldn't create evhttp. Exiting.\n");
return 1;
}
evhttp_set_cb(http, "/dump", dump_request_cb, NULL);
evhttp_set_gencb(http, send_document_cb, "c://");
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;
}
{
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_chunked_cb(request, ReadChunkCallback);
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 下载

; 3.2 编译
解压上述下载的源码包,如下所示:
[En]
Extract the source code package downloaded above as follows:






3.3 代码测试(http客户端)
#include
#include
int main(void)
{
CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1/login");
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "username=test");
res = curl_easy_perform(curl);
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
3.4 命令行
- 输出帮助信息
curl --help

- GET 请求
curl https://www.baidu.com/
curl -I https://www.baidu.com/
curl -i https://www.baidu.com/
curl -v https://www.baidu.com/
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 请求
curl -H "Content-Type: application/json" \
-d '{"name":"test", "type":"100"}' \
http://localhost/login
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 编译
下载源代码后,可以通过两种方式进行编译。
[En]
After the source code is downloaded, it can be compiled in two ways.
(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
{
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
{
public:
HTTPFormServer(): _helpRequested(false)
{
}
~HTTPFormServer()
{
}
protected:
void initialize(Application& self)
{
loadConfiguration();
ServerApplication::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);
ServerSocket svs(port);
HTTPServer srv(new FormRequestHandlerFactory, svs, new HTTPServerParams);
srv.start();
waitForTerminationRequest();
srv.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 下载

- (1)官网
https://dist.libuv.org/dist/
- (2)github
https://github.com/libuv/libuv

; 5.2 编译
通过CMake直接构建相应工程,进行编译。
5.3 代码测试(http客户端、服务端)
相关代码请参考以下网友的代码:
[En]
For related codes, please refer to the codes of the following netizens:
(1)libuv-webserver
https://github.com/bodokaiser/libuv-webserver
(2)基于libuv封装的http server client 服务端用法
https://github.com/zhs077/Http
结语
如果您觉得该方法或代码有一点点用处,可以给作者点个赞,或打赏杯咖啡;
╮( ̄▽ ̄)╭
如果您感觉方法或代码不咋地
//(ㄒoㄒ)// ,就在评论处留言,作者继续改进;
o_O???
如果您需要相关功能的代码定制化开发,可以留言私信作者;
(✿◡‿◡)
感谢各位大佬童鞋们的支持!
( ´ ▽´ )ノ ( ´ ▽´)っ!!!

Original: https://blog.csdn.net/hhy321/article/details/124780185
Author: 爱看书的小沐
Title: 【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)
相关阅读
Title: 【赵渝强老师】NoSQL数据库之Cassandra基础

一、Cassandra简介
Cassandra是一个混合型的非关系的数据库,类似于Google的BigTable。其主要功能比Dynamo (分布式的Key-Value存储系统)更丰富,但支持度却不如文档存储MongoDB(介于关系数据库和非关系数据库之间的开源产品,是非关系数据库当中功能最丰富,最像关系数据库的。支持的数据结构非常松散,是类似json的bjson格式,因此可以存储比较复杂的数据类型)。Cassandra最初由Facebook开发,后转变成了开源项目。它是一个网络社交云计算方面理想的数据库。以Amazon专有的完全分布式的Dynamo为基础,结合了Google BigTable基于列族(Column Family)的数据模型。P2P去中心化的存储。很多方面都可以称之为Dynamo 2.0。
二、安装与配置
- 解压安装包
tar -zxvf apache-cassandra-3.11.3-bin.tar.gz -C ~/training/
- 设置环境变量
CASSANDRA_HOME=/root/training/apache-cassandra-3.11.3
export CASSANDRA_HOME
PATH=$CASSANDRA_HOME/bin:$PATH
export PATH
- 将以下三个地址设置为Linux相应的IP
listen_address: 192.168.56.111
rpc_address: 192.168.56.111
- seeds: "192.168.56.111"
- 启动Cassandra
注意:要以root用户启动Cassandra,需要使用-R参数。
命令:cassandra -R
- 验证Cassandra运行环境:nodetool工具
命令:nodetool status

从Cassandra 2.1版本开始,日志和数据都被存放在logs和data的子目录下。老版本默认保存在/var/log/cassandra和 /var/lib/cassandra。
三、Cassandra的配置参数
核心配置文件:conf/cassandra.yaml,启动过程中的日志信息如下图所示:

- 主要的运行时参数
cluster_name: 集群的名称
storage_port:节点内部通信的端口(默认: 7000)
listen_address:Cassandra绑定的、用来连接其他Cassandra节点的IP地址或者主机名称。(默认: localhost)
native_transport_port:客户端监听CQL本地传输的端口(默认: 9042)
- 目录相关的参数
data_file_directories:这个目录位置就是表数据存储的地方(在SSTables里)。Cassandra将数据均匀的分布在这个位置,受配置的压缩策略粒度的限制。
commitlog_directory:这个目录是commit log 存放的地方。为了获得最佳的写入性能,将commit log放在单独的磁盘分区,或者(理想情况下)和data文件目录分开的物理设备上。commit log只能追加的。
saved_caches_directory:这个目录是table key和row缓存存放的地方。默认位置:$CASSANDRA_HOME/data/saved_caches
hints_directory:设置hints的存储位置(默认: $CASSANDRA_HOME/data/hints)
四、Cassandra的基本操作
(一)登录CQL客户端:cqlsh localhost

- 查看表system.local的结构:

- 查询系统的信息:

- 查看表空间:describe keyspaces;
- 查看已有表:describe tables;
- 查看表结构:describe table table_name;
(二)使用Cassandra的Java客户端
Cassandra使用cql语言作为操作语言,Cassandra在2.0之后,在操作上越来越像sql数据库的操作,这样想从传统关系型数据库,切换到Cassandra的话,上手成本也越来越低。使用官方java驱动操作Cassandra非常简单。maven引入相关的依赖如下所示:
<dependency>
<groupid>info.archinnov</groupid>
<artifactid>achilles-core</artifactid>
<version>6.0.0</version>
<classifier>shaded</classifier>
</dependency>
下面执行CRUD操作:


Original: https://www.cnblogs.com/collen7788/p/14376521.html
Author: 赵渝强老师
Title: 【赵渝强老师】NoSQL数据库之Cassandra基础
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/304787/
转载文章受原作者版权保护。转载请注明原作者出处!