开发者

Fast data (image) transfer server client using Boost Asio

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 app开发者_开发问答roaches 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;
 }


It seems you are comparing frame size (2.34MBit) with the network speed (54MBit/sec). If that's the case, and you are getting 10 fps, your actual rate is 23.4 MBit/sec –– which is not that bad on a 54 MBit/sec connection.

Anyway, please inform us what design you ended up with.

Thanks.


If bandwidth is a concern and you have enough CPU, look at x264, it has a lossless scheme which compresses quite well and fast. If CPU is more limited, take a look at libz or even a simple PNG library. Imho with good high level libraries these are not so difficult to implement, often it's just a few function calls and you have your data.

As for data reliability. TCP is the easy choice but is often not encouraged for real time streaming because of unpredictable latencies, jitter and the fast-restart, certainly avoidable when a wireless link is involved (which the 54mbps actually reminds me of). SCTP can be an better suited alternative, but there are many more (e.g. MPEG-TS, RSTP, ...). Finally, you always could built your own protocol for retransmission of missed/erroneous frames based on UDP, but that is not a trivial task.

Edit: you could always take a look at VNC/NX implementations, these could help you along also if the images are not fast-changing.


Not a real concrete answer, but some tips:

Use TCP. For data transfer it is much faster because all the checking and resending of packages is built-in and super-fast. Heck, it was invented for data transfer. Only use UDP if you need to send a lot of single packages real-time like in a FPS game.

Use a fast lossless compression algorithm, something like FastLZ (I just found it using Google, I have no experience with it). It won't work as good a pure lossy image compression algorithm that has certain "advantages" by knowing that it's working with images, but it will work with any kind of data and compress it.


Depending of the data you are transferring, you might consider sending only the changes between an image and the next one. This might be useful for a remote desktop, but useless for video. You might help by telling a bit more accurately what kind of data you are sending.


  • Actually it seems like you have a wireless connection with capability of 54Mbps. If it is, you have another problem which is that this connection is Half-Duplex. So, in that case, consider your network's bandwidth as 27Mbps.
  • Also, in wireless networks, the latency is increased which reduce TCP performance.
  • You can try OpenCV libraries for fast image compression. They both provide lossy and lossless compression option (JPEG, PNG, etc..)
  • You can try to send only the changes, if your streaming image is not changing too fast. You can use VLC libraries and OpenCV libraries to find this change data.
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