Boost::Asio : Problem with async_read - c++

I'm writing a simple ProxyServer that analyzes packages and sends them to another server instance, e.g. something like this:
client -> MyProxy -> SQLServer ->
client <- MyProxy <- SQLServer <-
It should run in an infinite loop.
My problem now is that the proxy seems to loose packages, sometimes it even hangs.
When I add a lot of debug information (which is written to the console), the ProxyServer is
much more stable. It seems like the ProxyServer is too fast.. :-)
I'm pretty sure I'm doing something wrong, here is code of my session class (the code is derived from the Boost::Asio examples).
#include "session.h"
#include <iostream>
using namespace std;
session::session(boost::asio::io_service& io_service)
: socket_(io_service)
, sqlsocket_(io_service)
, io_service_(io_service)
, resolver(io_service)
{
cout << "session::session()" << endl;
}
session::~session()
{
cout << "session::~session()" << endl;
cout << "closing session ..." << endl;
}
tcp::socket& session::socket()
{
return socket_;
}
void session::start()
{
cout << "session::start()" << endl;
cout << "starting session ..." << endl;
// connect to the sqlserver database
tcp::resolver::query query("192.168.1.50", "1317");
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::endpoint endpoint = *endpoint_iterator;
sqlsocket_.async_connect(endpoint,
boost::bind(&session::handle_sqlserver_connect, this,
boost::asio::placeholders::error, ++endpoint_iterator));
// TODO: connect to the connector
}
void session::handle_read(const boost::system::error_code& error,
size_t bytes_transferred)
{
cout << "session::handle_read()" << endl;
if (!error)
{
cout << "session::handle_read() (read: "
<< bytes_transferred << ")"
<< endl;
boost::asio::async_write(sqlsocket_,
boost::asio::buffer(data_, bytes_transferred),
boost::bind(&session::handle_sqlserver_write, this,
boost::asio::placeholders::error, bytes_transferred));
}
else
{
delete this;
}
}
void session::handle_sqlserver_read(const boost::system::error_code& error,
size_t bytes_transferred)
{
cout << "session::handle_sqlserver_read()" << endl;
if (!error)
{
cout << "session::handle_sqlserver_read() (read: "
<< bytes_transferred << ")"
<< endl;
boost::asio::async_write(socket_,
boost::asio::buffer(data_, bytes_transferred),
boost::bind(&session::handle_write, this,
boost::asio::placeholders::error, bytes_transferred));
}
else
{
delete this;
}
}
void session::handle_write(const boost::system::error_code& error,
size_t bytes_transferred)
{
static int count = 0;
cout << ++count << ". session::handle_write()" << endl;
if (!error)
{
cout << "session::handle_write() (read: "
<< bytes_transferred << ")"
<< endl;
socket_.async_read_some(boost::asio::buffer(data_, max_length),
boost::bind(&session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
{
delete this;
}
}
void session::handle_sqlserver_write(const boost::system::error_code& error,
size_t bytes_transferred)
{
cout << "session::handle_sqlserver_write()" << endl;
if (!error)
{
cout << "session::handle_sqlserver_write() (read: "
<< bytes_transferred << ")"
<< endl;
sqlsocket_.async_read_some(boost::asio::buffer(data_, max_length),
boost::bind(&session::handle_sqlserver_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
{
delete this;
}
}
void session::handle_sqlserver_connect(const boost::system::error_code& error,
tcp::resolver::iterator endpoint_iterator)
{
cout << "session::handle_sqlserver_connect()" << endl;
if (!error)
{
socket_.async_read_some(boost::asio::buffer(data_, max_length),
boost::bind(&session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else if (endpoint_iterator != tcp::resolver::iterator())
{
sqlsocket_.close();
tcp::endpoint endpoint = *endpoint_iterator;
sqlsocket_.async_connect(endpoint,
boost::bind(&session::handle_sqlserver_connect, this,
boost::asio::placeholders::error, ++endpoint_iterator));
}
}
Do I need to use other methods instead of async_* for my type of proxy?
I'm porting the code from some old project that my company wants to restart again, but with boost instead of the Winsock stuff that was used before.
Any idea what could be the problem?
The old code did something like this:
The main method with the accept method call created two threads
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)listenatclient, (LPVOID)cs, 0, 0);
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)listenatserver, (LPVOID)cs, 0, 0);
and the threads called the following functions:
void listenatclient(LPVOID connection)
{
connection_s* cs = (connection_s*)connection;
char inMessagecli[MSG_SIZE];
int rcount = 0;
...
do
{
memset(inMessagecli, 0, MSG_SIZE);
rcount = recv((SOCKET)cs->client, inMessagecli, MSG_SIZE, 0);
if (rcount != SOCKET_ERROR)
{
// analyze package
...
send((SOCKET)cs->server, inMessagecli, rcount, 0);
}
} while (rcount > 0);
}
void listenatserver(LPVOID connection)
{
connection_s* cs = (connection_s*)connection;
char inMessageserv[MSG_SIZE];
int rcount = 0;
do
{
memset(inMessageserv, 0, MSG_SIZE);
rcount = recv((SOCKET)cs->server, inMessageserv, MSG_SIZE, 0);
if (rcount != SOCKET_ERROR)
{
send((SOCKET)cs->client, inMessageserv, rcount, 0);
}
} while (rcount > 0);
}
[EDIT]:
I tried to run the async_read commands for the client and the sqlserver simultaneously, but now I get crashes all the time, sometimes in boost::bind, sometimes in other parts of the boost library.
What seems to happen is that 2 or three conections are created ( 3 sessions). While closing the first session, the crash seems to happen in the second session.
Is boost asio not treadsafe or am I doing something terribly wrong here :-) ?
I posted the code for the little ProxyServer here:
session.h : link
session.cpp : link
server.h: link
server.cpp: link
ProxyServer.cpp: link

I suspect what is happening is that one of your async_read_some calls is returning "some" data, but not enough for the SQL server to be satisfied that it has received a complete request. You code always follows the path of read_from_client -> send_to_server -> read_from_server -> send_to_client. It does not handle the case where you need read_from_client -> send_to_server - >read_from_client -> send_to_server -> read_from_server -> send_to_client.
The code you have written so far does not perform the same operations as the original. Specifically, the old code was simultaneously listening for reads on both sockets. Fortunately, since you're using ASIO, you don't need to mess with threads. Just issue issue simultaneous async_read_some requests on both sockets and deal with them asynchronously.

Related

How to prevent port changes when connection fails

I have some client application written using boost::asio.
I am trying to implement automatic reconnect when connection is lost. When connection can`t establishment
(for example, when the server is unavailable) or connection issues I trying establish connection again. However, on each attempt,
the application opens a new client port. This leads to a gradual usage of the all dynamic ports of the system.
class client
{
public:
client()
: socket_(nullptr)
, service_()
{
tcp::resolver r(service_);
auto iterator = r.resolve(tcp::resolver::query("127.0.0.1", "4572"));
if (iterator == tcp::resolver::iterator())
throw std::runtime_error("resolve");
endpoint_ = iterator->endpoint();
}
void start()
{
start_connect();
service_.run();
}
private:
void start_connect()
{
std::cout << "Trying " << endpoint_ << "...\n";
socket_ = std::make_shared<tcp::socket>(service_);
socket_->async_connect(endpoint_,
boost::bind(&client::handle_connect, this, _1));
}
void handle_connect(const boost::system::error_code& ec)
{
if (ec)
{
std::cout << "Connect error: " << ec.message() << "\n";
start_connect();
}
else
{
std::cout << "Connected to " << endpoint_ << "\n";
start_read();
}
}
void start_read()
{
boost::asio::async_read_until(*socket_, input_buffer_, '\n',
boost::bind(&client::handle_read, this, _1));
}
void handle_read(const boost::system::error_code& ec)
{
if (!ec)
{
std::string line;
std::istream is(&input_buffer_);
std::getline(is, line);
if (!line.empty())
std::cout << "Received: " << line << "\n";
start_read();
}
else
{
std::cout << "Error on receive: " << ec.message() << "\n";
start_connect();
}
}
private:
std::shared_ptr<tcp::socket> socket_;
boost::asio::io_service service_;
boost::asio::ip::tcp::endpoint endpoint_;
boost::asio::streambuf input_buffer_;
};
Is it possible to occupy only one client port for the entire execution time of the program? Or at least do not change the port for each attempt to connect to an unavailable server.
I think explicit setting the client port is not good way. Tried not to re-create the socket every time but in this case it is impossible to re-establish the connection.

Boost asio http deadline error?

The code bellow is mainly the HTTP client example with very few changes to support a download deadline.
It works as expected, but in rare cases e.g. if the internet is unstable, it doesn't work and the deadline can be more than what I set (20 or more seconds when I set 10). This happens very rarely and I am unable to reproduce this, it happens when I don't expect it.
To avoid posting a ton of lines (because few will read them) here is the place where I believe the error lies:
deadline_.expires_from_now(boost::posix_time::milliseconds(deadline));
tcp::resolver::query query(server, "http");
resolver_.async_resolve(query,
boost::bind(&client::handle_resolve, this,
boost::asio::placeholders::error,
boost::asio::placeholders::iterator));
deadline_.async_wait(boost::bind(&client::check_deadline, this));
Is the order of those lines correct?
And here is the check deadline function:
void check_deadline()
{
if(deadline_cancelled)
return;
else if (deadline_.expires_at() <= deadline_timer::traits_type::now())
socket_.close();
else
deadline_.async_wait(boost::bind(&client::check_deadline, this));
}
You should async_wait() on the deadline timer too. If you don't, you won't get notified, you just check (after the fact) whether the time had expired.
Then if it completes (with an ec other than operation_aborted) then you should
cancel() the async operations on the socket
optionally close the socket
PS. Mmm. It /seems/ that you are doing something similar, although it's unclear where
deadline_cancelled comes from
why you don't accept the error_code in the completion handler for deadline_.async_await and
why you are juggling with time comparisons manually, instead of trusting that the completion handler means what it says
Update Here's a full example doing a HTTP request. In fact, it downloads a million digits of PI from http://www.angio.net/pi/digits.html. This takes a while.
At the start of receiving the response I set a deadline timer for 800ms (and so the transfer should be - correctly - aborted).
This works as advertised. Pay special attention to the canceling of the socket and timer. Note that you could call expires_from_now() again after receiving each chunk of data. This is likely what you want. It will implicitly cancel() the timer each time it hadn't yet expired, so be prepared to handle the operatorion_aborted messages.
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/asio/deadline_timer.hpp>
class client
{
public:
client(boost::asio::io_service& io_service,
boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
: deadline_(io_service),
socket_(io_service)
{
boost::asio::async_connect(socket_.lowest_layer(), endpoint_iterator,
boost::bind(&client::handle_connect, this,
boost::asio::placeholders::error));
}
void handle_connect(const boost::system::error_code& error)
{
if (!error)
{
std::cout << "Enter message: ";
static char const raw[] = "GET /pi/digits/pi1000000.txt HTTP/1.1\r\nHost: www.angio.net\r\nConnection: close\r\n\r\n";
static_assert(sizeof(raw)<=sizeof(request_), "too large");
size_t request_length = strlen(raw);
std::copy(raw, raw+request_length, request_);
boost::asio::async_write(socket_,
boost::asio::buffer(request_, request_length),
boost::bind(&client::handle_write, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
{
std::cout << "Handshake failed: " << error.message() << "\n";
}
}
void deadline_expiration(const boost::system::error_code& error)
{
if (error == boost::asio::error::operation_aborted)
return;
std::cout << "\nDEADLINE REACHED\n";
socket_.cancel();
}
void handle_write(const boost::system::error_code& error,
size_t /*bytes_transferred*/)
{
if (!error)
{
std::cout << "starting read loop\n";
deadline_.expires_from_now(boost::posix_time::millisec(800));
//deadline_.expires_from_now(boost::posix_time::seconds(800));
deadline_.async_wait(boost::bind(&client::deadline_expiration, this, boost::asio::placeholders::error));
boost::asio::async_read_until(socket_,
//boost::asio::buffer(reply_, sizeof(reply_)),
reply_, '\n',
boost::bind(&client::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
{
std::cout << "Write failed: " << error.message() << "\n";
}
}
void handle_read(const boost::system::error_code& error, size_t /*bytes_transferred*/)
{
if (!error)
{
std::cout << "Reply: " << &reply_ << "\n";
boost::asio::async_read_until(socket_,
//boost::asio::buffer(reply_, sizeof(reply_)),
reply_, '\n',
boost::bind(&client::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
{
std::cout << "Read failed: " << error.message() << "\n";
deadline_.cancel(); // no need for after transfer completed
}
}
private:
boost::asio::deadline_timer deadline_;
boost::asio::ip::tcp::socket socket_;
char request_[1024];
boost::asio::streambuf reply_;
};
int main()
{
try
{
boost::asio::io_service io_service;
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::resolver::query query("www.angio.net", "80");
boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
client c(io_service, iterator);
io_service.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
}
Coliru link (Coliru doesn't support internet connectivity)

boost async ioservice, slow execution of a thread on linux

I am using boost asio in my application. I have created two ioservices. one for handling UDP sockets async operations, other to handle tcp async operations. on receiving data over udp socket, based on the data i will open a tcp connection to some other server. I am using async function calls in all places.
below is high level pseudo code.
int g_requestID ;
// boost async_recv handler
class UDPSocket
{
UDPSocket::OnDataReceived(const boost::system::error_code& error, std::size_t bytes_transferred
{
TCPSession *pSesson = new TCPSession() ;
g_requestID = pSesson->sendDataOverTCP(data);
}
}
// TCP Response Callback
void tcpResponse(int reqId)
{
if (g_requestID == reqId)
{
// received response for the request
}
}
class TCPSession
{
boost::asio::streambuf request_;
static int requestId ;
boost::tcp::socket m_socket ;
int currentsessionId = requestId ++ ;
int sendDataOverTCP(char* address, char* data)
{
m_socket.async_resolve()
return currentsessionId++ ;
}
void handle_resolve(const boost::system::error_code& err, tcp::resolver::iterator endpoint_iterator)
{
if (!err)
{
boost::asio::async_connect(m_socket, endpoint_iterator, boost::bind(&client::handle_connect, this, boost::asio::placeholders::error));
}
else
{
std::cout << "Error: " << err.message() << "\n";
}
}
void handle_connect(const boost::system::error_code& err)
{
if (!err)
{
// The connection was successful. Send the request.
boost::asio::async_write(m_socket, request_, boost::bind(&client::handle_write, this, boost::asio::placeholders::error));
}
else
{
std::cout << "Error: " << err.message() << "\n";
}
}
void handle_write(const boost::system::error_code& err)
{
if (!err)
{
boost::asio::async_read_until(socket_, response_, "\r\n", boost::bind(&client::handle_receive, this, boost::asio::placeholders::error));
}
else
{
std::cout << "Error: " << err.message() << "\n";
}
}
void handle_receive(const boost::system::error_code& err)
{
if (!err)
{
tcpResponse(currentsessionId) ;
}
else
{
std::cout << "Error: " << err << "\n";
}
}
}
Now Coming to my problem. in tcpResponse function, g_requestID contains garbage value. when i debugged by adding log statements, i found that sendDataOverTCP is returned after receiving tcpResponse callback. inside sendDataOverTcp, i am calling async_resolve only. it should return immediately. but it is not.
After debugging, I found following behaviour. async_resolve is working as expected. it is returning immediately. but sendDataOverTCP returns only after tcpResponse callback.
can anybody provide me solution, is it because of some thread scheduling ?
same code is working fine on windows. on linux, i am facing this issue.
I am using boost 1.53.0 on ubuntu 13.04 using virtual box

Boost asio "network connection was aborted by local system" on async_read_some

I'm having an issue creating a really simple TCP based server-client connection using boost asio. When I get a connection from a client on my server and get into the method that handles the async_read_some I check for an error, and am always getting error 1236, which gives the message "The network connection was aborted by the local system."
I've just started working with boost, so I'm not really familiar with how the libraries work and what I could have done wrong. I've provided a cut down version of my code below:
/*Client connection code*/
ClientConnection::ClientConnection(boost::asio::io_service& io_service) : m_Socket(io_service)
{
}
ClientConnection::ClientConnectionPointer ClientConnection::Create(boost::asio::io_service& io_service)
{
return ClientConnection::ClientConnectionPointer(new ClientConnection(io_service));
}
void ClientConnection::handle_write(const boost::system::error_code& error, size_t bytes_transferred)
{
//once we've written our packet, just wait for more
m_Socket.async_read_some(boost::asio::buffer(m_IncomingBytesBuffer, MAX_BYTES_LENGTH),
boost::bind(&ClientConnection::handle_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
void ClientConnection::handle_read(const boost::system::error_code& error, size_t bytes_transferred)
{
if(!error)
{
//deal with the data that comes in here
}
else
{
std::cout << "Error reading port data" << std::endl;
std::cout << error.message() << std::endl;
}
}
tcp::socket& ClientConnection::GetSocket(void)
{
return m_Socket;
}
void ClientConnection::RunClient(void)
{
std::cout << "Client connected." << std::endl;
//start by reading data from the connection
m_Socket.async_read_some(boost::asio::buffer(m_IncomingBytesBuffer, MAX_BYTES_LENGTH),
boost::bind(&ClientConnection::handle_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
/*Listener server code here*/
BarcodeServer::BarcodeServer(boost::asio::io_service& io_service) : m_acceptor(io_service, tcp::endpoint(tcp::v4(), SERVER_PORT_NUMBER))
{
start_accepting_connections();
}
void BarcodeServer::start_accepting_connections(void)
{
std::cout << "Waiting for a connection." << std::endl;
ClientConnection::ClientConnectionPointer new_connection = ClientConnection::Create(m_acceptor.get_io_service());
m_acceptor.async_accept(new_connection->GetSocket(), boost::bind(&BarcodeServer::handle_accepted_connection, this, new_connection, boost::asio::placeholders::error));
}
void BarcodeServer::handle_accepted_connection(ClientConnection::ClientConnectionPointer new_connection, const boost::system::error_code& error)
{
if(!error)
{
new_connection->RunClient();
}
start_accepting_connections();
}
/*main code here*/
try
{
boost::asio::io_service io_service;
BarcodeServer server(io_service);
io_service.run();
}
catch(std::exception& e)
{
cout << "Error when running server:" << endl;
cout << e.what() << endl;
return RETURN_CODE_SERVER_RUN_ERROR;
}
return RETURN_CODE_SUCCESS;
Most of this code is prety much just lifted straight from examples on the boost website, so I'm guessing I've just done something silly somewhere, but I've looked over the code a few times and can't figure out where.
Any help would be much appreciated.
The lifetime of ClientConnection ends after handle_accepted_connection() exits, because all the instances of shared_ptr<ClientConnection> go out of scope and get destroyed.
To avoid this situation, you can either use shared_from_this idiom within ClientConnection member-functions or store 1 shared_ptr<ClientConnection> in some "connection manager".

boost async tcp client

I've just started working with boost.
I'm writting TCP client-server with async sockets.
The task is the following:
Client send to server a number
Client can send another nubmer before receiving server's answer.
Server receives a number, do some computing with it and send back the result to client.
Multiple clients can be connected to server.
Now works the following
send a number from client to sever
server recieves a number in current thread and computes right in the OnReceive handler (I know this is bad...but how I should start a new thread to do computing in parallel)
server sends answer back but client already disconnected
How can allow client to input numbers from keyboard and to wait an answer from the server at the same time?
And why does my client not wait for the answer from sever?
The client code:
using boost::asio::ip::tcp;
class TCPClient
{
public:
TCPClient(boost::asio::io_service& IO_Service, tcp::resolver::iterator EndPointIter);
void Close();
private:
boost::asio::io_service& m_IOService;
tcp::socket m_Socket;
string m_SendBuffer;
static const size_t m_BufLen = 100;
char m_RecieveBuffer[m_BufLen*2];
void OnConnect(const boost::system::error_code& ErrorCode, tcp::resolver::iterator EndPointIter);
void OnReceive(const boost::system::error_code& ErrorCode);
void OnSend(const boost::system::error_code& ErrorCode);
void DoClose();
};
TCPClient::TCPClient(boost::asio::io_service& IO_Service, tcp::resolver::iterator EndPointIter)
: m_IOService(IO_Service), m_Socket(IO_Service), m_SendBuffer("")
{
tcp::endpoint EndPoint = *EndPointIter;
m_Socket.async_connect(EndPoint,
boost::bind(&TCPClient::OnConnect, this, boost::asio::placeholders::error, ++EndPointIter));
}
void TCPClient::Close()
{
m_IOService.post(
boost::bind(&TCPClient::DoClose, this));
}
void TCPClient::OnConnect(const boost::system::error_code& ErrorCode, tcp::resolver::iterator EndPointIter)
{
cout << "OnConnect..." << endl;
if (ErrorCode == 0)
{
cin >> m_SendBuffer;
cout << "Entered: " << m_SendBuffer << endl;
m_SendBuffer += "\0";
m_Socket.async_send(boost::asio::buffer(m_SendBuffer.c_str(),m_SendBuffer.length()+1),
boost::bind(&TCPClient::OnSend, this,
boost::asio::placeholders::error));
}
else if (EndPointIter != tcp::resolver::iterator())
{
m_Socket.close();
tcp::endpoint EndPoint = *EndPointIter;
m_Socket.async_connect(EndPoint,
boost::bind(&TCPClient::OnConnect, this, boost::asio::placeholders::error, ++EndPointIter));
}
}
void TCPClient::OnReceive(const boost::system::error_code& ErrorCode)
{
cout << "receiving..." << endl;
if (ErrorCode == 0)
{
cout << m_RecieveBuffer << endl;
m_Socket.async_receive(boost::asio::buffer(m_RecieveBuffer, m_BufLen),
boost::bind(&TCPClient::OnReceive, this, boost::asio::placeholders::error));
}
else
{
cout << "ERROR! OnReceive..." << endl;
DoClose();
}
}
void TCPClient::OnSend(const boost::system::error_code& ErrorCode)
{
cout << "sending..." << endl;
if (!ErrorCode)
{
cout << "\""<< m_SendBuffer <<"\" has been sent" << endl;
m_SendBuffer = "";
m_Socket.async_receive(boost::asio::buffer(m_RecieveBuffer, m_BufLen),
boost::bind(&TCPClient::OnReceive, this, boost::asio::placeholders::error));
}
else
{
cout << "OnSend closing" << endl;
DoClose();
}
}
void TCPClient::DoClose()
{
m_Socket.close();
}
int main()
{
try
{
cout << "Client is starting..." << endl;
boost::asio::io_service IO_Service;
tcp::resolver Resolver(IO_Service);
string port = "13";
tcp::resolver::query Query("127.0.0.1", port);
tcp::resolver::iterator EndPointIterator = Resolver.resolve(Query);
TCPClient Client(IO_Service, EndPointIterator);
cout << "Client is started!" << endl;
cout << "Enter a query string " << endl;
boost::thread ClientThread(boost::bind(&boost::asio::io_service::run, &IO_Service));
Client.Close();
ClientThread.join();
}
catch (exception& e)
{
cerr << e.what() << endl;
}
cout << "\nClosing";
getch();
}
Here is output from console
Client is starting...
Client is started!
OnConnect...
12
Entered: 12
sending...
"12" has been sent
receiving...
ERROR! OnReceive...
Closing
Server part
class Session
{
public:
Session(boost::asio::io_service& io_service)
: socket_(io_service)
{
dataRx[0] = '\0';
dataTx[0] = '\0';
}
tcp::socket& socket()
{
return socket_;
}
void start()
{
socket_.async_read_some(boost::asio::buffer(dataRx, max_length),
boost::bind(&Session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_read(const boost::system::error_code& error, size_t bytes_transferred)
{
cout << "reading..." << endl;
cout << "Data: " << dataRx << endl;
if (!error)
{
if (!isValidData())
{
cout << "Bad data!" << endl;
sprintf(dataTx, "Bad data!\0");
dataRx[0] = '\0';
}
else
{
sprintf(dataTx, getFactorization().c_str());
dataRx[0] = '\0';
}
boost::asio::async_write(socket_,
boost::asio::buffer(dataTx, max_length*2),
boost::bind(&Session::handle_write, this,
boost::asio::placeholders::error));
}
else
{
delete this;
}
}
void handle_write(const boost::system::error_code& error)
{
cout << "writing..." << endl;
if (!error)
{
cout << "dataTx sent: " << dataTx << endl;
dataTx[0] = '\0';
socket_.async_read_some(boost::asio::buffer(dataRx, max_length),
boost::bind(&Session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
{
delete this;
}
}
string getFactorization() const
{
//Do something
}
bool isValidData()
{
locale loc;
for (int i = 0; i < strlen(dataRx); i++)
if (!isdigit(dataRx[i],loc))
return false;
return true;
}
private:
tcp::socket socket_;
static const size_t max_length = 100;
char dataRx[max_length];
char dataTx[max_length*2];
};
class Server
{
public:
Server(boost::asio::io_service& io_service, short port)
: io_service_(io_service),
acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
{
Session* new_session = new Session(io_service_);
acceptor_.async_accept(new_session->socket(),
boost::bind(&Server::handle_accept, this, new_session,
boost::asio::placeholders::error));
}
void handle_accept(Session* new_session, const boost::system::error_code& error)
{
if (!error)
{
new_session->start();
new_session = new Session(io_service_);
acceptor_.async_accept(new_session->socket(),
boost::bind(&Server::handle_accept, this, new_session,
boost::asio::placeholders::error));
}
else
{
delete new_session;
}
}
private:
boost::asio::io_service& io_service_;
tcp::acceptor acceptor_;
};
int main(int argc, char* argv[])
{
cout << "Server is runing..." << endl;
try
{
boost::asio::io_service io_service;
int port = 13;
Server s(io_service, port);
cout << "Server is run!" << endl;
io_service.run();
}
catch (boost::system::error_code& e)
{
std::cerr << e << "\n";
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
Server's ouput
Server is runing...
Server is run!
reading...
Data: 12
writing...
dataTx sent: 13 //just send back received ++number
reading...
Data:
Your help will be very appreciated
========
Added
Ok, I understand. But check ErrorCode == boost::asio::error::eof does not works... What have I done wrong?
else if (ErrorCode == boost::asio::error::eof)
{
cout << "boost::asio::error::eof in OnReceive!" << endl;
}
else
{
cout << "ERROR! OnReceive..." << ErrorCode << endl;
DoClose();
}
The print out is ERROR! OnReceive...system:10009 it seems to be my comparison is incorrect
========
Added
I found the root cause. I've stated use async_receive (instead of async_read_some) and swaped the lines in main to
ClientThread.join();
Client.Close();
Now it works fine!
Now I'm trying to read and write data from/to socket at the same time (because the client should be able to sent additional requests before answer from the server is recieved.
In OnConnect function I create boost threads:
boost::thread addMsgThread(boost::bind(&TCPClient::addMsgLoop, this));
boost::thread receivingThread(boost::bind(&TCPClient::startReceiving, this));
boost::thread sendingThread(boost::bind(&TCPClient::startSending, this));
with inplementation
void TCPClient::startReceiving()
{
cout << "receiving..." << endl;
m_RecieveBuffer[0] = '\0';
m_Socket.async_receive(boost::asio::buffer(m_RecieveBuffer, m_BufLen),
boost::bind(&TCPClient::receivingLoop, this, boost::asio::placeholders::error)); //runtime error here
cout << "m_RecieveBuffer = " << m_RecieveBuffer << endl;
}
void TCPClient::receivingLoop(const boost::system::error_code& ErrorCode)
{
cout << "receiving..." << endl;
if (ErrorCode == 0)
{
cout << "m_RecieveBuffer = " << m_RecieveBuffer << endl;
m_Socket.async_receive(boost::asio::buffer(m_RecieveBuffer, m_BufLen),
boost::bind(&TCPClient::receivingLoop, this, boost::asio::placeholders::error));
}
else
{
cout << "ERROR! receivingLoop..." << ErrorCode << endl;
DoClose();
}
}
void TCPClient::addMsgLoop()
{
while (true)
{
string tmp;
cin >> tmp;
cout << "Entered: " << tmp << endl;
tmp += "\0";
try
{
msgQueue.push(tmp);
}
catch(exception &e)
{
cerr << "Canno add msg to send queue... " << e.what() << endl;
}
}
}
The issue is the same with both receive and send threads: runtime error (writing access violation somewhere in boost libraries).
void TCPClient::startReceiving()
{
...
m_Socket.async_receive(); //runtime error here
}
In sequent version all works fine (but I don't know how to implement multiple sending before answer).
Can anybody tell me how to fix the issue or how implement this by another way? May be pooling can help but I'm now sure that it is good way.
boost::asio::ip::tcp::socket::async_read_some as the name suggests is not guaranteed to read complete data. It sets error object to boost::asio::error::eof when client is finished writing.
The error you are getting is because of this:
server part
if (!error)
{
...
}
else
{
delete this;
}
In else block, you are assuming that this is a error case and closing the connection. This is not always the case. Before else you need to check for error == boost::asio::error::eof.
Apart from this in read handler, you should keep collecting whatever is read in a buffer till you hit error == boost::asio::error::eof. Only then you should validate read data and write back to client.
Take a look at HTTP server 1, 2, 3 implementation in examples section.
Update: Answer to updated question
You have thread synchronization issue with the updated code.
msgQueue is simultaneously accessed from two or more threads without any lock.
Read and write on the same socket can be called simultaneously.
If I understood your problem correctly, you want to:
take user input and send that to server.
Keep receiving server's response simultaneously.
You can use two boost::asio::io_service::strands for the two tasks. When using Asio, strands are the way to synchronize your tasks. Asio makes sure that tasks posted in a strand are executed synchronously.
In strand1 post a send task that looks like: read_user_input -> send_to_server -> handle_send -> read_user_input
In strand2 post a read task that looks like: read_some -> handle_read -> read_some
This will make sure msgQueue is not accessed simultaneously from two threads. Use two sockets for read and write to server, to make sure simultaneous read and write is not called on the same socket.