多线程中boost asio中的随机EOF

Posted

技术标签:

【中文标题】多线程中boost asio中的随机EOF【英文标题】:Random EOF in boost asio in multi thread 【发布时间】:2013-09-06 09:46:24 【问题描述】:

我是提升 asio 的新手,我在多线程服务器中遇到随机文件结束

我可以在这个小例子中重现我的问题:

服务器:

这是一个简单的回显服务器。协议很简单:

(1) 客户端连接 (2) 服务器读取一个字节。该字节是要读取和发回的字符串的长度。 (3) 服务器读取 N 个字节。 (4) 服务端发回 N+1 个字节给客户端,然后返回 (2)。

当客户端断开连接时(3)中捕获到一个EOF并且处理程序循环停止。

class MySocket
public:
    char buffer[257];
    boost::asio::ip::tcp::socket socket;
    MySocket(boost::asio::io_service*ios):socket(*ios)
    ~MySocket()
;

//Handlers

void readN(std::shared_ptr<MySocket>server,const boost::system::error_code&ec);

//(4)
void echo(std::shared_ptr<MySocket>server,const boost::system::error_code&ec)
    if(ec)
        throw std::exception(("This is NOT OK: "+ec.message()).c_str());
    size_t n=server->buffer[0]&0xFF;
    std::cout<<std::string(server->buffer+1,n)<<std::endl;
    boost::asio::async_write(server->socket,boost::asio::buffer(server->buffer,n+1),boost::bind(readN,server,boost::asio::placeholders::error));

//(3)
void read(std::shared_ptr<MySocket>server,const boost::system::error_code&ec)
    if(ec)
        throw std::exception(("This is OK: "+ec.message()).c_str());
    size_t n=server->buffer[0]&0xFF;
    boost::asio::async_read(server->socket,boost::asio::buffer(server->buffer+1,n),boost::bind(echo,server,boost::asio::placeholders::error));

//(2)
void readN(std::shared_ptr<MySocket>server,const boost::system::error_code&ec)
    if(ec)
        throw std::exception(("This is also NOT OK: "+ec.message()).c_str());
    boost::asio::async_read(server->socket,boost::asio::buffer(server->buffer+0,1),boost::bind(read,server,boost::asio::placeholders::error));

//Server

void serve(boost::asio::io_service*ios)
    for(;;)
        tryios->run();break;
        catch(const std::exception&e)std::cout<<e.what()<<std::endl;

//(1)
void accept(boost::asio::io_service*ios,boost::asio::ip::tcp::acceptor*acceptor,std::shared_ptr<MySocket>server,const boost::system::error_code&ec)
    if(server.get()!=nullptr)
        server->socket.set_option(boost::asio::ip::tcp::no_delay(true));
        readN(server,ec);
    server.reset(new MySocket(ios));
    acceptor->async_accept(server->socket,boost::bind(accept,ios,acceptor,server,boost::asio::placeholders::error));

int main()
    boost::asio::io_service ios;
    boost::asio::ip::tcp::acceptor acceptor(ios,boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(),1207));
    boost::asio::io_service::work work(ios);
    accept(&ios,&acceptor,nullptr,boost::system::error_code());
//    std::thread other(boost::bind(serve,&ios));
    serve(&ios);
    acceptor.close();
    ios.stop();
//    other.join();
    return 0;

客户:

客户端连接到服务器一次并发送 1000 个字符串。

int main()
    try
        boost::asio::io_service ios;
        boost::asio::ip::tcp::socket socket(ios);
        boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string("127.0.0.1"),1207);
        socket.connect(endpoint);
        socket.set_option(boost::asio::ip::tcp::no_delay(true));
        char buf[257];
        for(size_t i=0;i<1000;++i)
            size_t n=(i%127)+1;
            buf[0]=(char)n;
            for(size_t j=0;j<n;++j)
                buf[j+1]=(char)('A'+(j+i)%26);
            socket.send(boost::asio::buffer(buf,n+1));
            socket.receive(boost::asio::buffer(buf,1));
            if((buf[0]&0xFF)!=n)
                throw std::exception("Oups!");
            socket.receive(boost::asio::buffer(buf+1,n));
            for(size_t j=0;j<n;++j)
                if(buf[j+1]!=(char)('A'+(j+i)%26))
                    throw std::exception("Oups!");
            std::cout<<i<<": "<<std::string(buf+1,n)<<std::endl;
    catch(const std::exception&e)
        std::cout<<e.what()<<std::endl;
    return 0;

当服务器仅使用一个线程(其他线程已注释)时,服务器会正​​确回显 1000 个字符串。

当服务器使用另一个线程时,在(4)中在随机打印字符串之后捕获一个EOF。这绝不应该发生。

我尝试用一​​个 strand 封装所有异步调用,但没有奏效。 据我所知,不存在数据竞争问题。处理程序应该一个接一个地调用。

我错过了什么?

处理多线程 asio 应用程序的正确习惯用法是什么?

编辑:

我做了一些测试,看来如果我替换这一行

throw std::exception(("This is NOT OK: "+ec.message()).c_str());

与:

std::cout<<"This is not OK: "<<ec.message()<<std::endl;

即使我看到一些 EOF 被错误地作为参数传递了几次,服务器也会正确地回显 1000 行。

所以我想问题是当套接字显然没有关闭时,为什么我会得到一个不正确的 boost::asio::error::eof ?

这不是here 所说的。

【问题讨论】:

我无法用您发布的代码重现您的问题 - 它似乎在这里运行良好。如果您发现此代码存在问题,您能否详细说明如何重现它? 我有 Windows 7 64 位、Visual Studio 2012 和 boost 1.54 你还需要什么? 我没有可用的 Windows 系统 - 可能是时候拿出调试器了 :) 祝你好运! 感谢您对桑德的帮助。 【参考方案1】:

这是 boost::asio 1.54.0 的一个错误

我在网上找到了两个类似的帖子:

this one。 和that one(堆栈溢出)。

还有一个错误报告here。

我安装了 boost 1.53,现在运行良好。

【讨论】:

【参考方案2】:

此问题是回归问题,已在 Boost 1.55 中修复: http://www.boost.org/users/history/version_1_55_0.html

【讨论】:

以上是关于多线程中boost asio中的随机EOF的主要内容,如果未能解决你的问题,请参考以下文章

多线程和Boost::Asio

Boost.Asio、tcp::iostream 和多线程

C++ 提升 asio 多线程

Boost.Asio 异步 TCP 客户端和多线程

boost asio async_read中的随机EOF

多线程应用程序上的 boost::asio::ssl 访问冲突