I am trying to read specific number of bytes from the socket. My server is sending:
1) byte[0] - length of the message
2) byte[1:N] - the actual message
How do I read the first byte and then read the remaining bytes using boost::asio::ip::tcp::socket::read ? Here is the code snippet:
// receive data through the socket
void TCPTestClient::ReceiveData( )
{
try
{
boost::system::error_code error;
boost::asio::streambuf receivedStreamBuffer;
// reserve 512 bytes in output sequence
boost::asio::streambuf::mutable_buffers_type bufs =receivedStreamBuffer.prepare( 512 );
boost::asio::read( m_socket,
bufs,
error );
// transfer the buffer contents to string
std::istream is( &receivedStreamBuffer );
is >> m_receivedMessageStr;
// throw exception if error occurred
if ( error )
{
throw NetworkTestFailedException( error.message() );
}
}
catch(...)
{
}
}
You'll want to prepare a buffer for the one byte header, then prepare another buffer for the message. A simplified example might be
boost::asio::read(
m_socket,
receivedStreamBuffer.prepare(1),
error
);
if ( error ) {
std::cerr << "Read header failed: " << boost::system::system_error(error).what() << std::endl;
return;
}
receivedStreamBuffer.commit(1);
std::istream header( &receivedStreamBuffer );
uint8_t size;
header >> size;
// reserve message size in output sequence
boost::asio::read(
m_socket,
receivedStreamBuffer.prepare( size ),
bufs,
error
);
if ( error ) {
std::cerr << "Read message failed: " << boost::system::system_error(error).what() << std::endl;
return;
}
receivedStreamBuffer.commit( size );
// transfer the buffer contents to string
std::istream is( &receivedStreamBuffer );
is >> m_receivedMessageStr;
Related
I have created a ClientSocket and a ServerSocket class for simplifying functions. while sending a data, at first I am sending a 16 bytes header containing the message length followed by the message. But I am having trouble while sending data from client to server on the 2nd time. At first it is sending the header and the message properly but after that I am getting 0 bytes output from read() in ServerSocket::get_message while reading the header from the client. Please help me out here.
Sending and receiving part in Server.cpp
string ServerSocket::get_message(int client_socket_fd) {
//char *header = client_buffers[client_socket_fd].read_header;
char *read_buffer = client_buffers[client_socket_fd].read_buffer;
char header[16];
memset(header, 0, sizeof(header));
int read_result = -1;
read_result = read(client_socket_fd, header, 16);
cout << read_result << endl;
if (read_result > 0){
int read_size = stoi(string(header));
cout << read_size << endl;
memset(read_buffer, 0, sizeof(read_buffer));
read_result = read(client_socket_fd, read_buffer,read_size);
if (read_result > 0) return string(read_buffer);
}
cerr << "Unable to recieve message from client socket " << client_socket_fd << endl;
return "";
}
int ServerSocket::_send(int client_socket_fd, string message) {
//char *header = client_buffers[client_socket_fd].write_header;
char *write_buffer = client_buffers[client_socket_fd].write_buffer;
char header[16];
memset(header, 0, sizeof(header));
string write_size = to_string(message.length());
copy(write_size.begin(), write_size.end(), header);
int write_result = write(client_socket_fd, header, 16); // sending size of message
if (write_result > 0) {
write_result = write(client_socket_fd, message.c_str(), message.length());
}
if (write_result <= 0)
cerr << "Unable to send to client socket fd : " << client_socket_fd << endl;
return write_result;
}
Sending and receiving part in Client.cpp
string ClientSocket::_recieve(){
char read_header[16];
memset(read_header, 0, sizeof(read_header));
int read_result = read(socket_fd, read_header, 16);
if (read_result >0) {
int read_size = stoi(string(read_header));
memset(recieve_buffer, 0, sizeof(recieve_buffer));
read_result = read(socket_fd, recieve_buffer, read_size);
}
if ( read_result > 0) return string(recieve_buffer);
cerr << "Unable to read from server." << endl;
return "";
}
int ClientSocket::_send(string message) {
char write_header[16];
memset(write_header, 0, sizeof(write_header));
cout << message.length() << endl;
string s = to_string(message.length());
copy(s.begin(),s.end(), write_header);
int write_result = write(socket_fd, write_header, 16);
if (write_result > 0)
write_result = write(socket_fd, message.c_str(), message.length());
if (write_result <=0) cerr << "Unable to send message : "<< message << endl;
return write_result;
}
The code exhibits the two most frequent errors when using sockets:
Socket send/write and recv/read may not send/receive the number of bytes requested. The code must handle partial reads/writes in order to work correctly.
The received socket data is not zero-terminated. You need to zero-terminate the received data before passing it to functions that expect zero-terminated stings (std::string and stoi here). memset doesn't help when recv fills the entire buffer, you need to reserve one extra byte for the null terminator that recv doesn't overwrite.
I'm reading stdin using Boost.ASIO, but when I pipe into it I would expect that the pipe would close when the input has been fully consumed. I.e. I'm doing this at the commmand line:
cat somefile.txt | myprog
And I'd expect that myprog will see the file close. Instead it waits forever.
The code looks like this:
boost::asio::posix::stream_descriptor as_stdin(ios);
{
boost::system::error_code error;
as_stdin.assign(dup(STDIN_FILENO), error);
if ( error ) {
exit(2);
}
}
auto proc = [&as_stdinr](auto yield) {
boost::asio::streambuf buffer;
while ( as_stdin.is_open() ) {
auto bytes = boost::asio::async_read_until(as_stdin, buffer, '\n', yield);
if ( bytes ) {
buffer.commit(bytes);
std::istream in(&buffer);
std::string line;
std::getline(in, line);
std::cerr << line << std::endl;
} else {
std::cerr << "No bytes read" << std::endl;
}
}
std::cerr << "Done" << std::endl;
};
boost::asio::spawn(ios, proc);
All of the file content is properly echoed, so reading from the pipe works fine, but neither of the "No bytes read" or "Done" messages are ever printed. I've tried both with and without the dup system call.
Am I misunderstanding how the pipe works, or am I doing something wrong or missing something else?
I think this comes down to "How do I detect EOF when using coroutines?"
You could catch the exception from async_read_until
size_t bytes = 0;
bool eof = false;
try {
bytes = boost::asio::async_read_until(as_stdin, buffer, '\n', yield);
} catch(std::exception const& e) {
std::cerr << "Exception: " << e.what() << "\n";
bytes = 0;
eof = true;
}
// ...
if (eof) break;
Or use the error_code:
boost::system::error_code ec;
auto bytes = boost::asio::async_read_until(as_stdin, buffer, '\n', yield[ec]);
// ...
if (ec) {
std::cerr << "Error: " << ec.message() << "\n";
break;
}
Output is very similar in both cases
Exception: End of file
No bytes read
Done
Or
No bytes read
Error: End of file
Done
Limitations
Regular files cannot be used with POSIX stream_descriptor, see https://stackoverflow.com/a/23631715/85371
I am made a Server & Client Asynchronous Application. All works perfectly except the message I receive. I am sending image pieces into strings. But when I receive them back, the string is corrupted, I that it's not the same as I send. The length it's the same, and almost all characters. If I compare what I send with what I received I have like 300 characters different from what I sent. I am sending strings of 50.000 characters. Any idea what may be the problem? The most of the code are comments, so you will understand it in seconds. Also, I shrinked it and made it easier for you to read.
I am sending with this.
// Send a message
void StartSendMessage ( MessagePtr msg )
{
// As long as the queue is not empty, the 'sending agent' is still alive
bool writeInProgress =! m_messageQueue.empty() ;
// Queue the message
m_messageQueue.push ( msg ) ;
if ( msg -> BodyLength() != 0 )
{
std:: cout << "Sending :" << msg -> BodyLength() << std:: endl ;
}
// If the 'sending agent' is inactive, start it
if ( !writeInProgress )
{
// Send message asynchronously. We leave the message on the queue
// since it needs to be available during the async read
async_write ( m_socket , boost::asio::buffer ( msg -> HeaderData() , msg -> SendLength () ) ,
boost::bind ( &ASyncConnectionMT::HandleSentMessage , this , boost::asio::placeholders::error , boost::asio::placeholders::bytes_transferred ) ) ;
}
}
// Message was sent
void HandleSentMessage ( const boost::system::error_code& ec , size_t size )
{
// Check the error code
if ( ec )
{
// Transfer error
std:: cout << "Error sending message: " << ec.message() << std:: endl ;
DoStop() ;
return ;
}
// Remove the sent message from queue
m_messageQueue.pop() ;
// If the que is not empty, send next message asynchronously.
// We leave the message on the que since it needs to be available during the async send
if ( !m_messageQueue.empty() )
{
MessagePtr msg = m_messageQueue.front() ;
std:: cout << "Message send lenght "<< msg->SendLength() ;
async_write ( m_socket , boost::asio::buffer ( msg -> HeaderData() , msg -> SendLength () ) ,
boost::bind ( &ASyncConnectionMT:: HandleSentMessage , this , boost::asio::placeholders::error , boost::asio::placeholders::bytes_transferred ) ) ;
}
}
I am reading with this.
void StartReceiving()
{
// Create receive buffer
BufferPtr receiveBuffer ( new Buffer ) ;
// Start async read, must pass 'this' as shared_ptr, else the
// 'this' object will be destroyed after leaving this function
m_socket.async_read_some ( boost::asio::buffer ( *receiveBuffer ) , boost::bind ( &ASyncConnectionMT::HandleReceivedd , shared_from_this() , receiveBuffer ,
boost::asio::placeholders::error , boost::asio::placeholders::bytes_transferred ) );
}
// Handle received data
void HandleReceivedd ( BufferPtr receiveBuffer , const boost::system::error_code& ec , size_t size)
{
if ( !ec )
{
BufferPtr sendBuffer ( new Buffer ) ;
std:: cout << m_socket.remote_endpoint() << ": Message received: " << std:: string (receiveBuffer -> data() , size ) << std:: endl << std:: endl;
std:: cout << "Message lenght received " << size << std:: endl;
// Start receiving next bit
StartReceiving() ;
}
else if ( ec == boost::asio::error::eof)
{
// Client disconnected. Close the socket.
std:: cout << m_socket.remote_endpoint() << ": Connection closed ( handle received )" << std:: endl;
m_socket.close();
}
}
I see several problems in this chunk of code:
1) When you send, you are putting copy of msg into m_messageQueue. But, when you call async_write, your buffer is constructed from pointer taken from msg, not m_messageQueue. So eventually you can send from incorrent buffer.
2) On receive you creating receiveBuffer on stack. When async_read_some immediatelly returns (almost always), your receiveBuffer will be destroyed because you exit from StartReceiving call.
3) Same with sendBuffer
I am trying to make a server & client in boost asio. Currently I am receiving this error. Can you point me what I am doing wrong?
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
// Forward declaration
class ASyncConnectionMT;
class ASyncEchoServerMT;
// Typedef for the buffer type (shared_ptr)
typedef boost::array < char , 65536 > Buffer;
typedef boost::shared_ptr < Buffer > BufferPtr;
// Typedef for the ASyncConnectionMT shared_ptr
// Typedef for the ASyncEchoServerMT shared_ptr
// Derived from "enable_shared_from_this" so the 'this' object can
// be passed as shared_ptr to the callback function
typedef boost::shared_ptr < ASyncConnectionMT > ASyncConnectionMTPtr;
typedef boost::shared_ptr < ASyncEchoServerMT > ASyncEchoServerMTPtr;
// Class the handles the client
class ASyncConnectionMT : public::boost::enable_shared_from_this<ASyncConnectionMT>
{
private: // The socket class for communication.
boost::asio::ip::tcp::socket m_socket;
// Strand object to synchronise calling handlers. Multiple threads might acces the socked
// at the same time since send and recieve are started asynchronously at the same time
boost::asio::strand m_strand;
public: // Constructor with the IO service to use
ASyncConnectionMT ( boost::asio::io_service& ios) : m_socket(ios), m_strand (ios)
{
}
// Retrieve the socked used by this connection
// Need to be passed to acceptor.accept() function
boost::asio::ip::tcp::socket& Socket()
{
return m_socket;
}
// Start handling the connection
void Start()
{
std:: cout << m_socket.remote_endpoint() << ": Connection accepted" << std:: endl ;
StartReceiving() ;
}
// Start receiving data
void StartReceiving()
{
// Create receive buffer
BufferPtr receiveBuffer ( new Buffer ) ;
// Start async read, must pass 'this' as shared_ptr, else the
// 'this' object will be destroyed after leaving this function
async_write ( m_socket, boost::asio::buffer ( *receiveBuffer ) , m_strand.wrap ( boost::bind ( &ASyncConnectionMT::HandleReceived, shared_from_this() ,
receiveBuffer , boost::asio::placeholders::error ,
boost::asio::placeholders::bytes_transferred ) ) ) ;
}
// Handle received data
void HandleReceived ( BufferPtr receiveBuffer , const boost::system::error_code& ec , size_t size)
{
if (!ec)
{
// Print received message
std:: cout << m_socket.remote_endpoint() << ": Message received: " << std:: string (receiveBuffer -> data() , size ) << std:: endl ;
// Convert to uppercare. We can't use the same buffer because that could be
// overwritten by another recieve
// UPD -> boost shared_ptr<TBuffer> sendBuffer(new TBuffer());
BufferPtr sendBuffer ( new Buffer ) ;
for ( size_t i=0 ; i!=size ; i++ )
{
(( &sendBuffer )[i]) = toupper (( &receiveBuffer )[i]) ;
}
// Start sending reply, must pass 'this' as shared_ptr, else the 'this' object will be
// destroyed after leaving this function. We pass the buffer as shared_ptr to the handler
// so the buffer is still in memory after sending is complete. Without it, the buffer could
// be deleted before the send operation is complete. The Handle Set is now synchronised via the strand.
async_write ( m_socket, boost::asio::buffer ( *sendBuffer , size ) , m_strand.wrap ( boost::bind ( &ASyncConnectionMT::HandleSent ,
shared_from_this() , sendBuffer ,
boost::asio::placeholders::error ,
boost::asio::placeholders::bytes_transferred ) ) ) ;
// Start receiving next bit
StartReceiving() ;
}
else if ( ec == boost::asio::error::eof)
{
// Client disconnected. Close the socket.
std:: cout << m_socket.remote_endpoint() << ": Connection closed ( handle received )" << std:: endl;
m_socket.close();
}
}
// Handle for when the data si sent
void HandleSent ( BufferPtr sendBuffer , const boost::system::error_code& ec , size_t size)
{
if (!ec)
{
// Start receiving again
StartReceiving() ;
}
else if ( ec == boost::asio::error::eof)
{
// Client disconnected. Close the socket.
std:: cout << m_socket.remote_endpoint() << ": Connection closed ( handle received )" << std:: endl;
m_socket.close();
}
else
{
std:: cout << "Error: " << ec.message << std:: endl ;
}
}
};
I am receiving the following errors.
C3967 - boost::system::error_code::message': function call missing argument list; use '&'boost::system::error_code::message' to create a pointer to member
C2664 - 'toupper' : cannot convert parametor 1 from 'BufferPtr' to 'int'
C2512 - 'boost::array' : no appropriate default constructor available
C2039 - 'data' : is not a member of 'boost::shared_prt'
C2027 - use of undefined type 'boost::array '
pointer to incomplete class type is not allowed
no suitable conversion function from "BufferPtr" to "int" exists
incomplete type is not allowed
Thanks in advance.
You missed the include
#include <boost/array.hpp>
The toupper loop is wrong
((&sendBuffer)[i]) = toupper((&receiveBuffer)[i]);
should be more like
((*sendBuffer)[i]) = toupper((*receiveBuffer)[i]);
or even more like
std::transform(receiveBuffer->begin(), receiveBuffer->end(), sendBuffer->begin(), static_cast<int(&)(int)>(std::toupper));
Pro tip: consider using unsigned char in the buffer to avoid unwanted sign extension when passing int to_upper
((*sendBuffer)[i]) = toupper(static_cast<unsigned char>((*receiveBuffer)[i]));
like the comment (and the compiler message) says:
std::cout << "Error: " << ec.message << std::endl;
should be
std::cout << "Error: " << ec.message() << std::endl;
Then it compiles. No guarantees it does what you expect (I haven't read the code)
For some reason when I connect to my C++ server with Putty I get this
Recv: (string I typed)
Recv:
This happens everytime I send characters to my server using Putty
Source follows. If nessicary I will post the rest of my source. TYIA -Roland
void recvthread( void *pParams ) {
char buffer[128]
int err;
bool gonow = true;
while( true ) {
memset( buffer, '\0', 128 );
err = -1;
err = recv( datasock, buffer, 128, 0 );
if( err != -1 ) {
std::cout << "Recv: " << buffer << '\n';
std::cout << "Err = " << err << '\n';
}
Sleep(10);
}
}
I get this:
Recv: (string I typed)
Recv:
You get the string you typed plus whatever else was left over in the buffer from the previous time. If err is positive it is the number of bytes actually received. If it is zero it means the peer has disconnected and you should stop reading. Don't ignore these values.