I am developing an application with C++ and having some difficulty with boost sockets. The server sends an image but not all the bytes are received by the client; the client always receives about 500 bytes less than the server sent. Provided below is the pertinent code and screenshots of the program running.
Server code:
int sent = boost::asio::write(*socket, response, boost::asio::transfer_all(), error);
std::cout << "Sent: " << sent << std ::endl;
Client code (I know that read_some will block if the total bytes sent by the server is divisible by 10000; this code is just for testing):
int len = 0;
int count = 0;
do {
len = socket->read_some( boost::asio::buffer( imageData, 10000 ) );
count += len;
std::cout << "len: " << len << std::endl;
std::cout << "count: " << count << std::endl;
} while(len == 10000);
std::cout << "Image Received of size: " << count << std::endl;
Screenshot of server:
Screenshot of client:
Thanks for your time; any help or suggestions would be greatly appreciated.
There're no guarantee you'll receive complete buffers of 10000 bytes.
I'd recommend following approach:
To send some binary data w/o any explicit terminator, first send its size and only then data itself. In this case client will know how many data in this chunk it should receive.
Related
I am having a hard time figuring out a bug in my TCP client-server app. The problem I am facing: in my recv function do-while loop, if the condition is bytes > 0, the function hangs forever. Replacing that with bytes == NMAX, everything works fine, UNLESS NMAX is equal to 1. A few side notes: doing a single send-recv works fine, but trying to do a send-recv and then recv-send hangs forever. NMAX is a constant set to 4096 by default. Server is ran first, then the client.
This is my send function:
ssize_t sendData(const std::string data, int fd)
{
ssize_t total = data.length(), bytes, sent = 0;
do
{
ssize_t chunk = total > NMAX ? NMAX : total;
bytes = send(fd, data.c_str() + sent, chunk, 0);
if (bytes == -1)
{
throw std::system_error(errno, std::generic_category(), "Error sending data");
}
total -= bytes;
sent += bytes;
} while (total > 0);
return sent;
}
This is my recv function:
std::string recvData(int fd)
{
ssize_t bytes;
std::string buffer;
do
{
std::vector<char> data(NMAX, 0);
bytes = recv(fd, &data[0], NMAX, 0);
if (bytes == -1)
{
throw std::system_error(errno, std::generic_category(), "Error receiving data");
}
buffer.append(data.cbegin(), data.cend());
} while (bytes > 0); // Replacing with bytes == NMAX partially fixes the issue, why?
return buffer;
}
This is the client's main function:
std::cout << "Sent " << sendData(data) << " bytes\n";
std::cout << "Received: " << recvData() << "\n";
And this is the server's main function:
std::cout << "Received: " << recvData(client) << "\n";
std::cout << "Sent " << sendData("Hello from the server side!", client) << " bytes\n";
The problem with your program is that the receiving side does not know how many bytes to receive in total. Therefore it will just endlessly try to read more bytes.
The reason why it "hangs" is that you perform a blocking system call (recv) which will only unblock if at least 1 more byte had been received. However since the peer does not send more data this will never happen.
To fix the issue you need to have a proper wire-format for your data which indicates how big the transmitted data is, or where it starts and ends. A common way to do this is to prefix data with it's length in binary form (e.g. a 32bit unsigned int in big endian format). Another way is to have indicators inside the data that indicate it's end (e.g. the \r\n\r\n line breaks in HTTP).
Btw: Your send function is not ideal for cases where data.length() == 0. In this case you perform a send system call with 0 bytes - which is rather unnecessary.
Update: I can run the server and client perfect locally when they are on the same host. The below scenario is when they are on separate machines(more specifically one is running on ARM64 Petalinux and other on Ubuntu). Perhaps I need to set some options on server side etc
I have a poco websocket server written over the standard sample provided by Poco and a javascript client. While trying to openwebsocket it will get connected in first go sometimes after trying 2-3 times and so on. Now once connected, it might send the message or just gets disconnected on the client side. While on the server side there is no error and I am pretty sure its still open because when i try to open another connection from the client side it will get connected on different client port.
Now the interesting thing is I wrote a Poco websocket client and sending/receiving message continuously without delay in a loop, the client remains active for sometime and then both server and client says Exception connection reset by peer. Next when i put a delay in the loop while sending/receiving messages on client side (say 5s), the client will send/receive for say 3-4 times and then the client encounters Exception connection reset by peer and now this time no such message on server side. Its really boggling me since I couldn't find the solution no matter what i try.
PS: I have timeout of 10 days for websocket server
The server side request handling is as follow:
std::cout << "Request Received" << std::endl;
Poco::Util::Application& app = Poco::Util::Application::instance();
try
{
std::cout << "Trying to connect..." << std::endl;
Poco::Net::WebSocket ws(request, response);
ws.setReceiveTimeout(Poco::Timespan(10, 0, 0, 0, 0)); //Timeout of 10 days
app.logger().information("WebSocket connection established!");
int flags, n;
do
{
char buffer[1024] = {'\0'};
std::cout << "Waiting for incoming frame" << std::endl;
n = ws.receiveFrame(buffer, sizeof(buffer), flags);
strncpy(buffer, "Hello Client!!", 1024);
ws.sendFrame(buffer, strlen(buffer), Poco::Net::WebSocket::FRAME_TEXT);
}
while (n > 0 && (flags & Poco::Net::WebSocket::FRAME_OP_BITMASK) != Poco::Net::WebSocket::FRAME_OP_CLOSE);
std::cout << "Websocket Closed" << std::endl;
app.logger().information("WebSocket connection closed.");
}
catch (std::exception& e)
{
std::cout << "Exception " << e.what() << std::endl;
}
(As one can see, server will post a message when a websocket is closed but nothing is displayed when the client says connection reset by peer)
The poco websocket client side is:
HTTPClientSession cs("IP", port);
HTTPRequest request(HTTPRequest::HTTP_GET, "/?encoding=text",HTTPMessage::HTTP_1_1);
request.set("origin", "http://localhost");
HTTPResponse response;
bool run = true;
try {
WebSocket* m_psock = new WebSocket(cs, request, response);
char buffer[1024] = {'\0'};
std::string str = "Hello Server!!";
do {
char receiveBuff[256];
strncpy(buffer, str.c_str(), 1024);
int len=m_psock->sendFrame(buffer,strlen(buffer),WebSocket::FRAME_TEXT);
std::cout << "Sent bytes " << len << std::endl;
std::cout << "Sent Message: " << buffer << std::endl;
int flags=0;
int rlen=m_psock->receiveFrame(receiveBuff,256,flags);
std::cout << "Received bytes " << rlen << std::endl;
std::cout << receiveBuff << std::endl;
//std::cin >> str;
sleep(5);
} while (1);
m_psock->close();
}
catch (std::exception &e)
{
std::cout << "Exception " << e.what();
}
FYI: I can't process the connection reset by peer, because it directly goes to exception without printing received bytes as 0
This question already has answers here:
boost asio async_write : how to not interleaving async_write calls?
(2 answers)
Closed 6 years ago.
I use boost::asio for asynchronous client and server.
In working process a client send to server data of different type: small service messages (5-50 B) and largest messages (40-200 KB) with raw image data.
When I call Client::send in order (in one thread, successively):
send "small service message";
send "large image message";
I get mixed data (wrong) on the server lake as:
|begin of large message||small message||end of large message|
void Client::send(MessageType type, const void* data, int size, bool read_header_after) {
assert(cstatus.is_connected());
header.type = type;
size_t buf_size = sizeof(ProtocolHeader) + size;
Bytes *p = new Bytes();
p->resize(buf_size);
std::memcpy(&p->front(), &header, sizeof(ProtocolHeader));
if (size) {
std::memcpy(&p->at(sizeof(ProtocolHeader)), data, size);
}
std::cout << "***** SEND start: " << p->size() << " bytes *****" << std::endl;
ba::async_write(*socket, ba::buffer(&p->front(), buf_size),
ba::transfer_exactly(buf_size),
[this, p, read_header_after](const boost::system::error_code& ec, std::size_t length) {
std::cout << "***** SEND complete: "
<< p->size() << " bytes; ec="
<< ec.value() << " (" << ec.message() << ") bufsize="
<< p->size()
<< " *****"
<< std::endl;
size_t buf_size = p->size();
delete p; // remove sent data
if (ec) {
cstatus.set_last_network_error("Client::send " + ec.message());
connection_failed("send - ec");
} else if (length < buf_size) {
connection_failed("send - len");
} else {
if (read_header_after) {
read_header();
}
start_check_timer(NORMAL_INTERVAL_DATA_SEND_MILLISEC);
}
});
}
Output show that small message send to async_write as second but executed (finished) as the first before large message.
***** SEND start: 53147 bytes *****
***** SEND start: 5 bytes *****
***** SEND complete: 5 bytes; ec=0 (Success) bufsize=5 *****
***** SEND complete: 53147 bytes; ec=0 (Success) bufsize=53147 *****
How it possible and how sync this? Thanks!
Update
I don't need queue of sync tasks. I need sync two async_write operations with different buffers sizes.
It is not a bug. The documentation (http://www.boost.org/doc/libs/1_62_0/doc/html/boost_asio/reference/async_write/overload1.html) explicitly sais that no other write operations should be executed on the same socket until there's another one in progress.
This is because an async_write is translated in several calls to async_write_some.
If you are on the same thread, my suggestion is to use a write queue in which you add the data to be send, to then extract it on the write callback to perform another operation.
Here are the simple echo server I'm working on, the server will accept the request from client and return what client sends to it. The program works fine with socat, but will freeze when using my own client.
The problem that my old code has is that I use read instead of read_some. read will block the pipe until it reads certain number of bytes or get a broken pipe exception, whereas read_some will read a chunk at a time. The updated version uses read_some to read input stream and check if the last character the program read is \0, if it is \0, that means it reaches the end of command, so it will echo back. This works because I only pass string literals and there is no binary data in the pipe.
The code of the server is
using namespace std;
const char* epStr = "/tmp/socketDemo";
int main() {
namespace local = boost::asio::local;
boost::asio::io_service io_service;
::unlink(epStr);
local::stream_protocol::endpoint ep(epStr);
local::stream_protocol::acceptor acceptor(io_service, ep);
while(1) {
local::stream_protocol::socket *socket = new local::stream_protocol::socket(io_service);
acceptor.accept(*socket);
char buf[2048] = {0};
boost::system::error_code error;
size_t len = 0;
while(1) {
len += socket->read_some(boost::asio::buffer(buf + len, 2048 - len));
cout << "read " << len << endl;
if (buf[len] == '\0') {
break;
}
}
cout << "read " << len << " bytes" << endl;
cout << buf << endl;
boost::asio::write(*socket, boost::asio::buffer(buf, len), boost::asio::transfer_all());
}
}
When testing the server with socat command, for example
echo "12345" | socat - UNIX-CONNECT:/tmp/socketDemo
it will return the desired result.
My client code is
const char* epStr = "/tmp/socketDemo";
int main(int argc, const char* argv[]) {
boost::asio::io_service io_service;
boost::asio::local::stream_protocol::endpoint ep(epStr);
boost::asio::local::stream_protocol::socket socket(io_service);
socket.connect(ep);
boost::asio::write(socket, boost::asio::buffer(argv[1], strlen(argv[1])), boost::asio::transfer_all());
char buf[1024] = {0};
size_t len = 0;
while(1) {
len += socket.read_some(boost::asio::buffer(buf + len, 2048 - len));
std::cout << "read " << len << std::endl;
if (buf[len] == '\0') {
break;
}
}
std::cout << "read " << len << " bytes\n";
std::cout << buf << std::endl;
socket.close();
When execute the client, at first both have no output, after I killed the client, the server will output that it reads n bytes and get a broken pipe exception.
Can this be caused by the read function in the server? If so is there a way to let it know how much data it should read without sending the size of data chunk at the beginning of each message? I am also wondering why socat can work with this server without any problem? Thanks!
I am also wondering why socat can work with this server without any
problem?
Probably because socat closes the socket and your client doesn't.
If so is there a way to let it know how much data it should read
without sending the size of data chunk at the beginning of each
message?
For instance, reading one byte at a time until you read an end-of-message character, assuming that you're defining / using a protocol that includes EOM.
I'm developing a client-server app, both sides of which use boost::asio.
I'm trying to send a large package of data over TCP (356 kb)
On server side, I write like:
boost::asio::async_write(Msocket,
boost::asio::buffer(sendBuffer,dataLen),
boost::bind(&ServerSession::onDataWrite,
this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_trasferred));
The onDataWrite is simple:
void ServerSession::onDataWrite(const boost::system::error_code& error, const std::size_t bytesSent) {
if (error) {
std::cout << "Error " << error << " while sending data" << std::endl;
}
}
On client side:
int readSize = ...; // defined from msg header, in this case equals 300 kbytes.
boost::asio::async_read(*Msocket,
boost::asio::buffer(recvBuffer, 50*1024*1024),
boost::asio::transfer_exactly(readSize),
boost::bind(&ClientSession::onDataRead,
this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_trasferred,
readSize));
And onDataRead is:
void ClientSession::onDataRead(const boost::system::error_code& error, const std::size_t bytesRecvd, const int readSize) {
if (error || bytesRecvd != readSize) {
std::cout << "Error " << error << " while getting data, expect " << readSize <<", got " << bytesRecvd << std::endl;
}
}
During write, server side prints
Error system:10014 while sending data
And client prints
Error system:0 while getting data, expect 393216, got 131064
While 131064 = 128kb - 8 bytes of header.
It looks like this 128-kb issue is caused by send/receive buffer overflow. But I though Boost will take care about those buffers itself, transparently for me.
What do I misunderstand?