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

2023-07-20 17:35:23 浏览数 (1)

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 HttpServern");
    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:%sn", response.code_, head.data());
    std::string body;
    response.getBody(body);
    printf("[http client]body:%sn", body.data());

    HttpApp::Instance().wait();

    return getchar();
}

0 人点赞