简体   繁体   English

自制服务器未收到服务器发送事件

[英]Server Sent Events not received from a self-made server

I'm trying to set up a web server using uvw , a wrapper around libuv .我正在尝试使用uvw设置 web 服务器,它是libuv的包装器。 I've got the server to a point, where i can send out simple responses, and it works without issues.我已经让服务器达到了一定程度,我可以在其中发送简单的响应,并且它可以正常工作。 I wanted to add a SSE route, but no matter what I've tried, i cannot get the browser to properly receive the events I'm trying to send out.我想添加一个 SSE 路由,但无论我尝试了什么,我都无法让浏览器正确接收我试图发送的事件。 Is there something I'm missing about sse?我对 sse 有什么遗漏吗? I've already had success adding delay to a normal html response, but no matter what i change, the client (I'm using Javascripts EventSource) doesn't report any events, and neither does the network tab of the developer tools.我已经成功地为正常的 html 响应添加延迟,但是无论我更改什么,客户端(我使用的是 Javascripts EventSource)都不会报告任何事件,开发人员工具的网络选项卡也不会报告。

My code: main.cpp我的代码:main.cpp

#include <memory>
#include <map>
#include <uvw.hpp>
#include <functional>
#include <iostream>
#include <fstream>
    
using HttpRequestHandler = std::function<void(std::string_view header, uvw::TCPHandle& socket)>;

// Global (yeah) registry of http request handlers
std::map<std::string, HttpRequestHandler> httpHandlers;

// Utility functions for sending messages to tcp sockets 
void send (uvw::TCPHandle& target, std::string message) {
    target.tryWrite(message.data(), message.size());
}
void send (std::shared_ptr<uvw::TCPHandle> target, std::string message) {
    send(*target, message);
}

int main () {
    // Create the loop
    auto loop = uvw::Loop::getDefault();
    // Make a handler for our server
    auto tcp = loop->resource<uvw::TCPHandle>();

    // Setup logic for incoming connections
    tcp->on<uvw::ListenEvent>([](const uvw::ListenEvent& event, uvw::TCPHandle& server){
        std::shared_ptr<uvw::TCPHandle> connection = server.loop().resource<uvw::TCPHandle>();

        // Setup dealing with the other side disconnecting
        connection->once<uvw::EndEvent>([](const auto&, uvw::TCPHandle& connection){ connection.close(); });
        // Setup loging errors
        connection->on<uvw::ErrorEvent>([](const uvw::ErrorEvent& err, uvw::TCPHandle& connection) {
            std::cerr << "Error @ " << connection.sock().ip << ':' << connection.sock().port << "; Message: "
                << err.what() << '\n';
        });

        // Setup dealing with the request
        connection->on<uvw::DataEvent>([](const uvw::DataEvent& event, uvw::TCPHandle& connection){
            std::string_view msg {event.data.get(), event.length};
            
            // Extract the request path
            const auto firstSpaceIndex = msg.find(' ');
            const auto secondSpaceIndex = msg.find(' ', firstSpaceIndex+1);
            std::string_view requestPath {msg.data()+firstSpaceIndex+1, secondSpaceIndex-firstSpaceIndex-1};

            // TODO: Authenticate the other side

            // Use the apropriate handler
            if (httpHandlers.contains(std::string(requestPath))) {
                httpHandlers.at(std::string(requestPath))(msg, connection);
            }
            // Or if there's no good handler, just 404
            else {
                std::string http404Message = "HTTP/1.1 404 Not Found\r\n\r\n";
                connection.write(http404Message.data(), http404Message.size());
                connection.close();
            }
        });

        // Connect the prepared connection object with an actual request
        server.accept(*connection);
        // Start reading the data we're getting
        connection->read();
    });

    // Select a port
    tcp->bind("127.0.0.1", 4242);
    // and connect to it
    tcp->listen();

    // Open the file with the response of our index page
    std::ifstream indexPage ("resources/page.html");
    std::string indexPageMessage {std::istreambuf_iterator<char>(indexPage), std::istreambuf_iterator<char>()};

    // Register the index handler
    httpHandlers["/"] = [message = indexPageMessage](std::string_view header, uvw::TCPHandle& socket) -> void {
        socket.write(const_cast<char*>(message.data()), static_cast<unsigned int>(message.size()));
        socket.close();
    };

    // Register the SSE route handler
    httpHandlers["/gameEvents"] = [](std::string_view header, uvw::TCPHandle& socket) -> void {
        std::cout << "Connecting to gameEvents\n";
        
        // Setup the timer
        auto timer = socket.loop().resource<uvw::TimerHandle>();

        // Send the headerss
        send(socket, 
            "HTTP/1.1 200 OK\r\n"
            "Cache-Control: no-cache\r\n"
            "Content-Type: text/event-stream\r\n"
            "\r\n\r\n"
        );

        // Deal with other side closing
        socket.on<uvw::CloseEvent>([timer = timer->shared_from_this()](auto& ev, auto& socket) {
            timer->close();
            socket.close();
        });

        // React to timer ticks
        timer->on<uvw::TimerEvent>([socket = socket.shared_from_this()](auto& event, auto& timer){
            if(socket->writable()) {
                std::cout << "timer sending timer event\n";
                send(socket, "event: timer\n");
            } else {
                std::cout << "timer encountered unwriteable socket, ignoring for now\n";
            }
        });

        // Trigger the above handler every 5 seconds
        timer->start(uvw::TimerHandle::Time{510}, uvw::TimerHandle::Time{5000});
    };

    // Start the show
    loop->run();
    // Cleanup after ourselfs
    tcp->close();
}

and resources/page.html和资源/page.html

HTTP/1.1 200 OK
Content-Type: text/html;charset=utf-8


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Page</title>
</head>
<body>
    <h2>Hello, world!</h2>
    <script>
        const myEventStream = new EventSource("/gameEvents");
        myEventStream.addEventListener('message', ev => {
            console.log('sse event');
        })
    </script>
</body>
</html>

Found the issue, i was a dumbass, and i didn't send my newlines right.发现了问题,我是个笨蛋,而且我没有正确发送换行符。 The line线

send(socket, "event: timer\n")

should instead have been相反应该是

send(socket, "event: timer\r\n\r\n")

I wasn't sending an extra newline to signify the end of an event, so for the browser i was just sending a single, super long event.我没有发送额外的换行符来表示事件的结束,所以对于浏览器,我只是发送了一个超长的事件。 Also, my javascript was broken - myEventStream.addEventListener('message', ...) would listen to events with the name 'message', not react to any messages.另外,我的 javascript 坏了 - myEventStream.addEventListener('message', ...)会监听名为 'message' 的事件,而不是对任何消息作出反应。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM