简体   繁体   English

多线程 UDP 服务器中 io_context 或 std::cout 的奇怪行为

[英]Strange behaviour of either io_context or std::cout in multi-threaded UDP server

I have a simple UDP server created using boost\\asio which, for now, only prints the data received, as shown below.我有一个使用boost\\asio创建的简单 UDP 服务器,它现在只打印接收到的数据,如下所示。

using boost::asio::ip::udp;

enum { max_length = 2048 };

void server(boost::asio::io_context& io_context, unsigned short port)
{
    std::cout << "Server Created on port " + std::to_string(port) << " \n" << std::flush;
    udp::socket sock(io_context, udp::endpoint(udp::v4(), port));
    while(true)
    {
        udp::endpoint sender_endpoint;
        std::vector<char> v(max_length);
        sock.receive_from(boost::asio::buffer(v), sender_endpoint);
        for (int i = 0; i < v.size(); i++)
        {
            std::cout << v[i] <<std::flush;
        }
        std::cout << std::endl;
    }
}

I create 3 threads of the server function using boost::thread which I assign to a thread_group :我使用boost::thread创建了server函数的 3 个线程,并将其分配给thread_group

boost::asio::io_context io_context;

            boost::thread_group tg;

            boost::thread *t = new boost::thread(server, std::ref(io_context), 8889);
            boost::thread *t1 = new boost::thread(server, std::ref(io_context), 8890);
            boost::thread *t2 = new boost::thread(server, std::ref(io_context), 11111);

            tg.add_thread(t);
            tg.add_thread(t1);
            tg.add_thread(t2);

            tg.join_all();

To test the server I have used Packet Sender .为了测试服务器,我使用了Packet Sender The problem is that the output is... scrambled .问题是输出是... scrambled When sending packets on the 3 ports at (more or less) the same time once per second the output is slightly misplaced, but when increasing the packet frequency to once per 0.1 second, the output becomes unreadable, as shown in these two images .当在 3 个端口上每秒(或多或少)同时发送一次数据包时,输出稍微错位,但是当将数据包频率增加到每 0.1 秒一次时,输出变得不可读,如这两个图像所示。 I have tried giving one separate io_context object to every server, but the output remains the same at high frequency.我曾尝试为每台服务器提供一个单独的io_context对象,但输出频率高时保持不变。 Is there any way to avoid this?有什么办法可以避免这种情况吗?

The logical (and correct) solution is to use mutual exclusion on std::cout .合乎逻辑(且正确)的解决方案是在std::cout上使用互斥。 You know the appropriate scope of the lock (in your case, just a single UDP packet, but std::cout cannot guess that).您知道锁的适当范围(在您的情况下,只有一个 UDP 数据包,但std::cout无法猜测)。

The fancy solution is boost.asio.strand .奇特的解决方案是boost.asio.strand You don't need that for simple cases like this, but since you're trying to use boost.asio.io_context you should know that there is another class in boost.asio that could work like you intended.你并不需要一个简单的情况就是这样,但因为你试图使用boost.asio.io_context你应该知道,还有另一个类boost.asio像你预期可以工作。

std::cout automatically does some locking for you, a single print operation won't overlap with another print from a different thread. std::cout自动为您做一些锁定,单个打印操作不会与来自不同线程的另一个打印重叠。 However as you are printing one character at a time the output from each thread is likely to overlap.但是,当您一次打印一个字符时,每个线程的输出可能会重叠。 Flushing after every printed character is also likely to lead to poor performance.在每个打印字符后刷新也可能导致性能不佳。

If you build what you want to print into a single string it should print correctly:如果您将要打印的内容构建为单个字符串,它应该正确打印:

    std::vector<char> v(max_length);
    size_t bytes = sock.receive_from(boost::asio::buffer(v), sender_endpoint);
    std::string str(v.data(), bytes);
    str += '\n';
    std::cout << str;

Or you can skip the vector and save straight to a string:或者您可以跳过向量并直接保存到字符串:

    std:: string str(max_length);
    size_t bytes = sock.receive_from(boost::asio::buffer(str), sender_endpoint);
    str.resize(bytes)
    str += '\n';
    std::cout << str;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM