前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >OpenHttps是跨全平台的Actor模式、组件设计的高性能、高并发的超轻量、超迷你的Https框架

OpenHttps是跨全平台的Actor模式、组件设计的高性能、高并发的超轻量、超迷你的Https框架

原创
作者头像
linyouhappy
修改2023-07-20 17:35:23
5260
修改2023-07-20 17:35:23
举报
文章被收录于专栏:OpenLinyouOpenLinyou

OpenHttps

OpenHttps是一款Actor模式、组件设计的高性能、高并发的超轻量、超迷你的跨全平台Https框架。

使用OpenServer开源库开发,小巧迷你,支持IPv6,让C++开发Https如此简单,易如反掌。

由于时间关系,暂时没有实现状态机设计,不过使用OpenFSM库可以轻松实现状态机设计。

OpenHttps也超容易实现Websocket,由于时间关系,暂时不实现。

作为一款C++的Http框架需要满足几点:

  1. 高性能、高并发和跨全平台;
  2. Actor模式、组件设计和状态机设计;
  3. 任意组装,实现各种超难度的网络通信。

由于C++后端开发不适合协程,协程不在考虑范围。

https://github.com/OpenMiniServer

跨平台支持

Linux和安卓使用epoll,Windows使用IOCP(wepoll),iOS和Mac使用kqueue,其他系统使用select。

编译和执行

请安装cmake工具,用cmake可以构建出VS或者XCode工程,就可以在vs或者xcode上编译运行。

源代码:https://github.com/OpenMiniServer/openhttps

https需要openssl支持。如果本地没有opessl库,需要先安装openssl库。如果不安装,关闭USE_OPEN_SSL宏定义。

代码语言:txt
复制
#克隆项目
git clone https://github.com/OpenMiniServer/openhttps
cd ./openhttps
#创建build工程目录
mkdir build
cd build
cmake ..
#如果是win32,在该目录出现openhttps.sln,点击它就可以启动vs写代码调试
make
./helloworld

技术特点

OpenHttps的技术特点:

  1. 跨全平台设计,此服务器框架可以运行在安卓和iOS上。
  2. Linux和安卓使用epoll,Windows使用IOCP(wepoll),iOS和Mac使用kqueue,其他系统使用select。
  3. 支持IPv6,小巧迷你,采用Actor模式和组件设计,通过组件去组装业务。
  4. Actor模式和组件设计,可以非常容易实现高并发和分布式。也可以通过配置文件去定制业务和启动服务。
  5. 一条线程一个actor,一个actor由多个组件组装。用玩积木的方式去做服务器开发。

设计一个使用Https的访问国外网站工具

先声明:本例子只是展示OpenHttps的功能。由于访问国外网站涉及法律问题,由此造成的后果自己承担。

OpenHttps只是OpenServer技术的应用。具体用法见OpenServer。

OpenHttps只实现了三个组件OpenComHttpServer,OpenComHttpAgent和OpenComHttpClient。

OpenComHttpServer负责监听socket,与OpenComHttpAgent配合使用,OpenComHttpAgent处理具体的客户端通信,它们两组成一个web服务器。

OpenComHttpClient只是一个http客户端组件。

接下来就是组装出各自的server。

OpenHttpServer负责listen客户端连接,然后分发给OpenComHttpAgent,它的业务量不多只需要一个。

OpenHttpAgent1负责具体的客户端连接,需要处理大量的客户端连接。需要创建多个server,实现多核处理。

OpenHttpClient负责http客户端请求,可以用它来实现web服务器压力测试。

OpenHttps采用actor模式设计,启动它们只要向它们发送消息即可。

本展示的例子访问国外网站的原理。以访问https://www.bing.com 为例子。

假设服务器运行在本地电脑127.0.0.1上,类似海龟服务器。

在浏览器端,输入http://127.0.0.1/xx 。 使用浏览器插件,往http头加入client字段,client:https://www.bing.com.

浏览器就会请求127.0.0.1的服务器。

OpenHttpServer就会监听到浏览器的请求连接,把socket的fd发给OpenHttpAgent。

OpenHttpAgent解析http报文,发现http请求头含有client,进行移除,把url请求改成client指定的域名。然后把修改好的报文转发给OpenHttpClient。

OpenHttpClient再用修改的http报文向https://www.bing.com 请求。获得的数据立刻转发给OpenHttpAgent。OpenHttpAgent直接把接收到的数据立刻发给浏览器。

当然实际中,是没法实现访问国外网站的。因为访问国外网站

很不稳定,容易掉包,网络连接超时等,都可能导致请求失败。

需要采用UDP方案,而Http协议天生适合UDP通信,由于涉及违法行为,不会提供任何代码。

代码语言:C++
复制
#include <assert.h>
#include <iostream>
#include <stdio.h>
#include "openserver.h"
#include "opencomhttpclient.h"
#include "opencomhttpserver.h"

using namespace open;

class HttpApp :public OpenApp
{
    int balance_;
    OpenServer* server_;
    std::vector<OpenServer*> accepts_;
    std::vector<OpenServer*> clients_;

