2016-06-09 49 views
1

我想檢查Boost Asio是否確實執行異步連接。根據the diagrams corresponding to the asynchronous calls發佈在Asio的基礎知識,當io_service向操作系統發出信號時開始操作,因此我明白,在執行async.connect指令後,系統嘗試執行該連接。在GNU/Linux下,async_connect真的是異步的嗎?

我知道的是,如果你不打電話run你只是錯過了結果,但操作可能完成。所以我試着用nc -l -p 9000創建一個虛擬服務器,然後使用下面的代碼。

使用調試器,我已發表聲明並且在run調用io_service之前停止了。在服務器中,沒有任何反應。沒有關於連接 - 這是顯而易見的,因爲虛擬服務器不會告訴你關於它的信息 - 也不是async_write

在調用run函數後立即在服務器端彈出相應的消息。我一直在詢問Boost的IRC頻道,並在顯示我的strace後,一位真正聰明的人告訴我,這可能是因爲在調用run之前插座還沒有準備好。顯然,這不會發生在Windows上。

這是否意味着在GNU/Linux操作系統下異步並不是真正的異步?這是否意味着網站上顯示的圖表不符合GNU/Linux環境?

注:關於「是不是真的異步」:是的,它不會阻塞調用,因此該線程保持運行,做的事情,但我由開始的操作,他們已經被執行之後意味着異步

非常感謝您提前。

代碼

#include <iostream> 
#include <string.h> 
#include <boost/asio.hpp> 
#include <boost/bind.hpp> 

void connect_handler(const boost::system::error_code& error) 
{ 
    if(error) 
    { 
     std::cout << error.message() << std::endl; 
     exit(EXIT_FAILURE); 
    } 
    else 
    { 
     std::cout << "Successfully connected!" << std::endl; 
    } 
} 

void write_handler(const boost::system::error_code& error) 
{ 
    if(error) 
    { 
     std::cout << error.message() << std::endl; 
     exit(EXIT_FAILURE); 
    } 
    else 
    { 
     std::cout << "Yes, we wrote!" << std::endl; 
    } 
} 

int main() 
{ 
    boost::asio::io_service io_service; 
    boost::asio::ip::tcp::socket socket(io_service); 
    boost::asio::ip::tcp::endpoint endpoint(
     boost::asio::ip::address::from_string("127.0.0.1"), 9000); 

    socket.async_connect(endpoint, connect_handler); 

    std::string hello_world("Hello World!"); 
    boost::asio::async_write(socket, boost::asio::buffer(hello_world.c_str(), 
     hello_world.size()), boost::bind(write_handler, 
           boost::asio::placeholders::error)); 

    io_service.run(); 
    exit(EXIT_SUCCESS); 
} 

我strace的

futex(0x7f44cd0ca03c, FUTEX_WAKE_PRIVATE, 2147483647) = 0 
futex(0x7f44cd0ca048, FUTEX_WAKE_PRIVATE, 2147483647) = 0 
eventfd2(0, O_NONBLOCK|O_CLOEXEC)  = 3 
epoll_create1(EPOLL_CLOEXEC)   = 4 
timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC) = 5 
epoll_ctl(4, EPOLL_CTL_ADD, 3, {EPOLLIN|EPOLLERR|EPOLLET, {u32=37842600, u64=37842600}}) = 0 
write(3, "\1\0\0\0\0\0\0\0", 8)   = 8 
epoll_ctl(4, EPOLL_CTL_ADD, 5, {EPOLLIN|EPOLLERR, {u32=37842612, u64=37842612}}) = 0 
socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 6 
epoll_ctl(4, EPOLL_CTL_ADD, 6, {EPOLLIN|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET, {u32=37842704, u64=37842704}}) = 0 
ioctl(6, FIONBIO, [1])     = 0 
connect(6, {sa_family=AF_INET, sin_port=htons(9000), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now in progress) 
epoll_ctl(4, EPOLL_CTL_MOD, 6, {EPOLLIN|EPOLLPRI|EPOLLOUT|EPOLLERR|EPOLLHUP|EPOLLET, {u32=37842704, u64=37842704}}) = 0 
epoll_wait(4, {{EPOLLIN, {u32=37842600, u64=37842600}}, {EPOLLOUT, {u32=37842704, u64=37842704}}}, 128, -1) = 2 
poll([{fd=6, events=POLLOUT}], 1, 0) = 1 ([{fd=6, revents=POLLOUT}]) 
getsockopt(6, SOL_SOCKET, SO_ERROR, [0], [4]) = 0 
sendmsg(6, {msg_name(0)=NULL, msg_iov(1)=[{"Hello World!", 12}], msg_controllen=0, msg_flags=0}, MSG_NOSIGNAL) = 12 
fstat(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(136, 5), ...}) = 0 
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f44cd933000 
write(1, "Successfully connected!\n", 24Successfully connected! 
) = 24 
epoll_wait(4, {}, 128, 0)    = 0 
write(1, "Yes, we wrote!\n", 15Yes, we wrote! 
)  = 15 
exit_group(0)       = ? 
+++ exited with 0 +++ 
+0

「執行後立即啓動操作」不是「異步」的標準含義。您可能需要閱讀[boost.asio基本原理頁面](http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/overview/rationale.html),其中描述了該庫的用途。 – Sneftel

+0

謝謝@Sneftel,謝謝你的回答。我在該頁面閱讀,特別是_「Boost.Asio提供了管理這些長時間運行操作的工具,而無需程序使用基於線程和顯式鎖定的併發模型。」_部分。但是,如果我正確理解了鏈接狀態圖,那麼連接信號在執行connect語句後立即發送。這是最讓我困惑的部分。 – MikelAlejoBR

+0

啊,我看到了這個問題。我會更詳細地回答。 – Sneftel

回答

1

約Boost.Asio的一個奇怪的事情 - 不是唯一的,但通常來自特定OS異步網絡框架不同 - 它不依賴於輔助線程。這有一些重要的後果,可以歸結爲:boost.asio不是在後臺做事情。相反,它是用於做多個東西在前景

io_service::run()是boost.asio的「中心」,使用boost.asio的單線程程序應該將大部分時間花在io_service::run()之內,或者執行由它調用的完成處理程序。根據操作系統特定的內部實現,在該函數被調用之前,可能或可能不會運行特定的異步操作,這就是爲什麼調用它基本上是您啓動了最初的異步請求後首先要做的事情。

async_connect想象爲通過異步操作「佈防」您的io_service。實際的異步發生在io_service::run()期間。實際上,打電話async_connect緊接着是async_write是一件很奇怪的事情,我有點驚訝它的工作原理。通常,您會從connect_handler內執行async_write(或者相反,「胳膊」),因爲只有在那時您纔有連接的套接字。

+0

謝謝@Sneftel。是的,'async_write'真的很奇怪,但我故意這樣做,以便在調用'run'函數之前查看'connect'和'write'是否被「獨立」觸發。老實說,我也很驚訝它的工作。無論如何,謝謝你的解釋。 – MikelAlejoBR

+0

只要底層操作系統操作沒有阻塞,底層操作系統操作_may_就會在啓動函數('async_ *')內發生。如果底層操作會阻塞,它將被添加到反應堆中並通過運行'io_service'服務。在某些情況下,這種行爲允許'async_write'立即在'async_connect'之後成功。/cc @StrikeTeam –