workflow系列教程(5-1)HTTP Server

往期教程

如果觉得写的可以,请给一个点赞+关注支持一下
观看之前请先看,往期的博客教程,否则这篇博客没办法看懂

创建与启动http server

本示例里,我们采用http server的默认参数。创建和启动过程非常简单。

  • process为设置的异步回调函数
  • server.start(port):中port参数表示监听的端口号
cpp 复制代码
WFHttpServer server(process);
if (server.start(port) == 0)
{
    pause();
    server.stop();
}

start()接口有好几个重载函数,在WFServer.h里,可以看到如下一些接口:

cpp 复制代码
class WFServerBase
{
public:
    /* To start TCP server. */
    int start(unsigned short port);
    int start(int family, unsigned short port);
    int start(const char *host, unsigned short port);
    int start(int family, const char *host, unsigned short port);
    int start(const struct sockaddr *bind_addr, socklen_t addrlen);

    /* To start an SSL server */
    int start(unsigned short port, const char *cert_file, const char *key_file);
    int start(int family, unsigned short port,
              const char *cert_file, const char *key_file);
    int start(const char *host, unsigned short port,
              const char *cert_file, const char *key_file);
    int start(int family, const char *host, unsigned short port,
              const char *cert_file, const char *key_file);
    int start(const struct sockaddr *bind_addr, socklen_t addrlen,
              const char *cert_file, const char *key_file);

    /* For graceful restart or multi-process server. */
    int serve(int listen_fd);
    int serve(int listen_fd, const char *cert_file, const char *key_file);

    /* Get the listening address. Used when started a server on a random port. */
    int get_listen_addr(struct sockaddr *addr, socklen_t *addrlen) const;
};

这些接口都比较好理解。任何一个start函数,当端口号为0时,将使用随机端口。此时用户可能需要在server启动完成之后通过get_listen_addr获得实际监听地址。

http echo server的业务逻辑

我们看到在构造http server的时候,传入了一个process参数,这也是一个std::function,定义如下:

cpp 复制代码
using http_process_t = std::function<void (WFHttpTask *)>;
using WFHttpServer = WFServer<protocol::HttpRequest, protocol::HttpResponse>;

template<>
WFHttpServer::WFServer(http_process_t proc) :
    WFServerBase(&HTTP_SERVER_PARAMS_DEFAULT),
    process(std::move(proc))
{
}

其实这个http_proccess_t和的http_callback_t类型是完全一样的。都是处理一个WFHttpTask。

对server来讲,我们的目标就是根据request,填写好response。

同样我们用一个普通函数实现process。逐条读出request的http header写入html页面。

cpp 复制代码
void process(WFHttpTask *server_task)
{
    protocol::HttpRequest *req = server_task->get_req();
    protocol::HttpResponse *resp = server_task->get_resp();
    long seq = server_task->get_task_seq();
    protocol::HttpHeaderCursor cursor(req);
    std::string name;
    std::string value;
    char buf[8192];
    int len;

    /* Set response message body. */
    resp->append_output_body_nocopy("<html>", 6);
    len = snprintf(buf, 8192, "<p>%s %s %s</p>", req->get_method(),
                   req->get_request_uri(), req->get_http_version());
    resp->append_output_body(buf, len);

    while (cursor.next(name, value))
    {
        len = snprintf(buf, 8192, "<p>%s: %s</p>", name.c_str(), value.c_str());
        resp->append_output_body(buf, len);
    }

    resp->append_output_body_nocopy("</html>", 7);

    /* Set status line if you like. */
    resp->set_http_version("HTTP/1.1");
    resp->set_status_code("200");
    resp->set_reason_phrase("OK");

    resp->add_header_pair("Content-Type", "text/html");
    resp->add_header_pair("Server", "Sogou WFHttpServer");
    if (seq == 9) /* no more than 10 requests on the same connection. */
        resp->add_header_pair("Connection", "close");

    struct sockaddr_in addr;
    socklen_t len = sizeof(addr);
    serverTask->get_peer_addr((sockaddr *)&addr,&len);
    if(addr.sin_family == AF_INET){
        fprintf(stderr,"sin_family:AF_INET\n");
        fprintf(stderr,"ip:%s\n", inet_ntoa(addr.sin_addr));
        fprintf(stderr,"port:%d\n",ntohs(addr.sin_port));
    }
}
  • set_http_version设置http版本
  • set_status_code设置状态码
  • set_reason_phrase设置响应dui'x

大多数HttpMessage相关操作之前已经介绍过了,在这里唯一的一个新操作是append_output_body()。

显然让用户生成完整的http body再传给我们并不太高效。用户只需要调用append接口,把离散的数据一块块扩展到message里就可以了。

append_output_body()操作会把数据复制走,另一个带_nocopy后缀的接口会直接引用指针,使用时需要注意不可以指向局部变量。

相关几个调用在HttpMessage.h可以看到其声明:

cpp 复制代码
class HttpMessage
{
public:
    bool append_output_body(const void *buf, size_t size);
    bool append_output_body_nocopy(const void *buf, size_t size);
    ...
    bool append_output_body(const std::string& buf);
};

再次强调,使用append_output_body_nocopy()接口时,buf指向的数据的生命周期至少需要延续到task的callback。

函数中另外一个变量seq,通过server_task->get_task_seq()得到,表示该请求是当前连接上的第几次请求,从0开始计。

程序中,完成10次请求之后就强行关闭连接,于是:

cpp 复制代码
    if (seq == 9) /* no more than 10 requests on the same connection. */
        resp->add_header_pair("Connection", "close");

关闭连接还可以通过task->set_keep_alive()接口来完成,但对于http协议,还是推荐使用设置header的方式。

这个示例中,因为返回的页面很小,我们没有关注回复成功与否。

相关推荐
猫头虎9 小时前
如何在浏览器里体验 Windows在线模拟器:2026最新在线windows模拟器资源合集与技术揭秘
运维·网络·windows·系统架构·开源·运维开发·开源软件
不一样的故事12610 小时前
下的 “Wi-Fi参数配置” 列表,但您当前选中的导航菜单项是 “IP规划”。您遇到的 “IP加载不出来” 问题,很可能
网络协议·tcp/ip·华为
古城小栈11 小时前
Rust 网络请求库:reqwest
开发语言·网络·rust
qq_3168377514 小时前
IP网段冲突 配置指定ip使用指定的网络接口发送,而不经过默认网关
服务器·网络·tcp/ip
布史14 小时前
Tailscale虚拟私有网络指南
linux·网络
枷锁—sha15 小时前
彻底解决 Google Gemini 报错:异常流量与 IP 地址冲突排查指南
网络·网络协议·tcp/ip
Xの哲學15 小时前
深入剖析Linux文件系统数据结构实现机制
linux·运维·网络·数据结构·算法
-To be number.wan16 小时前
经典真题精讲|2010年408统考第34题:文件传输最少需要多久?
网络·计算机网络
逐梦苍穹16 小时前
不用公网 IP,把内网服务安全发布到公网:ZeroNews 快速上手
网络协议·tcp/ip·安全·内网穿透
知乎的哥廷根数学学派17 小时前
基于多尺度注意力机制融合连续小波变换与原型网络的滚动轴承小样本故障诊断方法(Pytorch)
网络·人工智能·pytorch·python·深度学习·算法·机器学习