您现在的位置是:首页 > 正文

libevent实现https服务器 热门推荐

2024-02-01 00:54:20阅读 2

libevent实现https服务器


./configure --prefix=/usr/local CPPFLAGS="-I/usr/local/Cellar/openssl/1.0.2h_1/include" LDFLAGS="-I/usr/local/Cellar/openssl/1.0.2h_1/lib"


参考老外服务器代码:

+

https://github.com/ppelleti/https-example

+

确保libevent在2.1.2之上版本。

+

确保系统安装openssl,确保libevent_openssl.so存在

+

搭建支持http是的libevent

1下载libevent 和 openssl

libevent

+

http://libevent.org/

+

2 安装openssl

wget http://www.openssl.org/source/openssl-1.0.1t.tar.gz
tar -zxvf openssl-1.0.1c.tar.gz
./config  --prefix=/usr/local --openssldir=/usr/local/openssl  

make depend
make
sudo make install



//若要生成libssl.so动态库文件 需要如下make
make clean
./config shared --prefix=/usr/local --openssldir=/usr/local/openssl  
make depend
make
sudo make install

*这里要注意,在执行./config的时候一定要加上 --prefix=/usr/local--openssldir=/usr/local/ssl/

+

否则libevent是找不到openssl库,那就不会编译生成带有openssl的libevent库了。*

+

3安装libevent

tar -zxvf libevent-release-2.1.6-beta.tar.gz
./autogen.sh

问题(1)如果出现

+

./autogen.sh: 11: ./autogen.sh: aclocal: not found

需要安装automake工具。

+

sudo apt-get install automake

问题(2) 如果出现

+

configure.ac:137: error: possibly undefined macro: AC_PROG_LIBTOOL

需要安装libtool工具。

+

sudo apt-get install libtool  
sudo apt-get install libsysfs-dev

如果成功,执行

+

./configure
make
sudo make install

登陆服务实现

结合我们之前用libevent实现的登陆服务器代码和老外的参考代码,

+

现在提供登陆的功能的https服务器代码.

+

此时服务器路径文件包括以下:

+

$ls
cJSON.c         https-common.h  server-private-key.pem
cJSON.h         https-server.c  server-certificate-chain.pem
https-common.c  Makefile

其中 server-certificate-chain.pem 为 当前https服务器的私人证书,此证书并没有CA认证,如果想要合法证书请购买。(改证书里携带 此服务器 认证公钥)

+

其中 server-private-key.pem 是此服务器 认证私钥

+

其中 https-common.h https-server.c https-common.c 为https服务器源码

+

https-common.h

+

/* (c)  Oblong Industries */

#ifndef COMMON_MAN
#define COMMON_MAN

/**
 * Rather than using the standard https port of 443, use this one.
 */
#define COMMON_HTTPS_PORT 8421

/**
 * This is the string the client tells the server in the POST request.
 */
#define COMMON_PASSCODE "R23"

/**
 * If an OpenSSL function returns a return value indicating failure
 * (for non-pointery functions, generally 1 means success, and non-1
 * means failure), then usually it ought to have left one or more
 * entries in the OpenSSL "error stack", which is a bit of thread-local
 * state containing error information.
 *
 * This function is a simple way to handle OpenSSL errors (something
 * better may be needed in a real application), which prints the
 * name of the function which failed (which you must supply as an
 * argument), prints the OpenSSL error stack (which hopefully says
 * something meaningful) and exits.
 */
void die_most_horribly_from_openssl_error (const char *func);

void error_exit (const char *fmt, ...);

#define error_report printf
#define info_report printf

/**
 * Calls some OpenSSL setup functions, which both the client and
 * server need to do.
 */
void common_setup (void);

#endif  /* COMMON_MAN */

https-common.c

+

/* (c)  Oblong Industries */

#include <signal.h>

#include "https-common.h"

#include <openssl/ssl.h>
#include <openssl/err.h>

#include <event2/event.h>


