read_some() works but very slow, read() doesn't - c++

Below code definitely works, but not fast as I expect.
I expect my program to read data at very good pace. There is another commercial app which connects to same server & retrives data at amazing speed. Server side is not a problem.
class A
{
//...
boost::asio::ip::tcp::socket* myPort;
}
void A::OpenPort()
{
if(myPort)
{
if(myPort->is_open())
{
return;
}
}
// make the connection
Connect();
if(! myPort->is_open())
{
return;
}
// set the protocol
static string init("INIT\r\n");
myPort->write_some(boost::asio::buffer(init.c_str(), init.length()));
}
void A::Read()
{
static string prev_msg = "";
try
{
OpenPort();
while(true)
{
boost::system::error_code error;
boost::asio::streambuf streamBuf;
boost::asio::streambuf::mutable_buffers_type mutableBuffer = streamBuf.prepare(614400);
size_t bytes_transferred = myPort->read_some(boost::asio::buffer(mutableBuffer), error);
if (error)
{
if (error != boost::asio::error::eof)
{
throw boost::system::system_error(error); // Some other error.
}
}
// add to any previous message we might not have processed
streamBuf.commit(bytes_transferred);
istreambuf_iterator<char> sbit(&streamBuf);
istreambuf_iterator<char> end;
string s(sbit, end);
prev_msg.append(s);
string delimiter1 = ",\r\n";
size_t pos1 = 0;
string response;
while ((pos1 = prev_msg.find(delimiter1)) != std::string::npos)
{
response = prev_msg.substr(0, pos1);
//SOME PROCESSING ON THE RESPONSE RECEIVED
}
}
}
catch (boost::system::system_error const& ex)
{
cout<<ex.what();
}
}
obviously, the problem is read_some(), program doesn't read complete data in one read operation, sometimes it receives 614000 bytes, sometimes very less.
I don't want to enforce any limit on size of the buffer, whatever server sends, program should read all that data in one go.
Hence, I decided to use just read(). But, now program is stuck at read(); read() call doesn't return.
boost::asio::streambuf streamBuf;
size_t bytes_transferred = read(*myPort, streamBuf, error);
if (error)
{
if (error != boost::asio::error::eof)
{
throw boost::system::system_error(error); // Some other error.
}
}
I must process the data received before requesting next data, Hence I can't use async_read().

