I am trying to wrap the boost TCP using a new class in c++. Things work like a charm while I call the boost function directly. However I fail to call socket close while the close is wrap in a class function. Please help have a look on the following codes.
class defination:
typedef boost::shared_ptr<tcp::socket> Socket;
class TCPConnector{
public :
bool isConnected;
Socket sock;
string ip;
int port;
TCPConnector(string ip, int port);
void Close();
bool Connect();
};
functions:
TCPConnector::TCPConnector(string ip,int port):ip(ip),port(port)
{
}
void TCPConnector::Close() {
boost::system::error_code error;
if (!isConnected)
return;
isConnected = false;
try {
sock->shutdown(boost::asio::ip::tcp::socket::shutdown_both, error);
cout << "ShutDown" << endl;
if (error)
throw boost::system::system_error(error);
sock->close(error);
if (error)
throw boost::system::system_error(error);
} catch (exception& e) {
cout << "#TCPConnector::Close()#" << e.what() << endl;
}
}
Main Function:
int main(int argc, char* argv[]) {
try {
TCPConnector* conn = new TCPConnector("127.0.0.1",8088);
for (int i = 0; i < 2; i++) {
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
tcp::resolver::query query(tcp::v4(), "127.0.0.1", "8088");
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
conn->sock.reset(new tcp::socket(io_service));
conn->sock->connect(*endpoint_iterator);
cout << "Connected" << endl;
boost::thread acceptorThread(boost::bind(receive,conn));
sleep(1);
unsigned char msg[8] = { 0, 6, 55, 56, 55, 56, 55, 0 };
boost::system::error_code error;
try {
boost::asio::write(*conn->sock, boost::asio::buffer(msg, 8),
boost::asio::transfer_all(), error);
cout << "Sent" << endl;
if (error)
throw boost::system::system_error(error);
conn->sock->shutdown(boost::asio::ip::tcp::socket::shutdown_both,
error);
if (error)
throw boost::system::system_error(error);
conn->sock->close(error);//close socket directly , the result is ok
//conn->Close();// close by calling functions, it causes problems.
cout << "Closed" << endl;
if (error)
throw boost::system::system_error(error);
io_service.stop();
} catch (std::exception& e) {
std::cerr << "Exception in thread: " << e.what() << "\n";
}
cout << "Sleep" << endl;
sleep(2);
cout << "Wake up" << endl;
}
} catch (std::exception& e) {
std::cerr << e.what() << std::endl;
}
return 0;
}
These 2 lines give the different behaviours. I don't know why the second one will cause problem.
conn->sock->close(error);//close socket directly , the result is ok
conn->Close();// close by calling functions, it causes problems.
mutex: Invalid argument was printed on
sock->close(error);
if (error)
throw boost::system::system_error(error);
Is the problem related to shared_ptr? or I missed something important to close the socket?
Thanks for any suggestion.
The problem is that the io_service should outlive the socket.
On all but the first iteration of the for loop, the statement conn->sock.reset(new tcp::socket(io_service)); calls the destructor of the previous iteration's socket. This destructor accesses elements of the previous iteration's io_service (specifically its mutex) which by that point have themselves been destroyed.
To fix this, you can move the io_service outside the for loop, or you can call conn->sock.reset(); at the end of the for loop in order to invoke the socket's destructor while the io_service is still valid.
Related
I'm trying to make a TCP/IP client using boost library. This is how I designed my program
->read thread to read from the server
->write thread to send commands
->a function that parses the read data from the server
int main()
{
TCP_IP_Connection router;
router.Create_Socket();
boost::thread_group t;
t.create_thread(boost::bind(&TCP_IP_Connection::get_status,&router,'i'));
t.create_thread(boost::bind(&TCP_IP_Connection::readTCP,&router));
std::string reply="\nend of main()";
std::cout<<reply;
t.join_all();
return 0;
}
void TCP_IP_Connection::Create_Socket()
{
tcp::resolver resolver(_io);//resolve into TCP endpoint
tcp::resolver::query query(routerip,rport);
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
//list of endpoints
tcp::resolver::iterator end;
boost::asio::streambuf b;
_socket = new tcp::socket(_io); //create socket
boost::system::error_code error= boost::asio::error::host_not_found;
try
{
while (error && endpoint_iterator != end) //if error go to next endpoint
{
_socket->close();
_socket->connect(*endpoint_iterator++, error);
}
if(error)
throw boost::system::system_error(error);
//else the router is connected
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
void TCP_IP_Connection::get_status(char p)
{
try
{
if(p=='i')
_socket->send(boost::asio::buffer("llist\n\n"));
//sending command for input command
else
_socket->send(boost::asio::buffer(" sspo l1\n\n"));
//sending signal presence for output command
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
void TCP_IP_Connection::readTCP()
{
this->len=0;
boost::system::error_code error= boost::asio::error::host_not_found;
try
{ //loop reading all values from router
while(1)
{
//wait for reply??
_socket->async_read_some(boost::asio::buffer(this-
>reply,sizeof(this>reply)),boost::bind(&TCP_IP_Connection::dataProcess,this,
boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));
_io.run();
if(error==boost::asio::error::eof) //connection closed by router
std::cout<<"connection closed by router";
}
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
void TCP_IP_Connection::dataProcess(const boost::system::error_code &er,size_t l)
{
if(!er)
{
if(l>0)
{
for(int i=0;i<l;i++)
{
this->data[i]=this->reply[i];
//if(data[i]="\n")
std::cout<<this->data[i];
}
}
}
}
When I run the code all I get is the response from the server that says the client is connected and not the response of the command I send. But when I try debugging I get full output as I need. Am I doing anything wrong in the threading, or in the TCP read buffer.
Your code is creating 2 threads. The first thread created has a thread function called get_status. In get_status, there is no looping so it only executes the code once. It appears to be sending the string "llist\n\n" to the server and this is done synchronously. After that, it does not send anything else. So, are you expecting the server to send other data after the first command is sent? The code in the first thread may or may not execute completely before the code in the second thread executes.
The second thread is created and this thread appears to be responsible for processing information coming off of the socket. There is an infinite loop of while(1), but no logic to exit the loop so it will run forever unless an exception is thrown. I believe that the async_read_some method will not cause any data to be transferred until the buffer is full. The size of the buffer is specified by the size of reply. This may be your problem since the dataProcess method won't get called until all of the data specified by the length of reply has been received. In many protocols, the first 4 bytes specifies the length of the message. So, if you are dealing with variable length messages, then your code will have to take this into account.
One other item worth mentioning is that the looping code in readTCP to call _io.Run is not really necessary. You can add a work object to your io_service object in order for it to run continuously. For example:
void SSLSocket::InitAsynchIO()
{
// This method is responsible for initiating asynch i/o.
boost::system::error_code Err;
string s;
stringstream ss;
//
try
{
ss << "SSLSocket::InitAsynchIO: Worker thread - " << Logger::NumberToString(boost::this_thread::get_id()) << " started.\n";
Log.LogString(ss.str(), LogInfo);
// Enable the handlers for asynch i/o. The thread will hang here until the stop method has been called or an error occurs.
// Add a work object so the thread will be dedicated to handling asynch i/o.
boost::asio::io_service::work work(*IOService);
IOService->run();
Log.LogString("SSLSocket::InitAsynchIO: receive worker thread done.\n", LogInfo);
}
catch (std::exception& e)
{
stringstream ss;
ss << "SSLSocket::InitAsynchIO: threw an error - " << e.what() << ".\n";
Log.LogString(ss.str(), LogError);
Stop();
}
}
It is ok to have your first thread do your first async read. Your read handler can be set up to call itself in order to handle the next message. For example:
void SSLSocket::HandleRead(const boost::system::error_code& error, size_t bytesTransferred)
{
// This method is called to process an incomming message.
//
std::stringstream ss;
int ByteCount;
try
{
ss << "SSLSocket::HandleRead: From worker thread " << boost::this_thread::get_id() << ".\n";
Log.LogString(ss.str(), LogInfo);
// Set to exit this thread if the user is done.
if (!ReqAlive)
{
// IOService->stop();
return;
}
if (!error)
{
// Get the number of bytes in the message.
if (bytesTransferred == 4)
{
ByteCount = BytesToInt(pDataBuf);
}
else
{
// Call the C# callback method that will handle the message.
ss << "SSLSocket::HandleRead: From worker thread " << boost::this_thread::get_id() << "; # bytes transferred = " << bytesTransferred << ".\n";
Log.LogString(ss.str(), LogDebug2);
Log.LogBuf(pDataBuf, (int)bytesTransferred, true, LogDebug3);
Log.LogString("SSLSocket::HandleRead: sending msg to the C# client.\n\n", LogDebug2);
CallbackFunction(this, bytesTransferred, (void*)pDataBuf);
// Prepare to read in the next message length.
ByteCount = MsgLenBytes;
}
pDataBuf = BufMang.GetPtr(ByteCount);
boost::system::error_code Err;
// boost::asio::async_read(pSocket, boost::asio::buffer(pDataBuf, ByteCount), boost::bind(&SSLSocket::HandleRead,
// this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
Locking CodeLock(SocketLock); // Single thread the code.
boost::asio::async_read(*pSocket, boost::asio::buffer(pDataBuf, ByteCount), boost::bind(&SSLSocket::HandleRead,
this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
// boost::asio::read(pSocket, boost::asio::buffer(reply_), boost::asio::transfer_exactly(ByteCount), Err);
}
else
{
Log.LogString("SSLSocket::HandleRead failed: " + error.message() + "\n", LogError);
Stop();
}
}
catch (std::exception& e)
{
stringstream ss;
ss << "SSLSocket::HandleRead: threw an error - " << e.what() << ".\n";
Log.LogString(ss.str(), LogError);
Stop();
}
}
If none of the above is helpful, then put in some debug code that logs all of the calls to a log file so that you can see what is going on. You might also want to consider downloading Wire Shark in order to see what data is going out and coming in.
I wonder how to implement a synchronous socket accept with boost which can be terminated.
To demonstrate my problem I slightly modified the synchonous tcp echo example.
Note: the provided code seems to be working on Windows platforms but i'm having problems on a Linux machine.
Let's say the server receives a quit message and now wants to terminate an endless loop which accepts new connections.
Most tutorials etc. recommend you to run acceptor->close() in this case. But as
this post states, the results might be undefined if close() is called from another thread.
If you do so, accept() won't terminate this time but when another client tries to connect it returnes an error (on Linux!)
So my question again: how do I correctly terminate a server which is based on boost::asio which continuously synchronously accepts connections?
Here the code:
#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/smart_ptr.hpp>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
using boost::asio::ip::tcp;
void session(boost::shared_ptr<tcp::socket> sock, tcp::acceptor *acceptor )
{
try {
for (;;) {
char data[ 1024 ];
boost::system::error_code error;
size_t length = sock->read_some(boost::asio::buffer(data), error);
if (error == boost::asio::error::eof) { break; }
else if (error) { throw boost::system::system_error(error); }
if( std::string("quit") == data ) { // TRY TO CANCEL THE ACCEPT
acceptor->close();
break;
}
boost::asio::write(*sock, boost::asio::buffer(data, length));
}
}
catch (std::exception& e) { std::cerr << "exception in thread: " << e.what() << "\n"; }
}
void server(boost::asio::io_service& io_service, short port)
{
tcp::acceptor a( io_service, tcp::endpoint(tcp::v4(), port) );
for (;;) {
boost::shared_ptr<tcp::socket> sock(new tcp::socket(io_service));
boost::system::error_code error;
a.accept( *sock, error );
if( !error ) {
boost::thread t( boost::bind( session, sock, &a ) );
}
else {
std::cout << "acceptor canceled "<< error << std::endl;
break;
}
}
}
int main(int argc, char* argv[])
{
try{
// ..check args here...
boost::asio::io_service io_service;
server(io_service, std::atoi(argv[1]));
}
catch (std::exception& e) {std::cerr << "Exception: " << e.what() << "\n";}
return 0;
}
I want simply to have a chat client that is asynchronously listening from a socket and from a boost::asio::posix::stream_descriptor assigned to stdin.
If I run this code in a single threaded application, all works fine.
If I call io_service.run() from 2 or more threads, the asynchronous operations from stdin, never go fine, but async readings from the socket are still executed.
here is the code:
MasterClient::MasterClient(boost::asio::io_service& io_service,boost::asio::ip::tcp::resolver::iterator iter, string nickName)
:it(iter),chatNick(nickName)
{
this->fdIn_ = str_ptr(new boost::asio::posix::stream_descriptor(io_service,::dup(STDIN_FILENO)));
this->dirServer_ = new(connectedPeer);
this->dirServer_->sock = socket_ptr(new boost::asio::ip::tcp::socket(this->io_service_));
boost::asio::async_connect(*(this->dirServer_->sock), this->it,
boost::bind(&MasterClient::connectionHandler, this,
boost::asio::placeholders::error));
}
main:
int main(int argc, const char * argv[])
{
boost::asio::io_service io_service(2);
boost::asio::io_service::work work(io_service);
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::resolver::query query(argv[1], argv[2]);
boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
string nick;
cout << "Inserire un nickname per la sessione di chat: " << flush;
getline(cin,nick);
MasterClient cli(io_service,iterator,nick);
cli.run();
}
and the MasterClient::run()
void MasterClient::run()
{
// Create a pool of threads to run all of the io_services.
std::vector<boost::shared_ptr<boost::thread> > threads;
boost::asio::io_service::work work(this->io_service_);
for (std::size_t i = 0; i < 1; ++i)
{
boost::shared_ptr<boost::thread> thread(new boost::thread(boost::bind(&boost::asio::io_service::run, &io_service_)));
threads.push_back(thread);
}
// Wait for all threads in the pool to exit.
for (std::size_t i = 0; i < threads.size(); ++i)
threads[i]->join();
}
here i call the async readings:
void MasterClient::clientDelegate()
{
if(this->connectToServer())
{
this->mainMenu();
boost::asio::async_read_until(*fdIn_, inBuff_, '\n',
boost::bind(&MasterClient::fdInMenuHandler,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
(*(this->dirServer_->sock)).async_read_some(boost::asio::buffer(this->buff_),
boost::bind(&MasterClient::serverHandler,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred,
this->dirServer_->sock));
this->io_service_.post(boost::bind(&MasterClient::printer,this));
}else
{
if(this->isDebugging)
cout << "Error in ClientDelegate." << endl;
}
if(this->isDebugging)
cout << "ClientDelegate END" << endl;
}
connection handler, where the clientDelegate gets called:
void MasterClient::connectionHandler(const boost::system::error_code& error)
{
cout << "connected" << endl;
try
{
if(error)
throw boost::system::system_error(error);
else
{
this->dirServer_->endpoint = boost::asio::ip::tcp::endpoint((*(this->dirServer_->sock)).remote_endpoint());
this->clientDelegate();
}
}catch(const boost::system::system_error& e)
{
cerr << "Boost Exception in ConnectHandler ---> " << e.what() << endl;
this->io_service_.stop();
}
}
What am i doing wrong?
According to the documentation of boost::asio::posix::stream_descriptor, it's unsafe to use the same instance from multiple threads. In multithreading situations, typically the handlers are wrapped into a strand for serialisation.
In your case I don't see the point of using multiple threads for the same client connection.
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.
i have a problems with ASIO boost library. The problem are asynchronous functions.
This is simple server code.
void handle_accept( const boost::system::error_code& error )
{
std::cout << "Someone connected" << std::endl;
}
void handle_read( const boost::system::error_code& error )
{
printf( "Message: %s \n", somedata);
}
int main()
{
std::cout << "Starting server ....\n" << std::endl;
try
{
boost::asio::io_service io_service;
tcp::socket mysocket(io_service);
tcp::acceptor myacceptor(io_service, tcp::endpoint(tcp::v4(), 5000));
myacceptor.async_accept( mysocket, boost::bind( &handle_accept, boost::asio::placeholders::error));
mysocket.async_receive( boost::asio::buffer(somedata, 1024) , boost::bind( &handle_read, boost::asio::placeholders::error) );
io_service.run();
std::cout << "END. \n" << std::endl;
sleep(5);
} catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
and client code here
int main()
{
std::cout << "Starting client ....\n" << std::endl;
try
{
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
tcp::resolver::query query(tcp::v4(), "localhost", "5000");
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::socket mysocket(io_service);
boost::asio::connect(mysocket, endpoint_iterator);
sleep(5);
sprintf( somedata, "This is a message i sent.");
mysocket.send( boost::asio::buffer(somedata, 1024) );
} catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
So, How it should work. Server should wait on connection from client, when client is connected, it should call handle_accept. When client is connected, handle_accept is really called, but it also calls handle_read even no data is recieved !!! why ? client sends data after 5 seconds after making connection.
thank you very much.
The receive completes immediately because it cannot wait because it has nothing to wait for. No data will ever be received on a TCP listening socket. Call async_receive on your connected sockets only.