/* 这个是调用openSSL提供的打印log接口 */
void die_most_horribly_from_openssl_error (const char *func)
{ 
    fprintf (stderr, "%s failed:\n", func);

    /* This is the OpenSSL function that prints the contents of the
     * error stack to the specified file handle. */
    ERR_print_errors_fp (stderr);

    exit (EXIT_FAILURE);
}

void error_exit (const char *fmt, ...)
{ va_list ap;

    va_start (ap, fmt);
    vfprintf (stderr, fmt, ap);
    va_end (ap);

    exit (EXIT_FAILURE);
}

/* OpenSSL has a habit of using uninitialized memory.  (They turn up their
 * nose at tools like valgrind.)  To avoid spurious valgrind errors (as well
 * as to allay any concerns that the uninitialized memory is actually
 * affecting behavior), let's install a custom malloc function which is
 * actually calloc.
 */
static void *my_zeroing_malloc (size_t howmuch)
{ 
    return calloc (1, howmuch); 
}

void common_setup (void)
{ 
    signal (SIGPIPE, SIG_IGN);

    CRYPTO_set_mem_functions (my_zeroing_malloc, realloc, free);
    SSL_library_init ();
    SSL_load_error_strings ();
    OpenSSL_add_all_algorithms ();

    printf ("Using OpenSSL version \"%s\"\nand libevent version \"%s\"\n",
            SSLeay_version (SSLEAY_VERSION),
            event_get_version ());
}

https-server.c

+

/**
 * @file https-server.c
 * @brief  
 * @author liu_danbing
 * @version 1.0
 * @date 2016-10-26
 */

#include "https-common.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

#include <sys/types.h>
#include <sys/stat.h>

#include <sys/stat.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>

#include <openssl/ssl.h>
#include <openssl/err.h>

#include <event2/bufferevent.h>
#include <event2/bufferevent_ssl.h>
#include <event2/event.h>
#include <event2/http.h>
#include <event2/buffer.h>
#include <event2/util.h>
#include <event2/keyvalq_struct.h>

#include <cJSON.h>

#define MYHTTPD_SIGNATURE   "MoCarHttpd v0.1"


#ifdef EVENT__HAVE_NETINET_IN_H
#include <netinet/in.h>
# ifdef _XOPEN_SOURCE_EXTENDED
#  include <arpa/inet.h>
# endif
#endif

unsigned short serverPort = COMMON_HTTPS_PORT;

/* Instead of casting between these types, create a union with all of them,
 * to avoid -Wstrict-aliasing warnings. */
typedef union
{ 
    struct sockaddr_storage ss;
    struct sockaddr sa;
    struct sockaddr_in in;
    struct sockaddr_in6 i6;
} sock_hop;


/* 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().
 */
