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.
Grayson is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
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
- run on separate threads
- run asynchronously using the same io_context
UPDATE
I just realized you were using globals, and indeed it works fine if I use netcat
to listen on 20000 and display the results:
1