4

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;
NmdMystery
  • 2,778
  • 3
  • 32
  • 60
Lehu
  • 116
  • 7
  • I've merged your unregistered account with this one, you can now edit your questions. Additionally, I merged the answer you left with your question. – Tim Post Mar 12 '11 at 11:38
  • @Lehu why are you starting a thread in handle_read? – Sam Miller Mar 12 '11 at 14:02
  • My task is: When I receive message from client, I have to retrive this message in new thread and when thread finishs job, than message to client should be send. – Lehu Mar 12 '11 at 14:17
  • @Sam. Is This thread is a serious problem? – Lehu Mar 12 '11 at 15:44
  • @Lehu yes it is a problem. It seems very odd to me that you start a thread which eventually invokes `socket::async_send` but you also invoke `async_write` on the same socket immediately after starting the thread. What is the point of the thread? – Sam Miller Mar 12 '11 at 15:46
  • @Sam. I updated content of thread function. I received specification to make. And one of requirements is: "Retrieve message from client in new thread, which is created durring receive message from client. When thread finishes job, send some information to client" – Lehu Mar 12 '11 at 16:07
  • 1
    @Lehu you likely have a race condition with the additional thread. **Again**, it is not clear to me why a separate thread is needed. Why can't the work performed by `retriveMessageFromClient` be done in `Session::handle_read`? Asynchronous programming is tricky, introducing threads makes it even harder. I strongly suggest you remove the additional thread and concentrate on getting the single threaded scenario working. I made this [same comment](http://stackoverflow.com/questions/5210796/boost-asio-how-to-write-console-server/5211349#5211349) in one of your previous questions. – Sam Miller Mar 12 '11 at 16:24
  • @Sam, Yes, you are right. Retrieve function is inside 'Session' class now and it works correctly. I have last question to you: Please look at 'TCPAsyncServer' class. There is a piece of code like : 'SessionPtr newSession(new Session(m_ioService, m_mapSessions, ++m_lIDGenerator));' -> Is this new created session as a new thread ? – Lehu Mar 12 '11 at 16:45
  • @Lehu You should post the Session class constrctor code. Though, if you are not instantiating a boost::thread, it is unlikely that a thread is implicitly created. – Sam Miller Mar 12 '11 at 18:37
  • @Sam, I posted Session contructor code. – Lehu Mar 12 '11 at 18:41
  • @Lehu it does not look like you instantiate a thread in the Session ctor. – Sam Miller Mar 12 '11 at 20:16

2 Answers2

2

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;
Sam Miller
  • 23,808
  • 4
  • 67
  • 87
1

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

Pat Mustard
  • 1,852
  • 9
  • 31
  • 58