static void
login_cb (struct evhttp_request *req, void *arg)
{ 
    struct evbuffer *evb = NULL;
    const char *uri = evhttp_request_get_uri (req);
    struct evhttp_uri *decoded = NULL;

    /* 判断 req 是否是GET 请求 */
    if (evhttp_request_get_command (req) == EVHTTP_REQ_GET)
    {
        struct evbuffer *buf = evbuffer_new();
        if (buf == NULL) return;
        evbuffer_add_printf(buf, "Requested: %s\n", uri);
        evhttp_send_reply(req, HTTP_OK, "OK", buf);
        return;
    }

    /* 这里只处理Post请求, Get请求,就直接return 200 OK  */
    if (evhttp_request_get_command (req) != EVHTTP_REQ_POST)
    { 
        evhttp_send_reply (req, 200, "OK", NULL);
        return;
    }

    printf ("Got a POST request for <%s>\n", uri);

    //判断此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;
    }

    /* Decode the payload */
    struct evbuffer *buf = evhttp_request_get_input_buffer (req);
    evbuffer_add (buf, "", 1);    /* NUL-terminate the buffer */
    char *payload = (char *) evbuffer_pullup (buf, -1);
    int post_data_len = evbuffer_get_length(buf);
    char request_data_buf[4096] = {0};
    memcpy(request_data_buf, payload, post_data_len);
    printf("[post_data][%d]=\n %s\n", post_data_len, payload);


    /*
       具体的:可以根据Post的参数执行相应操作,然后将结果输出
       ...
    */
    //unpack json
    cJSON* root = cJSON_Parse(request_data_buf);
    cJSON* username = cJSON_GetObjectItem(root, "username");
    cJSON* password = cJSON_GetObjectItem(root, "password");

    printf("username = %s\n", username->valuestring);
    printf("password = %s\n", password->valuestring);

    cJSON_Delete(root);


    //packet json
    root = cJSON_CreateObject();

    cJSON_AddStringToObject(root, "result", "ok");
    cJSON_AddStringToObject(root, "sessionid", "xxxxxxxx");

    char *response_data = cJSON_Print(root);
    cJSON_Delete(root);




    /* This holds the content we're sending. */

    //HTTP header


    evhttp_add_header(evhttp_request_get_output_headers(req), "Server", MYHTTPD_SIGNATURE);
    evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", "text/plain; charset=UTF-8");
    evhttp_add_header(evhttp_request_get_output_headers(req), "Connection", "close");

    evb = evbuffer_new ();
    evbuffer_add_printf(evb, "%s", response_data);
    //将封装好的evbuffer 发送给客户端
    evhttp_send_reply(req, HTTP_OK, "OK", evb);

    if (decoded)
        evhttp_uri_free (decoded);
    if (evb)
        evbuffer_free (evb);


    printf("[response]:\n");
    printf("%s\n", response_data);

    free(response_data);
}

/**
 * This callback is responsible for creating a new SSL connection
 * and wrapping it in an OpenSSL bufferevent.  This is the way
 * we implement an https server instead of a plain old http server.
 */
static struct bufferevent* bevcb (struct event_base *base, void *arg)
{ 
    struct bufferevent* r;
    SSL_CTX *ctx = (SSL_CTX *) arg;

    r = bufferevent_openssl_socket_new (base,
            -1,
            SSL_new (ctx),
            BUFFEREVENT_SSL_ACCEPTING,
            BEV_OPT_CLOSE_ON_FREE);
    return r;
}

static void server_setup_certs (SSL_CTX *ctx,
        const char *certificate_chain,
        const char *private_key)
{ 
    info_report ("Loading certificate chain from '%s'\n"
            "and private key from '%s'\n",
            certificate_chain, private_key);

    if (1 != SSL_CTX_use_certificate_chain_file (ctx, certificate_chain))
        die_most_horribly_from_openssl_error ("SSL_CTX_use_certificate_chain_file");

    if (1 != SSL_CTX_use_PrivateKey_file (ctx, private_key, SSL_FILETYPE_PEM))
        die_most_horribly_from_openssl_error ("SSL_CTX_use_PrivateKey_file");

    if (1 != SSL_CTX_check_private_key (ctx))
        die_most_horribly_from_openssl_error ("SSL_CTX_check_private_key");
}

