What Im trying to achieve is the following:
Run radio in the receive mode and check for incoming packages. Once a second Id like a timer interrupt to execute and switch the radio into transmit mode to send a package and then return back to the receive mode.
Right now I have the following code:
int main(int argc, char** argv)
{
boost::asio::io_service io;
boost::asio::deadline_timer t(io, boost::posix_time::seconds(1));
t.async_wait(boost::bind(sendSyncToDevices, boost::asio::placeholders::error, &t));
io.run();
}
void sendSyncToDevices(const boost::system::error_code& ,
boost::asio::deadline_timer* t)
{
DoSomething();
t->expires_at(t->expires_at() + boost::posix_time::seconds(1));
t->async_wait(boost::bind(sendSyncToDevices, boost::asio::placeholders::error, t));
}
however io.run() is a blocking call. Can somebody advice how I could add additional while(1) loop that would be checking for incoming messages on the radio?
Thanks!
You may get your answer in similar post
How do I perform a nonblocking read using asio?
They have discussed a way of having the read/write call non-blocking.
Alternatively,
Doing while(1) on the app code may not be the best way to wait for an event or I/O. You can also try polling style (more of a C -style programming) using select() on those file descriptors. Select() is inherently a poll() function which serves much better than having while(1) and reading the file again and again.
Hope this helps.
Related
I am writing a server that accepts data from a device and processes it. Everything works fine unless there is an interruption in the network (i.e., if I unplug the Ethernet cable, then reconnect it). I'm using read_until() because the protocol that the device uses terminates the packet with a specific sequence of bytes. When the data stream is interrupted, read_until() blocks, as expected. However when the stream starts up again, it remains blocked. If I look at the data stream with Wireshark, the device continues transmitting and each packet is being ACK'ed by the network stack. But if I look at bytes_readable it is always 0. How can I detect the interruption and how to re-establish a connection to the data stream? Below is a code snippet and thanks in advance for any help you can offer. [Go easy on me, this is my first Stack Overflow question....and yes I did try to search for an answer.]
using boost::asio::ip::tcp;
boost::asio::io_service IOservice;
tcp::acceptor acceptor(IOservice, tcp::endpoint(tcp::v4(), listenPort));
tcp::socket socket(IOservice);
acceptor.accept(socket);
for (;;)
{
len = boost::asio::read_until(socket, sbuf, end);
// Process sbuf
// etc.
}
Remember, the client initiates a connection, so the only thing you need to achieve is to re-create the socket and start accepting again. I will keep the format of your snippet but I hope your real code is properly encapsulated.
using SocketType = boost::asio::ip::tcp::socket;
std::unique_ptr<SocketType> CreateSocketAndAccept(
boost::asio::io_service& io_service,
boost::asio::ip::tcp::acceptor& acceptor) {
auto socket = std::make_unique<boost::asio::ip::tcp::socket>(io_service);
boost::system::error_code ec;
acceptor.accept(*socket.get(), ec);
if (ec) {
//TODO: Add handler.
}
return socket;
}
...
auto socket = CreateSocketAndAccept(IOservice, acceptor);
for (;;) {
boost::system::error_code ec;
auto len = boost::asio::read_until(*socket.get(), sbuf, end, ec);
if (ec) // you could be more picky here of course,
// e.g. check against connection_reset, connection_aborted
socket = CreateSocketAndAccept(IOservice, acceptor);
...
}
Footnote: Should go without saying, socket needs to stay in scope.
Edit: Based on the comments bellow.
The listening socket itself does not know whether a client is silent or whether it got cut off. All operations, especially synchronous, should impose a time limit on completion. Consider setting SO_RCVTIMEO or SO_KEEPALIVE (per socket, or system wide, for more info How to use SO_KEEPALIVE option properly to detect that the client at the other end is down?).
Another option is to go async and implement a full fledged "shared" socket server (BOOST example page is a great start).
Either way, you might run into data consistency issues and be forced to deal with it, e.g. when the client detects an interrupted connection, it would resend the data. (or something more complex using higher level protocols)
If you want to stay synchronous, the way I've seen things handled is to destroy the socket when you detect an interruption. The blocking call should throw an exception that you can catch and then start accepting connections again.
for (;;)
{
try {
len = boost::asio::read_until(socket, sbuf, end);
// Process sbuf
// etc.
}
catch (const boost::system::system_error& e) {
// clean up. Start accepting new connections.
}
}
As Tom mentions in his answer, there is no difference between inactivity and ungraceful disconnection so you need an external mechanism to detect this.
If you're expecting continuous data transfer, maybe a timeout per connection on the server side is enough. A simple ping could also work. After accepting a connection, ping your client every X seconds and declare the connection dead if he doesn't answer.
Consider this piece of code opening a socket using bost and sending some data:
boost::asio::io_service service;
tcp::resolver resolver(service);
tcp::resolver::query query(tcp::v4(), "localhost", 2000);
tcp::resolver::iterator iterator = resolver.resolve( query );
boost::asio::ip::tcp::socket socket(service);
socket.connect(*iterator);
boost::asio::write(socket, boost::asio::buffer(data, size));
If the receiver is coded in C++ with boost it can receive the data. However, we are trying to receive the data from Matlab, and this one does not receive the data:
t=tcpip('localhost', 2000, 'NetworkRole', 'server');
fopen(t);
// Now we launch the C++ code above from another process
// Now t.BytesAvailable remains 0
To have the data be received, we need to insert a sleep between socket.connect and boost::asio::write, then it works well (as this post mentioned it https://stackoverflow.com/a/20274486/3336423....).
I really hate having to do that (add sleep in my code). Is there any alternative? Is there a sort of is_ready attribute or something similar I could use to know when I can send the data and be sure the listener will get it?
Extra bonus question: Is it a kind of Matlab bug? As sleep is not needed when receiver is a C++/boost application....does only Matlab need this sleep to be operated in order to receive the first set of data transmitted?
I agree with the analysis on that linked answer, and it very much appears that Matlab is being buggy.
There's not going to be a solution to that unless you can fix the server (or if Matlab documents a protocol to reliable start the communications)
Note: you can use Asio to do asynchronous IO operations. It also comes with a deadline timer. So, if your gripe is that sleep blocks operations, that can be circumvented with slightly more complicated asynchronous calls.
What is the best way to handle an interrupt signal in infinite loop in server application?
I develop simple FTP Server where every client has its own thread. And now, what I want to do is to handle interrupt signal and then interrupt every thread in the vector of client threads.
In every thread I want to have an opportunity to send to client some response while sending/receiving some file and manually close socket. In main thread I want to only log that server was aborted and close server socket.
I had an idea to implement my server class FTPServer like singleton and in signal() call a function which calls method abort() of FTPServer instance. But I don't know what it will do when the main infinite loop is still running and accept() is still waiting for a new client ... And this pattern I can not use in client threads because there are not only one instance ..
My methods are:
void FTPServer::run() {
while ( 1 ){
int cliFd = TCPController::acceptClient ( m_serverFD );
serveNewClient(cliFd);
}
}
void FTPServer::serveNewClient( int clientFD ){
m_client_threads.push_back( thread(&FTPServer::clientThread, *this, clientFD) );
}
void FTPServer::clientThread( int clientFD ){
ClientThread client(clientFD);
client.run();
}
So I want to handle interrupt signal, break(?) the main infinite loop and call abort() method.
I was searching for something like this:
void FTPServer::run() {
try{
while ( 1 ){
int cliFd = TCPController::acceptClient ( m_serverFD );
serveNewClient(cliFd);
}
catch( RuntimeException & e ){
this.abort()
}
}
But I didn't find anything .. :(
In every client thread I have infinite loop too - it waits for client's commands ...
So, please, can you tell me, what is the best way to handle it?
Thank you !
You have multiple threads, each with its own loop. What you should do is make sure your thread loops frequently check to see if they should be aborted. So instead of blocking indefinitely on acceptClient, accept a connection with a timeout. When the timeout expires, or when a connection is accepted, check if the thread should quit.
Ive currently been messing around with boost trying it out. When i try to make a simple multi threaded echo server it exits when receiving with error code 3. I have looked over the documentation many times and still no luck. I know it is probably something very simple i'm overlooking. I have decent experience with winsock but i would like to learn the boost library.
here is the code thats failing i took out the
typedef boost::shared_ptr<tcp::socket> socket_ptr;
boost::asio::io_service io;
boost::array<char, 512> buf;
void startserver ( std::string host, std::string port )
{
tcp::acceptor a (io, tcp::endpoint(tcp::v4(), atoi(port.c_str())));
for(;;)
{
socket_ptr sock (new tcp::socket(io));
a.accept(*sock);
std::cout << sock->remote_endpoint() << std::endl;
boost::thread t (boost::bind(session, sock));
}
}
void session ( socket_ptr sock )
{
sock->send(boost::asio::buffer("welcome"),0,er);
size_t len;
for(;;)
{
len = sock->receive(boost::asio::buffer(buf));
sock->send(boost::asio::buffer(buf,len),0,er);
}
}
I can connect to it fine with netcat and it receives the welcome message but right when it goes to receive it crashs. Ive tried catching an error using boost::system::error_code on each one but nothing was returned
There are too many issues. Check asio documentation for correct examples. Some of issues:
Creating boost::thread object t and then immediately exit scope. This deattaches thread and it not controllable now; or, as mentioned Joachim Pileborg it can terminate (im not very familiar with boost::threads, so correct me if i wrong).
Right after this you starting new acceptor. You should hold only 1 acceptor per listening port.
No point to create thread for this at all, it is ASIO, use async ;)
receive does not wait data, it just fetch packet data ASIO already had (what is not true in this case)
Check examples at boost site, i think your case is blocking tcp echo server
It's most likely because the thread goes out of scope. From the manual page of the boost::thread destructor:
If the thread is joinable calls to std::terminate. Destroys *this.
This means that when the thread is started it might run for a little while before the thread in startserver gets control again and the thread object is destructed and your thread is terminated.
In our application we use Boost libraries (and ASIO for network communications).
Recently, we discovered that if we're sending our data from different threads via same socket, our client application is receiving garbaged data.
Small test to highlight the issue:
#include <stdio.h>
#include <boost/thread.hpp>
#include <boost/asio.hpp>
void send_routine(boost::shared_ptr<boost::asio::ip::tcp::socket> s, char c)
{
std::vector<char> data(15000, c);
data.push_back('\n');
for (int i=0; i<1000; i++)
boost::asio::write(*s, boost::asio::buffer(&data[0], data.size()));
}
int main()
{
using namespace boost::asio;
using namespace boost::asio::ip;
try {
io_service io_service;
io_service::work work(io_service);
const char* host = "localhost";
const char* service_name = "18000";
tcp::resolver resolver(io_service);
tcp::resolver::query query(tcp::v4(), host, service_name);
tcp::resolver::iterator iterator = resolver.resolve(query);
auto socket = boost::shared_ptr<tcp::socket>(new tcp::socket(io_service));
socket->connect(*iterator);
boost::thread t1(send_routine, socket, 'A');
boost::thread t2(send_routine, socket, 'B');
boost::thread t3(send_routine, socket, 'C');
t1.join();
t2.join();
t3.join();
}
catch (std::exception& e) {
printf("FAIL: %s\n", e.what());
}
return 0;
}
So, we create socket here, connect to localhost:18000 and start 3 threads which will write to the socket.
In different terminal window, I run nc -l -p 18000 | tee out.txt | sort | uniq | wc -l. I expect 3 as output, but it returns more then 100 "different strings" in the network stream (so, data is corrupted). But it works with small buffer sizes (if we'll change 15000 to 80, for example).
So, the question is: is it a correct behavior of ASIO library? And another: how to fix it? Should I use mutex inside my send_routine function (or there is another solution)?
write and async_write are not thread safe in the manner you are using them. The canonical way to approach this is to queue your messages, then write them out one at a time.
Yes there is another solution !
Strands: Use Threads Without Explicit Locking. Be care that strands only provides "atomic" access to socket for the "event handlers", of course you need to use asio "event handlers" which is not the case of your code. In other words you need to use boost::asio::async_write instead of boost::asio::write.
Well according to the documentation tcp::socket is not thread safe when shared between multiple threads.
So you either do a synchronisation like you suggested with boost::mutex or you use async write. The io_service the work for you.
You might have two problems, the threading issue could be solve for example by having one thread dedicated to writing and a queue where all threads post there response. You can also change your design to an asynchronous one and use the write_some() function and let the threading be done by the io_service::run(), which can be run by more than one thread.
Second, you might have a protocol problem, if a client expects the answers to it's questions in the same order.
hth
Torsten