Do not allocate a new buffer on each loop, do that only one time outside the loop.
while(true)
{
boost::system::error_code error;
boost::asio::streambuf streamBuf;
boost::asio::streambuf::mutable_buffers_type mutableBuffer = streamBuf.prepare(614400);
size_t bytes_transferred = myPort->read_some(boost::asio::buffer(mutableBuffer), error);
...
to be replaced by
boost::system::error_code error;
boost::asio::streambuf streamBuf;
boost::asio::streambuf::mutable_buffers_type mutableBuffer = streamBuf.prepare(614400);
while(true)
{
size_t bytes_transferred = myPort->read_some(boost::asio::buffer(mutableBuffer), error);
...

A few things:
With TCP you can never be sure that you will receive everything in one go.
Because you are reading up to a delimiter, read_until() is probably what you're after.
Make sure you're opening the socket with O_NDELAY, otherwise you will add 200ms to your write. Do this by calling myPort->set_option(tcp::no_delay(true)) where appropriate in your code.
The sleep is not a good idea. Design your code so that it isn't necessary.
Your code seems to go into an endless loop if the socket is closed.
You call write_some() without checking the return value. You should probably call write() to ensure all your data is written.
If you have many threads you will probably get an improvement from redesigning your code to be async.

Related

Is there any way to know the number of bytes transfered in an async_read function if the read handler won't get invoked?

I have coded the following DoRead function which reads data from the opened serial port, and it works as expected except one thing:
When the timeout elapses before the read completes, then no read handler will be invoked and I can not get the number of bytes read at this point.
Here is my code:
std::size_t wxSerialPort::DoRead(std::string& str, const int timeout)
{
m_bytes_transferred_read = 0;
boost::asio::async_read(m_serialPort, boost::asio::buffer(str),
std::bind(&wxSerialPort::AsyncReadHandler, this,
std::placeholders::_1, std::placeholders::_2));
m_io_context.restart();
if (timeout == wxTIMEOUT_INFINITE)
{
m_io_context.run_until(std::chrono::steady_clock::time_point::max());
}
else
{
m_io_context.run_for(std::chrono::milliseconds(timeout));
}
return m_bytes_transferred_read; // At this point I always get 0 bytes read.
}
void wxSerialPort::AsyncReadHandler(const boost::system::error_code& error, std::size_t bytes_transferred)
{
m_bytes_transferred_read = bytes_transferred;
}
Keep in mind that any variable preceded with m_ is a member variable.
But if I give a small buffer for example to the function, then the read handler will be invoked before the timeout, and I get the actual number of bytes read.
Thank you in advance.
It sounds like you need to call async_read_some instead of async_read.
The async_read function ensures that the requested amount of data is read before the asynchronous operation completes, i.e. it needs enough data to fill the buffer before it calls the read handler.
The basic_serial_port::async_read_some method calls the read handler whenever data has been received, regardless of whether the buffer is full or not.
So simply replace the call to async_read with:
m_serialPort.async_read_some(boost::asio::buffer(str),
std::bind(&wxSerialPort::AsyncReadHandler, this,
std::placeholders::_1, std::placeholders::_2));
it turns out that, boost-asio -by design-, won't call any IO handler for any of the io_context::run_for, io_context::run_one_for, io_context::run_until and io_context::run_one_until functions when the timeout elapses.
And the solution for this problem, would be to provide our own wait handler and cancel (basic_serial_port::cancel) all asynchronous operations associated with the serial port in that wait handler, that in turn will trigger our read handler with a boost::asio::error::operation_aborted error code.
And the resulting code will be as follows:
std::size_t wxSerialPort::DoRead(std::string& str, const int timeout)
{
m_bytes_transferred_read = 0;
if (timeout == wxTIMEOUT_INFINITE)
{
m_timer.expires_at(std::chrono::steady_clock::time_point::max());
}
else
{
m_timer.expires_from_now(std::chrono::milliseconds(timeout));
}
m_timer.async_wait(std::bind(&wxSerialPort::AsyncWaitHandler, this,
std::placeholders::_1));
boost::asio::async_read(m_serialPort, boost::asio::buffer(str),
std::bind(&wxSerialPort::AsyncReadHandler, this,
std::placeholders::_1, std::placeholders::_2));
m_io_context.restart();
m_io_context.run();
return m_bytes_transferred_read;
}
void wxSerialPort::AsyncReadHandler(const boost::system::error_code& error, std::size_t bytes_transferred)
{
if (error != boost::asio::error::operation_aborted)
{
m_timer.cancel();
}
m_bytes_transferred_read = bytes_transferred;
}
void wxSerialPort::AsyncWaitHandler(const boost::system::error_code& error)
{
if (error != boost::asio::error::operation_aborted)
{
m_serialPort.cancel();
}
}
Thank you.

Sockets - keeping a socket open after data transfer

I have written simple server/client programs, in which the client sends some hardcoded data in small chunks to the server program, which is waiting for the data so that it can print it to the terminal. In the client, I'm calling send() in a loop while there is more data to send, and on the server, I'm doing the same with read(), that is, while the number of bytes returned is > 0, I continue to read.
This example works perfectly if I specifically call close() on the client's socket after I've finished sending, but if I don't, the server won't actually exit the read() loop until I close the client and break the connection. On the server side, I'm using:
while((bytesRead = read(socket, buffer, BUFFER_SIZE)) > 0)
Shouldn't bytesRead be 0 when all the data has been received? And if so, why will it not exit this loop until I close the socket? In my final application, it will be beneficial to keep the socket open between requests, but all of the sample code and information I can find calls close() immediately after sending data, which is not what I want.
What am I missing?
When the other end of the socket is connected to some other network system halfway around the world, the only way that the receiving socket knows "when all the data has been received" is precisely when the other side of the socket is closed. That's what tells the other side of the socket that "all the data has been received".
All that a socket knows about is that it's connected to some other socket endpoint. That's it. End of story. The socket has no special knowledge of the inner workings of the program that has the other side of the socket connection. Nor should it know. That happens to be the responsibility of the program that has the socket open, and not the socket itself.
If your program, on the receiving side, has knowledge -- by the virtue of knowing what data it is expected to receive -- that it has now received everything that it needs to receive, then it can close its end of the socket, and move on to the next task at hand.
You will have to incorporate in your program's logic, a way to determine, in some form or fashion, that all the data has been transmitted. The exact nature of that is going to be up to you to define. Perhaps, before sending all the data on the socket, your sending program will send in advance, on the same socket, the number of bytes that will be in the data to follow. Then, your receiving program reads the number of bytes first, followed by the data itself, and then knows that it has received everything, and can move on.
That's one simplistic approach. The exact details is up to you. Alternatively, you can also implement a timeout: set a timer and if any data is not received in some prescribed period of time, assume that there is no more.
You can set a flag on the recv call to prevent blocking.
One way to detect this easily is to wrap the recv call:
enum class read_result
{
// note: numerically in increasing order of severity
ok,
would_block,
end_of_file,
error,
};
template<std::size_t BufferLength>
read_result read(int socket_fd, char (&buffer)[BufferLength], int& bytes_read)
{
auto result = recv(socket_fd, buffer, BufferLength, MSG_DONTWAIT);
if (result > 0)
{
return read_result::ok;
}
else if (result == 0)
{
return read_result::end_of_file;
}
else {
auto err = errno;
if (err == EAGAIN or err == EWOULDBLOCK) {
return read_result::would_block;
}
else {
return read_result ::error;
}
}
}
One use case might be:
#include <unistd.h>
#include <sys/socket.h>
#include <cstdlib>
#include <cerrno>
#include <iostream>
enum class read_result
{
// note: numerically in increasing order of severity
ok,
would_block,
end_of_file,
error,
};
template<std::size_t BufferLength>
read_result read(int socket_fd, char (&buffer)[BufferLength], int& bytes_read)
{
auto result = recv(socket_fd, buffer, BufferLength, MSG_DONTWAIT);
if (result > 0)
{
return read_result::ok;
}
else if (result == 0)
{
return read_result::end_of_file;
}
else {
auto err = errno;
if (err == EAGAIN or err == EWOULDBLOCK) {
return read_result::would_block;
}
else {
return read_result ::error;
}
}
}
struct keep_reading
{
keep_reading& operator=(read_result result)
{
result_ = result;
}
const operator bool() const {
return result_ < read_result::end_of_file;
}
auto get_result() const -> read_result { return result_; }
private:
read_result result_ = read_result::ok;
};
int main()
{
int socket; // = open my socket and wait for it to be connected etc
char buffer [1024];
int bytes_read = 0;
keep_reading should_keep_reading;
while(keep_reading = read(socket, buffer, bytes_read))
{
if (should_keep_reading.get_result() != read_result::would_block) {
// read things here
}
else {
// idle processing here
}
}
std::cout << "reason for stopping: " << should_keep_reading.get_result() << std::endl;
}

Reading a Serial Port Asynchronously Boost

I am trying to read several bytes asynchronously from a serial port. Currently the code I am using (source is How do I perform a nonblocking read using asio?), only allows me to read 1 byte and then it exits. How do I get it so that the code keeps reading until there is no more incoming data in the serial port? Thanks :)
void foo()
{
boost::asio::io_service io_svc;
boost::asio::serial_port ser_port(io_svc, "/dev/ttyS0");
boost::asio::deadline_timer timeout(io_svc);
unsigned char my_buffer[2];
bool data_available = false;
ser_port.async_read_some(boost::asio::buffer(my_buffer),
boost::bind(&read_callback, boost::ref(data_available),
boost::ref(timeout),boost::asio::placeholders::error(),
boost::asio::placeholders::bytes_transferred()));
timeout.expires_from_now(boost::posix_time::seconds(30));
timeout.async_wait(boost::bind(&wait_callback, boost::ref(ser_port),boost::asio::placeholders::error()));
io_svc.run();
io_svc.
if(!data_available)
{
ser_port.close();
cout << "ser_port was closed";
}
}
void read_callback(bool& data_available, boost::asio::deadline_timer& timeout, const boost::system::error_code& error, std::size_t bytes_transferred)
{
if (error || !bytes_transferred)
{
// No data was read!
data_available = false;
return;
}
timeout.cancel();
data_available = true;
}
void wait_callback(boost::asio::serial_port& ser_port, const boost::system::error_code& error)
{
if (error)
{
// Data was read and this timeout was cancelled
return;
}
ser_port.cancel();
}
In your read_callback you cancel your timer and do not start a new read.
So what do you expect ASIO to do. You just canceled all handlers and like the documentation states, the run method will return when no handlers are left.
So if you want to have more data than just the one byte you receive you can do two things:
First just issue another call to asio to read more from the port.
void read_callback(bool& data_available, boost::asio::deadline_timer& timeout, const boost::system::error_code& error, std::size_t bytes_transferred)
{
if (error || !bytes_transferred)
{
// No data was read!
data_available = false;
return;
}
// do something with the data you just read
ser_port.async_read_some(boost::asio::buffer(my_buffer),
boost::bind(&read_callback, boost::ref(data_available),
boost::ref(timeout),boost::asio::placeholders::error(),
boost::asio::placeholders::bytes_transferred()));
//restart your timer
data_available = true;
}
or you can add use transfer_at_least to get at least the amount of data you need.

How to read to asio buffer say `1` byte from one socket and than `read_some` more from another?

So I am trying to implement timed http connection Keep-Alive. And I need to be capable of killing it on some time-out. So currently I have (or at least I would like to have):
void http_request::timed_receive_base(boost::asio::ip::tcp::socket& socket, int buffer_size, int seconds_to_wait, int seconds_to_parse)
{
this->clear();
http_request_parser_state parser_state = METHOD;
char* buffer = new char[buffer_size];
std::string key = "";
std::string value = "";
boost::asio::ip::tcp::iostream stream;
stream.rdbuf()->assign( boost::asio::ip::tcp::v4(), socket.native() );
try
{
do
{
stream.expires_from_now(boost::posix_time::seconds(seconds_to_wait));
int bytes_read = stream.read_some(boost::asio::buffer(buffer, buffer_size));
stream.expires_from_now(boost::posix_time::seconds(seconds_to_parse));
if (stream) // false if read timed out or other error
{
parse_buffer(buffer, parser_state, key, value, bytes_read);
}
else
{
throw std::runtime_error("Waiting for 2 long...");
}
} while (parser_state != OK);
}
catch (...)
{
delete buffer;
throw;
}
delete buffer;
}
But there is no read_some in tcp::iostream, so compiler gives me an error:
Error 1 error C2039: 'read_some' : is not a member of 'boost::asio::basic_socket_iostream<Protocol>'
That is why I wonder - how to read 1 byte via stream.read (like stream.read(buffer, 1);) and than read_some to that very buffer via socket API ( it would look like int bytes_read = socket.read_some(boost::asio::buffer(buffer, buffer_size)); and than call my parse_buffer function with real bytes_read value)
BTW it seems like there will be a really sad problem of 1 last byte..(
Sorry to be a bit rough, but did you read the documentation? The socket iostream is supposed to work like the normal iostream, like cin and cout. Just do stream >> var. Maybe you want basic_stream_socket::read_some instead?

boost::asio::streambuf asserts "iterator out of bounds"

Client sends to server near about 165kB of data. At first all is fine.
But when client send the same data once again(165kB), I receive an assert on server side.
Assert contains information about "iterator out of bounds"
On the call stack, there is some information about read_until method.
So I think that I made a mistake.
TCP Asynchronous Server code is below:
Code for handle_read:
void Session::handle_read(const boost::system::error_code& a_error,
size_t a_nbytestransferred)
{
if (!a_error)
{
std::ostringstream dataToRetrive;
dataToRetrive << &m_bufferRead;
boost::thread threads(boost::bind(retriveMessageFromClient,
shared_from_this(), dataToRetrive.str()));
boost::asio::async_write(m_socket, m_bufferWrite,
boost::bind(&Session::handle_write,
shared_from_this(), boost::asio::placeholders::error));
}
else
disconnect();
}
Code for handle_write:
void Session::handle_write(const boost::system::error_code& a_error)
{
if (!a_error)
{
boost::asio::async_read_until(m_socket,
m_bufferRead, boost::regex(G_strREQUESTEND),
boost::bind(&Session::handle_read, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
disconnect();
}
Both m_bufferRead, m_bufferWrite are members of class Session.
class Session...
boost::asio::streambuf m_bufferRead;
boost::asio::streambuf m_bufferWrite;
Update
I detected that problem is layed in other place of my code.
After than thread finishs tasks, metdhod
do_writeMessage() is called.
Thread function
void retriveMessageFromClient(boost::shared_ptr<Session>& A_spSesion, std::string A_strDataToRetrive)
{
try
{
std::string strAnswer;
bool bFind = (A_strDataToRetrive.find(G_REGEX_BIG_FILE_BEGIN) != std::string::npos);
if(bFind) // Write large data to osFile
{
A_strDataToRetrive = boost::regex_replace(A_strDataToRetrive, boost::regex(G_REGEX_BIG_FILE_BEGIN), std::string(""));
std::string strClientFolder = str(boost::format("%1%%2%") % CLIENT_PRE_FOLDER_FILE % A_spSesion->getIdentifier());
std::string strClientFile = str(boost::format("%1%\\%2%%3%") % strClientFolder % strClientFolder % CLIENT_EXTENSION);
if ( boost::filesystem::exists(strClientFolder) )
boost::filesystem::remove_all(strClientFolder);
else
boost::filesystem::create_directory( strClientFolder );
std::ofstream osFile(strClientFile.c_str());
osFile << A_strDataToRetrive;
osFile.close();
strAnswer = str(boost::format(G_FILE_WAS_WRITE) % strClientFile);
}
else
{
double dResult = sin (30.0 * 3.14/180);
strAnswer = str(boost::format(G_OPERATION_RESULT) % dResult);
}
// Sleep thread
boost::xtime timeToSleep;
boost::xtime_get(&timeToSleep, boost::TIME_UTC);
timeToSleep.sec += 2;
boost::this_thread::sleep(timeToSleep);
A_spSesion->do_writeMessage(strAnswer);
}
catch (std::exception& e)
{
std::cerr << THREAD_PROBLEM << e.what() << "\n";
}
}
Session do_writeMessage
void Session::do_writeMessage(const std::string& A_strMessage)
{
m_strMessage = A_strMessage;
m_strMessage += G_strRESPONSEEND;
// m_socket.send(boost::asio::buffer(m_strMessage)); It works correctly
m_socket.async_send(boost::asio::buffer(m_strMessage),
boost::bind(&Session::handle_write, shared_from_this(),
boost::asio::placeholders::error)); -- after that assert
}
So finnally I have a problem with asynch_send...
UPDATED
**TCPAsyncServer**::TCPAsyncServer(boost::asio::io_service& A_ioService, short port,
: m_ioService(A_ioService), m_lIDGenerator(0),
m_clientSocket(m_ioService, tcp::endpoint(tcp::v4(),
port)),
{
SessionPtr newSession(new Session(m_ioService, m_mapSessions, ++m_lIDGenerator));
m_clientSocket.async_accept(newSession->getSocket(),
boost::bind(&TCPAsyncServer::handle_ClientAccept, this,
newSession, boost::asio::placeholders::error));
Session contructor
Session::Session(boost::asio::io_service& A_ioService, std::map<long, boost::shared_ptr<Session> >& A_mapSessions, long A_lId)
: m_socket(A_ioService), m_mapSessions(A_mapSessions), m_lIdentifier(A_lId), m_ioService(A_ioService)
{}
Session members
std::map<long, boost::shared_ptr<Session> >& m_mapSessions;
long m_lIdentifier;
boost::asio::ip::tcp::socket m_socket;
boost::asio::io_service& m_ioService;
You need to use prepare, consume, and commit when using asio::streambuf to read and write from a socket. The documentation describes this with an example. It's not obvious to me based on your sample code if you are doing that.
writing
boost::asio::streambuf b;
std::ostream os(&b);
os << "Hello, World!\n";
// try sending some data in input sequence
size_t n = sock.send(b.data());
b.consume(n); // sent data is removed from input sequence
reading
boost::asio::streambuf b;
// reserve 512 bytes in output sequence
boost::asio::streambuf::mutable_buffers_type bufs = b.prepare(512);
size_t n = sock.receive(bufs);
// received data is "committed" from output sequence to input sequence
b.commit(n);
std::istream is(&b);
std::string s;
is >> s;
If you are using async_read / async_read_until you don't need to specify a size for streambuf but do need to ensure the data you read into it is not greater than it maximum allowed size. In relation to the “iterator out of bounds” issue; I have found that telling asio to read when it's already reading causes a race condition for the streambuf to which asio reads which results in the assertion error:
Assert “iterator out of bounds”
You can use something like:
strand_.wrap(boost::bind(&your_class::handle_read, this,
asio::placeholders::error, asio::placeholders::bytes_transferred)));
to help synchronize your threads but you must be careful not to 'wrap' something that is already running with access to shared data.
HTH