static int serve_some_http (void)
{ 
    struct event_base *base;
    struct evhttp *http;
    struct evhttp_bound_socket *handle;


    base = event_base_new ();
    if (! base)
    { 
        fprintf (stderr, "Couldn't create an event_base: exiting\n");
        return 1;
    }

    /* 创建一个 evhttp 句柄,去处理用户端的requests请求 */
    http = evhttp_new (base);
    if (! http)
    { fprintf (stderr, "couldn't create evhttp. Exiting.\n");
        return 1;
    }

    /* 创建SSL上下文环境 ,可以理解为 SSL句柄 */
    SSL_CTX *ctx = SSL_CTX_new (SSLv23_server_method ());
    SSL_CTX_set_options (ctx,
            SSL_OP_SINGLE_DH_USE |
            SSL_OP_SINGLE_ECDH_USE |
            SSL_OP_NO_SSLv2);

    /* Cheesily pick an elliptic curve to use with elliptic curve ciphersuites.
     * We just hardcode a single curve which is reasonably decent.
     * See http://www.mail-archive.com/openssl-dev@openssl.org/msg30957.html */
    EC_KEY *ecdh = EC_KEY_new_by_curve_name (NID_X9_62_prime256v1);
    if (! ecdh)
        die_most_horribly_from_openssl_error ("EC_KEY_new_by_curve_name");
    if (1 != SSL_CTX_set_tmp_ecdh (ctx, ecdh))
        die_most_horribly_from_openssl_error ("SSL_CTX_set_tmp_ecdh");

    /* 选择服务器证书 和 服务器私钥. */
    const char *certificate_chain = "server-certificate-chain.pem";
    const char *private_key = "server-private-key.pem";
    /* 设置服务器证书 和 服务器私钥 到 
     OPENSSL ctx上下文句柄中 */
    server_setup_certs (ctx, certificate_chain, private_key);

    /* 
        使我们创建好的evhttp句柄 支持 SSL加密
        实际上,加密的动作和解密的动作都已经帮
        我们自动完成,我们拿到的数据就已经解密之后的
    */
    evhttp_set_bevcb (http, bevcb, ctx);

    /* 设置http回调函数 */
    //默认回调
    //evhttp_set_gencb (http, send_document_cb, NULL);
    //专属uri路径回调
    evhttp_set_cb(http, "/login", login_cb, NULL);

    /* 设置监听IP和端口 */
    handle = evhttp_bind_socket_with_handle (http, "0.0.0.0", serverPort);
    if (! handle)
    { 
        fprintf (stderr, "couldn't bind to port %d. Exiting.\n",(int) serverPort);
        return 1;
    }

    { 
        /* 打印收到的客户端链接信息  */
        sock_hop 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, &ss.sa, &socklen))
        { 
            perror ("getsockname() failed");
            return 1;
        }
        if (ss.ss.ss_family == AF_INET)
        { 
            got_port = ntohs (ss.in.sin_port);
            inaddr = &ss.in.sin_addr;
        }
        else if (ss.ss.ss_family == AF_INET6)
        { 
            got_port = ntohs (ss.i6.sin6_port);
            inaddr = &ss.i6.sin6_addr;
        }
        else
        { 
            fprintf (stderr, "Weird address family %d\n", ss.ss.ss_family);
            return 1;
        }
        addr = evutil_inet_ntop (ss.ss.ss_family, inaddr, addrbuf,
                sizeof (addrbuf));
        if (addr)
            printf ("Listening on %s:%d\n", addr, got_port);
        else
        { 
            fprintf (stderr, "evutil_inet_ntop failed\n");
            return 1;
        }
    }

    /* 开始阻塞监听 (永久执行) */
    event_base_dispatch (base);


    return 0;
}

int main (int argc, char **argv)
{ 
    /*OpenSSL 初始化 */
    common_setup ();              

    if (argc > 1) {
        char *end_ptr;
        long lp = strtol(argv[1], &end_ptr, 0);
        if (*end_ptr) {
            fprintf(stderr, "Invalid integer\n");
            return -1;
        }
        if (lp <= 0) {
            fprintf(stderr, "Port must be positive\n");
            return -1;
        }
        if (lp >= USHRT_MAX) {
            fprintf(stderr, "Port must fit 16-bit range\n");
            return -1;
        }

        serverPort = (unsigned short)lp;
    }

    /* now run http server (never returns) */
    return serve_some_http ();
}

Makefile

+

CC=gcc
CPPFLAGS= -I. -I./include 
CFLAGS=-Wall 
LIBS=-lpthread -levent -lm -lssl -lcrypto -levent_openssl

#找到当前目录下所有的.c文件
src = $(wildcard *.c)

#将当前目录下所有的.c  转换成.o给obj
obj = $(patsubst %.c, %.o, $(src))

server=server
target=$(server)
ALL:$(target)


#生成所有的.o文件
$(obj):%.o:%.c
    $(CC) -c $< -o $@ $(CPPFLAGS) $(CFLAGS) 

#test_main程序
$(server): https-server.o https-common.o cJSON.o
    $(CC) $^ -o $@ $(LIBS)

#clean指令

clean:
    -rm -rf $(obj) $(target) 

