【Web开发】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

官网地址:
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 框架。

【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)

; 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 兼容、功能丰富、支持良好、速度快、文档完整,并且已被许多知名、大型和成功的公司使用。

【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)

; 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下载源代码
    【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)
  • 源代码文件夹如下:
    【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)
mkdir build
cd build
cmake ..

cmake .. -DENABLE_CJSON_UTILS=On -DENABLE_CJSON_TEST=Off

【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)
  • 生成的工程文件如下:
    【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)
  • 举例说明:
    这里有一个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);

【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)

2、libevent

2.1 下载

https://github.com/libevent/libevent

【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)

; 2.2 VS2017编译

采用最新的版本2.1.12-stable。

【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)

2.3 VS2008编译

采用比较早期版本,比如2.0.22-stable。

【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)
【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)
  • 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

【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)
cd C:\Users\tomcat\Desktop\libevent-2.0.22-stable.tar\libevent-2.0.22-stable
nmake Makefile.nmake

【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)
【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)
考虑到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 下载

https://curl.se/download.html

【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)

; 3.2 编译

解压上述下载的源码包,如下所示:

[En]

Extract the source code package downloaded above as follows:

【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)
【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)
【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)
【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)
【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)
【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)

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

【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)
  • 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

【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)
  • 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)通过官网提供的压缩包下载源代码

【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)

4.2 编译

下载源代码后,可以通过两种方式进行编译。

[En]

After the source code is downloaded, it can be compiled in two ways.

(1)运行bat文件生成vs工程文件
(2)通过cmake生成vs工程文件

【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)

; 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 下载

http://libuv.org/

【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)

【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)

; 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

结语

&#x5982;&#x679C;&#x60A8;&#x89C9;&#x5F97;&#x8BE5;&#x65B9;&#x6CD5;&#x6216;&#x4EE3;&#x7801;&#x6709;&#x4E00;&#x70B9;&#x70B9;&#x7528;&#x5904;&#xFF0C;&#x53EF;&#x4EE5;&#x7ED9;&#x4F5C;&#x8005;&#x70B9;&#x4E2A;&#x8D5E;&#xFF0C;&#x6216;&#x6253;&#x8D4F;&#x676F;&#x5496;&#x5561;&#xFF1B;╮( ̄▽ ̄)╭
&#x5982;&#x679C;&#x60A8;&#x611F;&#x89C9;&#x65B9;&#x6CD5;&#x6216;&#x4EE3;&#x7801;&#x4E0D;&#x548B;&#x5730;//(ㄒoㄒ)// &#xFF0C;&#x5C31;&#x5728;&#x8BC4;&#x8BBA;&#x5904;&#x7559;&#x8A00;&#xFF0C;&#x4F5C;&#x8005;&#x7EE7;&#x7EED;&#x6539;&#x8FDB;&#xFF1B;o_O???
&#x5982;&#x679C;&#x60A8;&#x9700;&#x8981;&#x76F8;&#x5173;&#x529F;&#x80FD;&#x7684;&#x4EE3;&#x7801;&#x5B9A;&#x5236;&#x5316;&#x5F00;&#x53D1;&#xFF0C;&#x53EF;&#x4EE5;&#x7559;&#x8A00;&#x79C1;&#x4FE1;&#x4F5C;&#x8005;&#xFF1B;(✿◡‿◡)
&#x611F;&#x8C22;&#x5404;&#x4F4D;&#x5927;&#x4F6C;&#x7AE5;&#x978B;&#x4EEC;&#x7684;&#x652F;&#x6301;&#xFF01;( ´ ▽´ )ノ ( ´ ▽´)っ!!!

【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)

Original: https://blog.csdn.net/hhy321/article/details/124780185
Author: 爱看书的小沐
Title: 【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)



相关阅读

Title: 【赵渝强老师】NoSQL数据库之Cassandra基础

【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)

一、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
&#x6CE8;&#x610F;&#xFF1A;&#x8981;&#x4EE5;root&#x7528;&#x6237;&#x542F;&#x52A8;Cassandra&#xFF0C;&#x9700;&#x8981;&#x4F7F;&#x7528;-R&#x53C2;&#x6570;&#x3002;
&#x547D;&#x4EE4;&#xFF1A;cassandra -R
  • 验证Cassandra运行环境:nodetool工具
&#x547D;&#x4EE4;&#xFF1A;nodetool status

【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)

从Cassandra 2.1版本开始,日志和数据都被存放在logs和data的子目录下。老版本默认保存在/var/log/cassandra和 /var/lib/cassandra。

三、Cassandra的配置参数

