I am writing simple server using boost::asio.
Currently I have ready part of the server that receives data.
But I have problem. Boost::asio receives only one message (my client program sends multiple number of messages).
After receiving message I must do something to be able to receive another message from the same client?
TCPConnection::TCPConnection(boost::asio::io_service& io_service)
: socket_(io_service),
socketActive(true)
{
Traces() << "\n" << "LOG: TCPConnection::TCPConnection(boost::asio::io_service& io_service) : socket_(io_service)";
}
void TCPConnection::Start()
{
Traces() << "\n" << "LOG: void TCPConnection::Start()";
socket_.async_read_some(boost::asio::buffer(buffer_),
boost::bind(&TCPConnection::HandleRead, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void TCPConnection::HandleRead(const boost::system::error_code& e,
std::size_t bytes_transferred)
{
Traces() << "\n" << "LOG: void TCPConnection::HandleRead(const boost::system::error_code& e, std::size_t bytes_transferred)";
if (!e)
{
Traces() << "\n" << "LOG: New message";
Message tempMessage;
tempMessage.CopyData((boost::shared_ptr<TCPConnection>)this, buffer_.data());
messageQueue->PushBack(tempMessage);
}
else if (e != boost::asio::error::operation_aborted)
{
Traces() << "\n" << "LOG: Close connection";
this->Stop();
}
}
Every time you successfully read data, you need to request the io_service/socket to once again handle a data read. My usual solution looks like this:
void handle_read(asio::error_code ec, size_t bytes_read) {
if(!ec) {
/*Do Stuff*/
socket.async_read(/*buffer*/, handle_read);
} else {
/*Error handling*/
}
}
I haven't syntax checked the code I'm providing here, but you should get the right idea.
Related
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.
Currently I'm looking into sending data with Boost ASIO. I understand that io_service is a abstraction of the underlying OS-dependend IO functionality and that the run() call will poll all outstanding handles in the asio queue and finishs afterwards.
But looking at the SSL example of asio (see below for code) I don't know how I can manage to keep the connection (or session, or whatever) open and read and write data over this connection outside of the normal 'workflow' of the client.
That means: if I call my send() method of the client right in handle_handshake the message is send. But if I try to call c.send() from my main method, nothing happens after Client Handshake success.
Why is send() handled different in the descriped scenario? Is io_service already finished?
Note: Please do not scare about the amount of code below. It is the example code provided with ASIO standalone. I only added a custom send() method to the client and a main to execute client and server in one running example.
Edit
I also tried using asio::io_service::work work(io_service); but the client still does nothing after the handshake.
class session{
public:
session(asio::io_service& io_service,
asio::ssl::context& context)
: socket_(io_service, context) { }
ssl_socket::lowest_layer_type& socket(){
return socket_.lowest_layer();
}
void start(){
std::cout << " Session start "<< "\n";
socket_.async_handshake(asio::ssl::stream_base::server,
std::bind(&session::handle_handshake, this,
std::placeholders::_1));
}
void handle_handshake(const asio::error_code& error){
if (!error){
std::cout << " Session handshake success "<< "\n";
socket_.async_read_some(asio::buffer(data_, max_length),
std::bind(&session::handle_read, this,
std::placeholders::_1, std::placeholders::_2));
}
else {
std::cout << " Session handshake failed "<< "\n";
delete this;
}
}
void handle_read(const asio::error_code& error,
size_t bytes_transferred) {
if (!error) {
std::cout << " Session Read success "<< "\n";
asio::async_write(socket_,
asio::buffer(data_, bytes_transferred),
std::bind(&session::handle_write, this,
std::placeholders::_1));
}
else {
std::cout << " Session Read failed "<< "\n";
delete this;
}
}
void handle_write(const asio::error_code& error){
if (!error){
std::cout << " Write success "<< "\n";
socket_.async_read_some(asio::buffer(data_, max_length),
std::bind(&session::handle_read, this,
std::placeholders::_1,
std::placeholders::_2));
}
else{
delete this;
}
}
private:
ssl_socket socket_;
enum { max_length = 1024 };
char data_[max_length];
};
class server
{
public:
server(asio::io_service& io_service, unsigned short port)
: io_service_(io_service),
acceptor_(io_service,
asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port)),
context_(asio::ssl::context::sslv23) {
context_.set_options(
asio::ssl::context::default_workarounds
| asio::ssl::context::no_sslv2
| asio::ssl::context::single_dh_use);
context_.set_password_callback(std::bind(&server::get_password, this));
context_.use_certificate_chain_file("server.pem");
context_.use_private_key_file("server.pem", asio::ssl::context::pem);
context_.use_tmp_dh_file("dh1024.pem");
start_accept();
}
std::string get_password() const{
return "test";
}
void start_accept(){
std::cout << " Server accept start "<< "\n";
session* new_session = new session(io_service_, context_);
acceptor_.async_accept(new_session->socket(),
std::bind(&server::handle_accept, this, new_session,
std::placeholders::_1));
}
void handle_accept(session* new_session,
const asio::error_code& error){
if (!error){
std::cout << " Server Accept success "<< "\n";
new_session->start();
}
else
{
delete new_session;
}
start_accept();
}
private:
asio::io_service& io_service_;
asio::ip::tcp::acceptor acceptor_;
asio::ssl::context context_;
};
enum { max_length = 1024 };
class client
{
public:
client(asio::io_service& io_service,
asio::ssl::context& context,
asio::ip::tcp::resolver::iterator endpoint_iterator)
: socket_(io_service, context) {
socket_.set_verify_mode(asio::ssl::verify_peer);
socket_.set_verify_callback(
std::bind(&client::verify_certificate, this, std::placeholders::_1, std::placeholders::_2));
asio::async_connect(socket_.lowest_layer(), endpoint_iterator,
std::bind(&client::handle_connect, this,
std::placeholders::_1));
}
bool verify_certificate(bool preverified,
asio::ssl::verify_context& ctx){
return true;
}
void handle_connect(const asio::error_code& error){
if (!error){
std::cout << "Client Connect success "<< "\n";
socket_.async_handshake(asio::ssl::stream_base::client,
std::bind(&client::handle_handshake, this,
std::placeholders::_1));
}
else {
std::cout << "Client Connect failed: " << error.message() << "\n";
}
}
void handle_handshake(const asio::error_code& error){
if (!error) {
std::cout << "Client Handshake success "<< "\n";
//send("test") no send here
}
else{
std::cout << "Client Handshake failed: " << error.message() << "\n";
}
}
void send(char * request_){
std::cout << " Client Sende daten "<< "\n";
size_t request_length = strlen(request_);
asio::async_write(socket_,
asio::buffer(request_, request_length),
std::bind(&client::handle_write, this,
std::placeholders::_1,
std::placeholders::_2));
}
void handle_write(const asio::error_code& error, size_t bytes_transferred){
if (!error)
{
std::cout << " Client Write success "<< "\n";
asio::async_read(socket_,
asio::buffer(reply_, bytes_transferred),
std::bind(&client::handle_read, this,
std::placeholders::_1,
std::placeholders::_2));
}
else {
std::cout << "Client Write failed: " << error.message() << "\n";
}
}
void handle_read(const asio::error_code& error, size_t bytes_transferred) {
if (!error) {
std::cout << "Client Reply: ";
std::cout.write(reply_, bytes_transferred);
std::cout << "\n";
}
else {
std::cout << "Client Read failed: " << error.message() << "\n";
}
}
private:
asio::ssl::stream<asio::ip::tcp::socket> socket_;
char request_[max_length];
char reply_[max_length];
};
using namespace std;
int main(){
std::thread t {
[](){
asio::io_service server_service;
server s(server_service, 8877);
server_service.run();
}
};
std::this_thread::sleep_for(std::chrono::milliseconds(3000));
asio::io_service io_service;
asio::ip::tcp::resolver resolver(io_service);
asio::ip::tcp::resolver::query query("127.0.0.1", std::to_string(8877));
asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
asio::ssl::context ctx(asio::ssl::context::sslv23);
ctx.load_verify_file("ca.pem");
client c(io_service, ctx, iterator);
c.send("test");
io_service.run();
t.join();
return 0;
}
I would like to create client/server communication programs pair using Boost ASIO + SSL. So I started off with the examples provided by boost, and I learned how that works, and I'm almost ready to develop my communication protocol, except that there's one problem.
So starting from this example, I'm modifying the handle_read() callback function after the handshake. The following is my code. My only modification is: Add another callback function called startComm(), which will start the communication.
void handle_read(const boost::system::error_code& error,
size_t bytes_transferred)
{
if (!error)
{
std::cout << "Reply: ";
std::cout.write(reply_, bytes_transferred);
std::cout << "\n";
boost::asio::async_write(socket_,
boost::asio::buffer(std::string("Now?")),
boost::bind(&SSLClient::startComm, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
{
std::cout << "Read failed: " << error.message() << "\n";
}
}
void startComm(const boost::system::error_code& error,
size_t bytes_transferred)
{
if (!error)
{
std::cout << "Reply: ";
std::cout.write(reply_, bytes_transferred); //problem here, bytes transferred should contain the number of received chars not number of written chars
std::cout << "\n";
}
else
{
std::cout << "Read failed: " << error.message() << "\n";
}
}
In the async_write() above, there's an argument boost::asio::placeholders::bytes_transferred which parametrizes my callback function to provide the number of bytes that were sent to the server. Now I would like to know the number of bytes the server responded with. How can I do that in my simple example?
Thanks. If you require any additional details, please ask.
The write call sends data.
Since it doesn't, at all, receive data the number of bytes received is by definition 0.
If you want to receive data, use (async_)read and it will tell you the number of bytes received.
These call backs use the same placeholder (bytes_transferred) but it carries different meaning depending on the direction of the transfer that has been completed.
Here's a solution that technically does what you want: define an extra parameter of startComm and bind it (not with a placeholder).
void handle_read(const boost::system::error_code &error, size_t bytes_received) {
if (!error) {
std::cout << "Reply: ";
std::cout.write(reply_, bytes_received);
std::cout << "\n";
boost::asio::async_write(socket_, boost::asio::buffer(std::string("Now?")),
boost::bind(&SSLClient::startComm,
this,
boost::asio::placeholders::error,
bytes_received,
boost::asio::placeholders::bytes_transferred));
} else {
std::cout << "Read failed: " << error.message() << "\n";
}
}
void startComm(const boost::system::error_code &error, size_t had_received, size_t bytes_sent) {
if (!error) {
std::cout << "Reply: ";
std::cout.write(reply_, had_received);
std::cout << "\n";
} else {
std::cout << "Write failed: " << error.message() << "\n";
}
}
Note that I still think you might mistakenly expect async_write to receive a reply, which (obviously?) isn't the case
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".
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.