distclean:
    -rm -rf $(obj) $(target) 

#将clean目标 改成一个虚拟符号
.PHONY: clean ALL distclean










./configure --prefix=/usr/local CPPFLAGS="-I/usr/local/Cellar/openssl/1.0.2h_1/include" LDFLAGS="-I/usr/local/Cellar/openssl/1.0.2h_1/lib"

 

网站文章

  • 计数排序(counting sort)

    计数排序(Counting sort)是一种稳定的线性时间排序算法。计数排序使用一个额外的数组 C ,其中第i个元素是待排序数组A中值等于 i的元素的个数。然后根据数组 C 来将 A中的元素排到正确的位置。计数排序特征当输入的元素是n个 0 到k 之间的整数时,它的运行时间是Θ\ThetaΘ (n+k)。计数排序不是比较排序,排序的速度快于任何比较排序算法。由于用来计数的数组 C 的长度...

    2024-02-01 00:54:14
  • JAVA中四种线程池

    JAVA中四种线程池

    四种线程池newFixedThreadPoolnewCachedThreadPoolnewSingleThreadExecutornewScheduledThreadPool newFixedThre...

    2024-02-01 00:53:40
  • 轻量级日志管理系统-loki 简单部署

    轻量级日志管理系统-loki 简单部署

    使用grafana开发的loki工具,10分钟手动搭建一个轻量级日志管理系统

    2024-02-01 00:53:32
  • 测试开发工程师面试总结(二)——算法篇

    算法也属于常见面试内容之一,但基本不会超过《剑指offer》的范围,在此附上一篇简书上整理的内容: 第二版java解法 常见的面试题包括以下几类:字符串操作,文件输入输出流及统计,矩阵操作,单例模式等。 1.针对字符串的操作:如字符串反转、字符串去重、含有左右括号的字符串匹配。 含有左右括号的字符串匹配的题目及代码如下: 给定一个字符串,其中的字符只包含三种括号:花...

    2024-02-01 00:53:04
  • 适合 Go 新手学习的开源项目——在 GitHub 学编程

    适合 Go 新手学习的开源项目——在 GitHub 学编程

    作者:HelloGitHub-小鱼干&amp;卤蛋 故事要从 2007 年说起。因为受够了 C++ 煎熬的 Google 首席软件工程师 Rob Pike 召集 Robert Griesemer 和 ...

    2024-02-01 00:52:57
  • 怎么制作gif动态图 QQ动态表情包怎么制作

    怎么制作gif动态图 QQ动态表情包怎么制作

    在平时的聊天中经常会使用到GIF动图,不仅仅可以缓解气氛,还很有趣,那这些动态图是如何制作的呢?没有想象的那么难,今天来看看怎么制作的吧! 1、先准备好素材,要制作什么样的动图,可以是图片也可以是...

    2024-02-01 00:52:50
  • 系统架构设计专业技能 · 系统工程与系统性能

    系统架构设计专业技能 · 系统工程与系统性能

    系统工程的生命周期阶段包括。

    2024-02-01 00:52:42
  • java 接口bean_详解Spring中接口的bean是如何注入的

    java 接口bean_详解Spring中接口的bean是如何注入的

    java 接口bean_详解Spring中接口的bean是如何注入的

    2024-02-01 00:52:14
  • Tomcat的使用

    Tomcat的使用

    Tomcat下载tomcat官网:http://tomcat.apache.org安装直接将下载的tomcat压缩包解压即可*注意:此处建议不要安装在有空格或中文目录下卸载直接将解压的文件删除即可目录解析(apache开源项目通用结构)bin 可执行文件conf 配置文件lib 依赖j...

    2024-02-01 00:52:06
  • P4145 上帝造题的七分钟2 / 花神游历各国 (线段树区间开方)

    题目链接:点击这里 题目大意: 给定一个长度为 nnn 的序列 a1,a2,...,ana_1,a_2,...,a_na1,a2,...,an ,对序列进行区间开方和区间查询 题目分析: 因为 1≤a...

    2024-02-01 00:51:59