I'm relatively new to network programming and have some questions on best practices for continuous fast data (image) transfer between a client and a server using Boost Asio. Important point, we cannot apply compression that lowers image quality. We use a dedicated network (54MBit) with no traffic other than ours. We were recommended to use Boost Asio, since it seems to be suited for our needs. However, as Boost is powerful it is challenging for inexperienced (Boost) developers like me.
We want to develop a tool, as simple as possible, that sends continuously image data as fast as possible between a client and a server. Basically it is streaming. We would prefer to use TCP, but if we could have significant performance gain with UDP, we would not mind losing data packets once in a while.
The data is an unsigned char buffer (640x480px = 307200 byte = 2.34MBit, monochrome).
I started with the Asio tutorials and played around with sync, async, UDP, TCP projects. For now I'm able to send data with ~10fps, ~0.1ms per image with TCP and ~13fps with UDP. This is far too slow. I expected sending 2.4MBit in a 54MBit network to be way faster.
Today, I do not use serialization, archive and compression (zip) etc of my data. I think this will improve the transfer, but I'm wondering if I have to settle my expectations and/or if I have to change my approach entirely?
Is serialization of data the way to go for data streaming with Asio? Will zip compression improve the transfer probably significantly? Are there alternative approaches or frameworks?
TCP Server code sample
// sends data whenever it receives a request by the client
int main(int argc, char* argv[])
{
init_image_buffer();
try
{
boost::asio::io_service io_service;
tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 13));
for (;;)
{
tcp::socket socket(io_service);
acceptor.accept(socket);
boost::system::error_code ignored_error;
boost::asio::write(socket, boost::asio::buffer(image_buffer),
boost::asio::transfer_all(), ignored_error);
}
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
TCP Client code sample
I realize that this code is not optimal. But I couldn't figure out how to remain connected and requesting new data with this kind of approach.
int main(int argc, char* argv[])
{
Clock clock;
clock.Initialise();
float avg = 0.0;
float min = 1000000.0;
float max = 0.0;
float time = 0.0;
// sending multiple images
for(int j=0;j<IMAGE_COUNT;j++){
try
{
clock.GetTime();
if (argc != 2)
{
std::cerr << "Usage: client <host>" << std::endl;
return 1;
}
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
tcp::resolver::query query(argv[1], 13);
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::resolver::iterator end;
tcp::socket socket(io_service);
boost::system::error_code error = boost::asio::error::host_not_found;
while (error && endpoint_iterator != end)
{
socket.close();
socket.connect(*endpoint_iterator++, error);
}
if (error)
throw boost::system::system_error(error);
// we read all received data but do NOT save them into a dedicated image buffer
for (;;)
{
boost::array<unsigned char, 65536> temp_buffer;
boost::system::error_code error;
size_t len = socket.read_some(boost::asio::buffer(temp_buffer), error);
if (error == boost::asio::error::eof)
break; // Connection closed cleanly by peer.
else if (error)
throw boost::system::system_error(error); // Some other error.
}
time = clock.GetTime();
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
// calculate some time transfer statistics
avg+=time;
if(time < min) min = time;
if(time > max) max = time;
}
std::cout << "avg: " << avg/(float)IMAGE_COUNT << " freq: " << 1.0/(avg/(float)IMAGE_COUNT) << std::endl;
std::cout << "min: " << min << " max: " << max << std::endl;
return 0;
}