Reputation: 11
I am trying to send boost/asio IP commands withing a REST server and it appears as if nothing actually happens. For better context I have a method that will send the IP commands when a watchdog timer times out; If I call the function before the REST listen method, the commands will be sent and everything works, but as soon as the REST server starts I am no longer able to use boost/asio. I am not very experienced with boost/asio so I am very puzzled.
Here is an example of how the basic structure of my code is:
#include <iostream>
#include <boost/asio.hpp>
#include "../include/httplib.h"
#include "../include/json.hpp"
using boost::asio::ip::tcp;
static boost::asio::io_context io_context;
static tcp::socket testSocket(io_context);
boost::system::error_code boost_error;
std::string testIP = "192.168.9.114";
std::string testPort = "20000";
int serverPort__ = 8077;
static httplib::Server server_;
namespace RestServer {
std::string what(const std::exception_ptr &eptr = std::current_exception()) {
if (!eptr) { throw std::bad_exception(); }
try { std::rethrow_exception(eptr); }
catch (const std::exception &e) { return e.what() ; }
catch (const std::string &e) { return e ; }
catch (const char *e) { return e ; }
catch (...) { return "who knows"; }
}
int main() {
std::cout << "Starting REST server at port " << serverPort__ << std::endl;
if (!server_.is_valid()) {
std::cout << "Rest server has an error..." << std::endl;
return -1;
}
server_.Get("/stop", [&](const httplib::Request &req, httplib::Response &res) {
std::cout << "Rest server is stopping..." << std::endl;
(void)req;
(void)res;
server_.stop();
});
server_.set_error_handler([](const httplib::Request &req, httplib::Response &res) {
const char *fmt = "<p>Error Status: <span style='color:red;'>%d</span></p>";
char buf[BUFSIZ];
if (res.body.length() > 4) {
snprintf(buf, sizeof(buf), "<p>Error Status: <span style='color:red;'>%d</span></p><p>Message: %s</p>", res.status, res.body.c_str());
}
else {
snprintf(buf, sizeof(buf), fmt, res.status);
}
res.set_content(buf, "text/html");
});
server_.set_exception_handler([](const httplib::Request &req, httplib::Response &res, std::exception_ptr ep) {
std::cout << "REST Exception! " << what(ep) << std::endl;
});
server_.Get("/TEST", [](const httplib::Request &req, httplib::Response &res) {
nlohmann::json jsonRes;
std::string cmdStr = req.get_param_value("cmd");
std::string opStr = req.get_param_value("op");
uint8_t operation = 0;
uint8_t command = 0;
try {
operation = std::stoi(opStr);
command = std::stoi(cmdStr);
}
catch (std::exception &_) {
res.body = "Bad operation/command/addr/numBytes";
res.status = 400;
return;
}
switch (operation) {
case 0: {
switch (command) {
case 1: {
// example of boost/asio method done via rest
sendTestIPCommand(0x02, 0x01);
}
}
}
case 1: {
switch (command) {
case 1: {
// other read commands
}
}
}
}
res.set_content(jsonRes.dump(), "application/json");
});
server_.listen("0.0.0.0", serverPort__);
std::cout << "Quitting REST server at port" << serverPort__ << std::endl;
return 0;
}
}
void startTestSocket() {
try {
testSocket = tcp::socket(io_context);
tcp::resolver signalLightResolver(io_context);
tcp::resolver::results_type signalLightEndpoints = signalLightResolver.resolve(tcp::resolver::query(tcp::v4(), testIP, testPort));
boost::asio::connect(testSocket, signalLightEndpoints);
}
catch (std::exception &e) {
std::cerr << "Socket Exception: " << e.what() << std::endl;
}
}
void sendTestIPCommand(uint8_t color, uint8_t mode) {
unsigned char c_pIdataW[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
c_pIdataW[0] = 'W';
c_pIdataW[color] = mode;
auto start_time = std::chrono::steady_clock::now();
try {
while (true) {
boost::asio::write(testSocket, boost::asio::buffer(c_pIdataW), boost_error);
auto now = std::chrono::steady_clock::now();
if (std::chrono::duration_cast<std::chrono::seconds>(now - start_time).count() > 3) {
std::cout << "socket start break" << std::endl;
break;
}
}
}
catch (std::exception &e) {
std::cerr << "Socket Exception: " << e.what() << std::endl;
}
if (!boost_error) {
std::cout << "starting signal light.." << std::endl;
}
else {
std::cerr << "a socket error has occurred" << std::endl;
}
}
int main() {
startTestSocket();
return RestServer::main();
}
If you have any off the dome information regarding as to why or possible solutions that would be much appreciated!
I have tried multi-threading: Created a unique thread for the rest server expecting that to allow REST to be non-blocking to the boost/asio functions. Created a unique thread for the io_context expecting that to allow the boost/asio funtions to be non-blocking to rest. I have tried mutex in both cases. I have tried the async write methods from boost/asio not really sure what I expected with this as it was a last ditch effort recommended by GPT-4o.
Upvotes: 1
Views: 49
Reputation: 393134
Your startTestSocket
never returns until the connect completes. If the testIP
/testPort
combination doesn't respond, this may take a long time.
You can see how it changes to print the Socket Exception
when I change the testIP
to 127.0.0.1 (which my computer knows doesn't respond, so it doesn't block indefinitely):
It seems unclear what startTestSocket
is actually supposed to do, so maybe you can do without it? If you need both to run at the same time, make them
I just realized you were using globals, and indeed it works fine if I use netcat
to listen on 20000 and display the results:
Upvotes: 0