【问题标题】:Server-side-event C++ implementation?服务器端事件 C++ 实现?
【发布时间】:2018-08-21 12:49:05
【问题描述】:

我正在尝试实现一个 C++ 服务器来为 javascript EventSource 生成事件,我正在使用 cpprest 构建它。从我在 PHP 或 Node.js 中看到的示例来看,它看起来非常简单,但我一定遗漏了一些东西,因为我在 Firefox 控制台中得到了这个:

Firefox can’t establish a connection to the server at http://localhost:32123/data.

对于 Postman,我正确地收到了 "data : test",所以我认为我错过了一些继续,可能需要做的不仅仅是回复请求,但我没有找到一个很好的解释这是怎么回事应该可以工作了。如果您有一些文档可以指点我,将不胜感激!

html 页面脚本如下所示:

var source = new EventSource("http://localhost:32123/data");

source.onmessage = function (event) {
    document.getElementById("result1").innerHTML += event.data + "<br>";
};

C++ 服务器响应:

wResponse.set_status_code(status_codes::OK);
wResponse.headers().add(U("Access-Control-Allow-Origin"), U("*"));
wResponse.set_body(U("data: test"));
iRequest.reply(wResponse);

我的服务器正在接收的请求:

GET /data HTTP/1.1
Accept: text/event-stream
Accept-Encoding: gzip, deflate
Accept-Language: en-us, en;q=0.5
Cache-Control: no-cache
Connection: keep-alive
Host: localhost:32123
Origin: null
Pragma: no-cache
User-Agent: Mozilla/5.0 (Windows NT6.1; Win64, x64; rv:61.0) Gecko/20100101 Firefox/61.0

【问题讨论】:

  • 您是否考虑过使用开源 HTTP 服务器库(如 libonion...)或客户端库(如 libcurl...)?因为 HTTP 是一个相当复杂的协议。
  • 我不知道 libonion,我一定会去看看! HTTP 实际上相当复杂,但我喜欢 CppRest 的一点是他们在异步方面所做的一切,pplx::task 延续概念 (task1.then(task2).then(task3)) 也是我想尝试的东西。 CppRest 也是开源的。

标签: c++ server-side eventsource cpprest-sdk


【解决方案1】:

Found a solution here

这是一个小证明。它一点也不完美,但它正在工作。下一步是弄清楚如何存储连接,检查它们是否还活着,等等......

编辑:在达伦发表评论后更新答案

正确的解决方案似乎围绕着提供一个绑定到basic_istream&lt;uint8_t&gt;producer_consumer_buffer&lt;char&gt;,该basic_istream&lt;uint8_t&gt; 设置为http_response 主体。

然后,一旦http_request::reply 完成,连接将保持打开状态,直到缓冲区关闭,这可以通过wBuffer.close(std::ios_base::out).wait(); 完成。

我不是 100% 确定,但似乎 wBuffer.sync().wait(); 的行为类似于 PHP flush 命令将用于类似的 event-providing-server 场景。

下面添加了一个工作示例。

显然,这不是一个完整的解决方案。管理连接等还有更多乐趣。用make_unique 实例化一些Connection 并将它们存储到事件访问的容器中可能是我要走的路...

ma​​in.cpp

#include "cpprest/uri.h"
#include "cpprest/producerconsumerstream.h"
#include "cpprest/http_listener.h"

using namespace std;
using namespace web;
using namespace http;
using namespace utility;
using namespace concurrency;
using namespace http::experimental::listener;

struct MyServer
{
  MyServer(string_t url);
  pplx::task<void> open()  { return mListener.open(); };
  pplx::task<void> close() { return mListener.close(); };

private:

  void handleGet(http_request iRequest);
  http_listener mListener;
};

MyServer::MyServer(utility::string_t url) : mListener(url)
{
  mListener.support(methods::GET, bind(&MyServer::handleGet, this, placeholders::_1));
}

void MyServer::handleGet(http_request iRequest)
{
  ucout << iRequest.to_string() << endl;

  http_response wResponse;

  // Setting headers
  wResponse.set_status_code(status_codes::OK);
  wResponse.headers().add(header_names::access_control_allow_origin, U("*"));
  wResponse.headers().add(header_names::content_type, U("text/event-stream"));

  // Preparing buffer
  streams::producer_consumer_buffer<char> wBuffer;
  streams::basic_istream<uint8_t> wStream(wBuffer);
  wResponse.set_body(wStream);

  auto wReplyTask = iRequest.reply(wResponse);

  wBuffer.putn_nocopy("data: a\n",10).wait();
  wBuffer.putn_nocopy("data: b\n\n",12).wait();
  wBuffer.sync().wait();  // seems equivalent to 'flush'

  this_thread::sleep_for(chrono::milliseconds(2000));

  wBuffer.putn_nocopy("data: c\n", 10).wait();
  wBuffer.putn_nocopy("data: d\n\n", 12).wait();
  wBuffer.sync().wait();
  // wBuffer.close(std::ios_base::out).wait();    // closes the connection
  wReplyTask.wait();      // blocking!
}

unique_ptr<MyServer> gHttp;

void onInit(const string_t iAddress)
{
  uri_builder wUri(iAddress);
  auto wAddress = wUri.to_uri().to_string();
  gHttp = unique_ptr<MyServer>(new MyServer(wAddress));

  gHttp->open().wait();
  ucout << string_t(U("Listening for requests at: ")) << wAddress << endl;

}

void onShutdown()
{
  gHttp->close().wait();
}


void main(int argc, wchar_t* argv[])
{

  onInit(U("http://*:32123"));

  cout << "Wait until connection occurs..." << endl;
  getchar();

  onShutdown();
}

sse.htm

<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
</head>
    <body>
        <div id="result"></div>
    </body>
</html>

<script>

    if (typeof (EventSource) !== undefined)
    {
        document.getElementById("result").innerHTML += "SSE supported" + "<br>";
    } 
    else
    {
        document.getElementById("result").innerHTML += "SSE NOT supported" + "<br>";
    }

    var source = new EventSource("http://localhost:32123/");

    source.onopen = function ()
    {
        document.getElementById("result").innerHTML += "open" + "<br>";
    };

    source.onerror = function ()
    {        
        document.getElementById("result").innerHTML += "error" + "<br>";
    };

    source.onmessage = function (event) {
        document.getElementById("result").innerHTML += event.data + "<br>";
    };

</script>

【讨论】:

  • 最好描述或总结链接所说的内容,而不是仅仅给出链接。
  • 我认为您是在说,与其给wResponse.set_body(...); 一个固定的字符串,不如给它一个对流的引用。然后,您可以继续将数据写入该流。是调用wReplyTask.wait();从服务器端关闭连接的方式吗?
  • 嘿@DarrenCook!您完全正确,答案已更新。干杯。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-26
  • 2018-05-19
  • 2017-07-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多