使用Boost.ASIO

时间:2016-05-21 21:23:43

标签: c++ boost network-programming boost-asio

我在大约10年前使用过ASIO(我记得当时还有另一个boost.netwrok lib很短的时间),从那以后我一直在使用我自己的网络代码或其他一些实现或语言(例如ACE,nodejs等)。现在我想用ASIO编写简单的代码,但是我没有足够的词来描述我讨厌它的程度。

首先,它是我之后的简单同步代码,它只有几行BSD / winsock代码,并且使用asio完全混乱。不仅所有代码看起来都像是掩盖了真实逻辑的一堆杂乱的东西,它还迫使我使用一些我真正想要避免的东西,如瘟疫。

所以,首先,我描述了我需要做的事情: 1)在mymagichost.com上连接到服务器:1234在ASIO中这部分虽然看起来像是满口的命名空间谈话或多或少好了:

#include <boost/asio.hpp>
// I'm scared what the code would look like without this one.
using boost::asio::ip::tcp; 
int main()
{
    boost::asio::io_service io_service;
    tcp::socket sock(io_service);
    tcp::resolver resolver(io_service);
    boost::asio::connect(sock, resolver.resolve(
        tcp::resolver::query(tcp::v4(), "mymagichost.com", "1234")));
}

我根本不知道我怎么可能不能将1234作为int传递,这是否意味着在这里传递一个字符串会将整个巨大的服务名称列入我的二进制文件中以便能够计算出来我需要1234端口?! WTF?

连接后,我需要与服务器进行简单的对话:

// when I start talking I say PING\n to the server
"PING\n"

// it replies back with 64-bit unsigned int id
"PONG <connection id>\n"

// I ask server to execute command, where <cmd id>
// is an int and connection id is that number that
// server sent to me in PONG.
"COMMAND <cmd id>:<connection id>\n"

// server executes my command
"OK <message id>\n<binary data until remote closes socket>"

使用阻止bsd袜子,它就像10-20行代码:

void talk(int sock)  // consider it pseudo-code
{
    char buf[1024];
    int cmd_id = 1234;
    send(sock, "PING\n", 5, 0);
    unsigned long long connection_id, msg_id;
    recv(sock, buf, sizeof(buffer), 0);
    sscanf(buf, "PONG %llu\n", &connection_id);
    int n = sprintf(buf, "COMMAND %u:llu\n", cmd_id, connection_id);
    send(sock, buf, n, 0);
    vector<char> data;
    for (; (n = recv(sock, buf, sizeof(buf), 0)) > 0;)
       data.insert(data.end(), buf, buf+n);
    sscanf(buf, "OK %llu\n%n", &msg_id, &n);
    char *binary_data = buf.data()+n;
    int binary_data_size = data.size()-n;
}

那么,如何在同步asio中完成此操作? 在我的BSD代码中,我有很多缺点:在收到回复时,我可能会收到部分回复,例如我需要循环接收,直到落后&#39; \ n&#39;收到(也许这不会发生就是这个简单的例子)。 在许多接收数据的功能中,有一个功能正是如此:

boost::asio::read_until(sock, buf, '\n');

然而,与boost :: asio :: read不同,read_until只接受streambuf(我真的想避免像瘟疫一样)。我只是不知道,为什么它不允许我使用我自己的固定缓冲区,是因为asio dev决定阻止我在我脚下射击?我仍然可以deference null指针是我想要的。无论如何,不​​是什么大不了的,我可以使用streambuf,但这里的事情变得非常难看。 根据文档,在我读完之后,最后&#39; \ n&#39;在服务器OK回复缓冲区中实际可能包含更多数据here's what the docs say:&#34;成功读取read_until操作后,streambuf可能包含除分隔符之外的其他数据。应用程序通常会将该数据保留在streambuf中,以供后续的read_until操作进行检查。 &#34 ;. 现在处理完服务器上的最后一个OK回复之后,我真的别无选择,只能继续使用可怕的streambuf来读入所有剩余的数据(megs),即使我喜欢循环并追加到我的最终缓冲区(字符串或向量)。当我读完数据的所有方法从流buf中获取它时我非常糟糕:多次复制数据或使用istream_iterator / istreambuf_iterator将其附加到我的容器中。在我看来,这两个都非常慢,我的视觉工作室几乎停顿了几秒钟(顺便说一句,我没有收到数据)。

那么,用asio处理它的正确方法是什么?

1 个答案:

答案 0 :(得分:1)

首先,地址不能被认为是数字的,因为您要求它被解析。没有任何迹象表明如果解析器看到端口是数字AFAICT,则无法避免加载服务定义。

其次,ASIO中有一些高级原语。你可以用它:

import Foundation

let a = [1, 2, 3]
var aCopy = a
aCopy[0] = aCopy[0] // <- just to trigger the copying
let aCopyPointer = UnsafeMutablePointer<Int>(aCopy)

let b = [4, 5, 6]
let bPointer = UnsafePointer<Int>(b)

let bData = NSData(bytes: bPointer, length: sizeof(Int) * b.count)
bData.getBytes(aCopyPointer, length: sizeof(Int) * b.count)

print(a) // [1, 2, 3]

查看 Live On Coliru

如果你想没有精神,你可能会使用void talk(std::iostream& sock) // consider it pseudo-code { int cmd_id = 1234; sock << "PING" << std::endl; uint64_t connection_id; if (sock >> connection_id) { sock << "COMMAND " << cmd_id << ":" << connection_id << std::endl; char buf[1024]; std::vector<char> data; // read till eof while (sock.read(buf, sizeof(buf)) data.insert(data.end(), buf, buf+sizeof(buf)); data.insert(data.end(), buf, buf+sock.gcount()); } uint64_t msg_id; int n; if (sock >> qi::phrase_match("OK" >> qi::auto_ >> '\n' >> qi::auto_, qi::blank, msg_id, n)) { // do something with n and msg_id } } int main() { boost::asio::ip::tcp::iostream s { "127.0.0.1", "1234" }; talk(s); } 和类似的sscanf装置来解决问题。

相关问题