问题描述
我正在尝试使用 uvw(围绕 libuv 的包装器)设置网络服务器。我已经让服务器达到了一个点,在那里我可以发送简单的响应,并且它可以正常工作。我想添加一个 SSE 路由,但无论我尝试过什么,我都无法让浏览器正确接收我尝试发送的事件。我对 sse 有什么遗漏吗?我已经成功地为正常的 html 响应添加了延迟,但是无论我如何更改,客户端(我使用的是 Javascripts EventSource)都不会报告任何事件,开发人员工具的网络选项卡也不会。
我的代码: 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();
}
和资源/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>
解决方法
发现问题,我是个笨蛋,我没有正确发送换行符。 线
send(socket,"event: timer\n")
应该是
send(socket,"event: timer\r\n\r\n")
我没有发送额外的换行符来表示事件的结束,所以对于浏览器,我只是发送了一个超长的事件。此外,我的 javascript 已损坏 - myEventStream.addEventListener('message',...)
会侦听名为“消息”的事件,而不对任何消息做出反应。