    static HttpApp OpenApp_;
public:
    static inline HttpApp& Instance() { return OpenApp_; }
    virtual void start()
    {
        OpenApp::start();

        OpenTimer::Run();
        OpenServer::RegisterCom<OpenComHttpServer>("OpenComHttpServer");
        OpenServer::RegisterCom<OpenComHttpAgent>("OpenComHttpAgent");
        OpenServer::RegisterCom<OpenComHttpClient>("OpenComHttpClient");

        accepts_ = {
            OpenServer::StartServer("OpenHttpAgent1", { "OpenComHttpAgent" }),
            OpenServer::StartServer("OpenHttpAgent2", { "OpenComHttpAgent" }),
            OpenServer::StartServer("OpenHttpAgent3", { "OpenComHttpAgent" }),
            OpenServer::StartServer("OpenHttpAgent4", { "OpenComHttpAgent" })
        };
        server_ = OpenServer::StartServer("OpenHttpServer", { "OpenComHttpServer" });
        assert(server_);

        balance_ = 0;
        clients_ = {
            OpenServer::StartServer("OpenHttpClient1", { "OpenComHttpClient" }),
            OpenServer::StartServer("OpenHttpClient2", { "OpenComHttpClient" }),
            OpenServer::StartServer("OpenHttpClient3", { "OpenComHttpClient" }),
            OpenServer::StartServer("OpenHttpClient4", { "OpenComHttpClient" })
        };
    }
    bool httpServer(std::shared_ptr<OpenHttpServerMsg>& protoMsg)
    {

        for (size_t i = 0; i < accepts_.size(); i++)
            protoMsg->vectAccepts_.push_back(accepts_[i]->pid());

        for (size_t i = 0; i < clients_.size(); i++)
            protoMsg->vectClients_.push_back(clients_[i]->pid());

        auto proto = std::shared_ptr<OpenMsgProto>(new OpenMsgProto);
        proto->msg_ = protoMsg;
        bool ret = OpenThread::Send(server_->pid(), proto);
        assert(ret);
        return ret;
    }
    bool httpClient(std::shared_ptr<OpenMsgProtoMsg>& protoMsg, bool isAll)
    {
        if (clients_.empty())
        {
            assert(false);
            return false;
        }
        //isAll
        auto proto = std::shared_ptr<OpenMsgProto>(new OpenMsgProto);
        proto->msg_ = protoMsg;
        bool ret = false;
        if (isAll)
        {
            for (size_t i = 0; i < clients_.size(); i++)
            {
                ret = OpenThread::Send(clients_[i]->pid(), proto);
                assert(ret);
            }
        }
        else
        {
            if (balance_ >= clients_.size()) balance_ = 0;

            OpenServer* client = clients_[balance_++];
            ret = OpenThread::Send(client->pid(), proto);
            assert(ret);
        }
        return ret;
    }
    bool httpClient(std::shared_ptr<OpenHttpRequest>& request)
    {
        if (clients_.empty())
        {
            assert(false);
            return false;
        }
        auto protoMsg = std::shared_ptr<OpenHttpClientSyncMsg>(new OpenHttpClientSyncMsg);
        protoMsg->request_ = request;
        if (balance_ >= clients_.size())
        {
            balance_ = 0;
        }
        OpenServer* client = clients_[balance_++];
        auto proto = std::shared_ptr<OpenMsgProto>(new OpenMsgProto);
        proto->msg_ = protoMsg;
        bool ret = OpenThread::Send(client->pid(), proto);
        assert(ret);
        protoMsg->openSync_.await();
        return ret;
    }
};
HttpApp HttpApp::OpenApp_;

void OnOpenHttpHandle(OpenHttpRequest& req, OpenHttpResponse& resp)
{
#ifdef USE_OPEN_SSL
    //80 redirect to 443
    if (req.listenPort_ == 80)
    {
        resp.code_ = 302;
        resp["location"] = "https://" + req.host_ + ":" + std::to_string(req.port_) + "/" + req.url_;
        return;
    }
#endif
    resp.response(".html", "<html><body><h1>HelloWorld</h1>"
        "<p>Welcome to OpenLinyou</p></body></html>");
}

int main()
{
    HttpApp::Instance().start();

    printf("start HttpServer\n");
    auto msg = std::shared_ptr<OpenHttpServerMsg>(new OpenHttpServerMsg);
    msg->ip_ = "0.0.0.0";
#ifdef USE_OPEN_SSL
    msg->port_ = 443;
    msg->port1_ = 80;
    msg->isHttps_ = 1;
    msg->keyFile_ = "www.xx.com.key";
    msg->certFile_ = "www.xx.com.crt";
#else
    msg->port_ = 80;
    msg->port1_ = 0;
    msg->isHttps_ = 0;
#endif
    msg->handle_ = &OnOpenHttpHandle;
    HttpApp::Instance().httpServer(msg);

    //httpclient
    OpenThread::Sleep(500);

    auto request = std::shared_ptr<OpenHttpRequest>(new OpenHttpRequest);
    request->method_ = "GET";
#ifdef USE_OPEN_SSL
    request->url_ = "https://www.xx.com/";
    (*request)["client"] = "https://www.bing.com/";
#else
    request->url_ = "http://127.0.0.1/";
    (*request)["client"] = "http://www.bing.com/";
#endif
    HttpApp::Instance().httpClient(request);

    auto& response = request->response_;
    std::string head;
    response.getHead(head);
    printf("[http client]code:%d, header:%s\n", response.code_, head.data());
    std::string body;
    response.getBody(body);
    printf("[http client]body:%s\n", body.data());

    HttpApp::Instance().wait();

    return getchar();
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • OpenHttps
    • 跨平台支持
      • 编译和执行
        • 技术特点
          • 设计一个使用Https的访问国外网站工具
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档