核心配置文件:conf/cassandra.yaml,启动过程中的日志信息如下图所示:

【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)
  • 主要的运行时参数
cluster_name: &#x96C6;&#x7FA4;&#x7684;&#x540D;&#x79F0;
storage_port&#xFF1A;&#x8282;&#x70B9;&#x5185;&#x90E8;&#x901A;&#x4FE1;&#x7684;&#x7AEF;&#x53E3;(&#x9ED8;&#x8BA4;: 7000)
listen_address&#xFF1A;Cassandra&#x7ED1;&#x5B9A;&#x7684;&#x3001;&#x7528;&#x6765;&#x8FDE;&#x63A5;&#x5176;&#x4ED6;Cassandra&#x8282;&#x70B9;&#x7684;IP&#x5730;&#x5740;&#x6216;&#x8005;&#x4E3B;&#x673A;&#x540D;&#x79F0;&#x3002;(&#x9ED8;&#x8BA4;: localhost)
native_transport_port&#xFF1A;&#x5BA2;&#x6237;&#x7AEF;&#x76D1;&#x542C;CQL&#x672C;&#x5730;&#x4F20;&#x8F93;&#x7684;&#x7AEF;&#x53E3;(&#x9ED8;&#x8BA4;: 9042)
  • 目录相关的参数
data_file_directories&#xFF1A;&#x8FD9;&#x4E2A;&#x76EE;&#x5F55;&#x4F4D;&#x7F6E;&#x5C31;&#x662F;&#x8868;&#x6570;&#x636E;&#x5B58;&#x50A8;&#x7684;&#x5730;&#x65B9;(&#x5728;SSTables&#x91CC;)&#x3002;Cassandra&#x5C06;&#x6570;&#x636E;&#x5747;&#x5300;&#x7684;&#x5206;&#x5E03;&#x5728;&#x8FD9;&#x4E2A;&#x4F4D;&#x7F6E;&#xFF0C;&#x53D7;&#x914D;&#x7F6E;&#x7684;&#x538B;&#x7F29;&#x7B56;&#x7565;&#x7C92;&#x5EA6;&#x7684;&#x9650;&#x5236;&#x3002;
commitlog_directory&#xFF1A;&#x8FD9;&#x4E2A;&#x76EE;&#x5F55;&#x662F;commit log &#x5B58;&#x653E;&#x7684;&#x5730;&#x65B9;&#x3002;&#x4E3A;&#x4E86;&#x83B7;&#x5F97;&#x6700;&#x4F73;&#x7684;&#x5199;&#x5165;&#x6027;&#x80FD;&#xFF0C;&#x5C06;commit log&#x653E;&#x5728;&#x5355;&#x72EC;&#x7684;&#x78C1;&#x76D8;&#x5206;&#x533A;&#xFF0C;&#x6216;&#x8005;(&#x7406;&#x60F3;&#x60C5;&#x51B5;&#x4E0B;)&#x548C;data&#x6587;&#x4EF6;&#x76EE;&#x5F55;&#x5206;&#x5F00;&#x7684;&#x7269;&#x7406;&#x8BBE;&#x5907;&#x4E0A;&#x3002;commit log&#x53EA;&#x80FD;&#x8FFD;&#x52A0;&#x7684;&#x3002;
saved_caches_directory&#xFF1A;&#x8FD9;&#x4E2A;&#x76EE;&#x5F55;&#x662F;table key&#x548C;row&#x7F13;&#x5B58;&#x5B58;&#x653E;&#x7684;&#x5730;&#x65B9;&#x3002;&#x9ED8;&#x8BA4;&#x4F4D;&#x7F6E;&#xFF1A;$CASSANDRA_HOME/data/saved_caches
hints_directory&#xFF1A;&#x8BBE;&#x7F6E;hints&#x7684;&#x5B58;&#x50A8;&#x4F4D;&#x7F6E;(&#x9ED8;&#x8BA4;: $CASSANDRA_HOME/data/hints)

四、Cassandra的基本操作

(一)登录CQL客户端:cqlsh localhost

【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)
  • 查看表system.local的结构:

【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)
  • 查询系统的信息:

【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)
  • 查看表空间: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操作:

【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)

【Web开发】C++实现Web服务器(libevent,libcurl,libuv,poco)

Original: https://www.cnblogs.com/collen7788/p/14376521.html
Author: 赵渝强老师
Title: 【赵渝强老师】NoSQL数据库之Cassandra基础

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/304787/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

最近整理资源【免费获取】:   👉 程序员最新必读书单  | 👏 互联网各方向面试题下载 | ✌️计算机核心资源汇总