IT戦記

プログラミング、起業などについて書いているプログラマーのブログです😚

C++ のお勉強

とりあえず daytime のクライアント

ほとんどこれの写経

#include <iostream>
#include <boost/asio.hpp>

int main () {

        // io_service を介して OS の IO を使う
        boost::asio::io_service io_service;

        // ソケットを作る
        boost::asio::ip::tcp::socket socket(io_service);

        // 名前解決をする
        // (ホスト名とサービス名から、 IP とポートを求める)
        std::cout << "resolving hostname ..." << std::endl;
        boost::asio::ip::tcp::resolver resolver(io_service);
        boost::asio::ip::tcp::resolver::query query("time.nist.gov", "daytime");
        boost::asio::ip::tcp::endpoint endpoint(*resolver.resolve(query));

        // つなぐ
        std::cout << "connecting to " << endpoint << "..." << std::endl;
        socket.connect(endpoint);

        // バッファ
        boost::array<char, 128> buf;

        while (true) {

                // エラーコード取得用変数
                boost::system::error_code error;

                // ソケットからの入力をバッファにコピー
                // (変数 buf の参照と変数 error の参照を渡す)
                std::size_t len = socket.read_some(boost::asio::buffer(buf), error);

                // ソケットからの入力が終わったら break;
                if (error == boost::asio::error::eof) {
                        break;
                }

                // 読み込んだデータを std::cout に出力
                std::cout.write(buf.data(), len);
        }

        // 正常終了
        return 0;
}

boost::asio::buffer が何故あるのか分からない。
mutable なバッファか const なバッファか型から判断して、 mutable_buffer か const_buffer を返すんだけど、 read_some の中でやっちゃだめなのかなあ。

次に read_some を非同期にしてみる

#include <iostream>
#include <boost/asio.hpp>

// バッファ
boost::array<char, 128> buf;

// 読み込んだときに呼ばれる関数
void read_some_handler(const boost::system::error_code& error, std::size_t len) {

        // 標準出力に出すよ
        std::cout.write(buf.data(), len);
}

int main () {

        // io_service を介して OS の IO を使う
        boost::asio::io_service io_service;

        // ソケットを作る
        boost::asio::ip::tcp::socket socket(io_service);

        // 名前解決をする
        // (ホスト名とサービス名から、 IP とポートを求める)
        std::cout << "resolving hostname ..." << std::endl;
        boost::asio::ip::tcp::resolver resolver(io_service);
        boost::asio::ip::tcp::resolver::query query("time.nist.gov", "daytime");
        boost::asio::ip::tcp::endpoint endpoint(*resolver.resolve(query));

        // つなぐ
        std::cout << "connecting to " << endpoint << "..." << std::endl;
        socket.connect(endpoint);

        // ソケットからの入力を非同期で
        // (変数 buf の参照と関数の参照を渡す)
        socket.async_read_some(boost::asio::buffer(buf), read_some_handler);

        // スレッドが終わるのを待つよ
        io_service.run();

        // 正常終了
        return 0;
}

buf をグローバル変数にした。

buf をローカル変数にして、 boost::bind を使ってみる

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

// 読み込んだときに呼ばれる関数
void read_some_handler(const boost::system::error_code& error, boost::array<char, 128> &buf, std::size_t len) {

        // 標準出力に出すよ
        std::cout.write(buf.data(), len);
}

int main () {

        // io_service を介して OS の IO を使う
        boost::asio::io_service io_service;

        // ソケットを作る
        boost::asio::ip::tcp::socket socket(io_service);

        // 名前解決をする
        // (ホスト名とサービス名から、 IP とポートを求める)
        std::cout << "resolving hostname ..." << std::endl;
        boost::asio::ip::tcp::resolver resolver(io_service);
        boost::asio::ip::tcp::resolver::query query("time.nist.gov", "daytime");
        boost::asio::ip::tcp::endpoint endpoint(*resolver.resolve(query));

        // つなぐ
        std::cout << "connecting to " << endpoint << "..." << std::endl;
        socket.connect(endpoint);

        // バッファ
        boost::array<char, 128> buf;

        // ソケットからの入力を非同期で
        // boost::bind を使って read_some_handler の第二引数を buf に束縛する
        socket.async_read_some(boost::asio::buffer(buf), boost::bind(read_some_handler, _1, buf, _2));

        // スレッドが終わるのを待つよ
        io_service.run();

        // 正常終了
        return 0;
}

でも、実行してみると

$ ./a.out 
resolving hostname ...
connecting to 192.43.244.18:13...
 3&#65533;&#65533;hq&#65533;&#65533;&#65533;ЊP&#65533;Њ0+`cϊ0+[

うまくいかない><
うーん
read_some_handler に渡す buf をポインタ渡しにしてみる

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

// 読み込んだときに呼ばれる関数
void read_some_handler(const boost::system::error_code& error, boost::array<char, 128> *buf, std::size_t len) {

        // 標準出力に出すよ
        std::cout.write(buf->data(), len);
}

int main () {

        // io_service を介して OS の IO を使う
        boost::asio::io_service io_service;

        // ソケットを作る
        boost::asio::ip::tcp::socket socket(io_service);

        // 名前解決をする
        // (ホスト名とサービス名から、 IP とポートを求める)
        std::cout << "resolving hostname ..." << std::endl;
        boost::asio::ip::tcp::resolver resolver(io_service);
        boost::asio::ip::tcp::resolver::query query("time.nist.gov", "daytime");
        boost::asio::ip::tcp::endpoint endpoint(*resolver.resolve(query));

        // つなぐ
        std::cout << "connecting to " << endpoint << "..." << std::endl;
        socket.connect(endpoint);

        // バッファ
        boost::array<char, 128> buf;

        // ソケットからの入力を非同期で
        // boost::bind を使って第二引数を buf に束縛する
        socket.async_read_some(boost::asio::buffer(buf), boost::bind(read_some_handler, _1, &buf, _2));

        // スレッドが終わるのを待つよ
        io_service.run();

        // 正常終了
        return 0;
}

実行

$ ./a.out 
resolving hostname ...
connecting to 192.43.244.18:13...

54730 08-09-21 09:44:59 50 0 0 111.4 UTC(NIST) *

おおお。うまくいった
何故だろうか
マニュアル読んだ
Boost: bind.hpp documentation - 1.36.0
boost::ref 使えって書いてあるお(^ω^;
boost::ref 使ってみた

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

// 読み込んだときに呼ばれる関数
void read_some_handler(const boost::system::error_code& error, boost::array<char, 128> buf, std::size_t len) {

        // 標準出力に出すよ
        std::cout.write(buf.data(), len);
}

int main () {

        // io_service を介して OS の IO を使う
        boost::asio::io_service io_service;

        // ソケットを作る
        boost::asio::ip::tcp::socket socket(io_service);

        // 名前解決をする
        // (ホスト名とサービス名から、 IP とポートを求める)
        std::cout << "resolving hostname ..." << std::endl;
        boost::asio::ip::tcp::resolver resolver(io_service);
        boost::asio::ip::tcp::resolver::query query("time.nist.gov", "daytime");
        boost::asio::ip::tcp::endpoint endpoint(*resolver.resolve(query));

        // つなぐ
        std::cout << "connecting to " << endpoint << "..." << std::endl;
        socket.connect(endpoint);

        // バッファ
        boost::array<char, 128> buf;

        // ソケットからの入力を非同期で
        // boost::bind を使って第二引数を buf に束縛する
        socket.async_read_some(boost::asio::buffer(buf), boost::bind(read_some_handler, _1, boost::ref(buf), _2));

        // スレッドが終わるのを待つよ
        io_service.run();

        // 正常終了
        return 0;
}

実行

./a.out 
resolving hostname ...
connecting to 192.43.244.18:13...

54730 08-09-21 09:44:59 50 0 0 111.4 UTC(NIST) * 

すげー。うごいたー

boost::bind を使って read_some_handler 自体を要らなくする。

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

int main () {

        // io_service を介して OS の IO を使う
        boost::asio::io_service io_service;

        // ソケットを作る
        boost::asio::ip::tcp::socket socket(io_service);

        // 名前解決をする
        // (ホスト名とサービス名から、 IP とポートを求める)
        std::cout << "resolving hostname ..." << std::endl;
        boost::asio::ip::tcp::resolver resolver(io_service);
        boost::asio::ip::tcp::resolver::query query("time.nist.gov", "daytime");
        boost::asio::ip::tcp::endpoint endpoint(*resolver.resolve(query));

        // つなぐ
        std::cout << "connecting to " << endpoint << "..." << std::endl;
        socket.connect(endpoint);

        // バッファ
        boost::array<char, 128> buf;

        // ソケットからの入力を非同期で
        socket.async_read_some(
                boost::asio::buffer(buf),
                boost::bind(
                        &std::basic_ostream<char, std::char_traits<char> >::write,
                        boost::ref(std::cout),
                        buf.data(),
                        _2
                )
        );

        // スレッドが終わるのを待つよ
        io_service.run();

        // 正常終了
        return 0;
}

実行

$ ./a.out
resolving hostname ...
connecting to 192.43.244.18:13...

54730 08-09-21 10:22:46 50 0 0 237.3 UTC(NIST) * 

まじかよー。
書いてある通りにやったら動いた。何でこんなことが可能なんだ。。。

[C++] 次は connect も非同期にしてみる

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

void accept_handler(const boost::system::error_code& error, boost::asio::ip::tcp::socket &socket, boost::array<char, 128> &buf) {

        // ソケットからの入力を非同期で
        socket.async_read_some(
                boost::asio::buffer(buf),
                boost::bind(
                        &std::basic_ostream<char, std::char_traits<char> >::write,
                        boost::ref(std::cout),
                        buf.data(),
                        _2
                )
        );

}

int main () {

        // バッファ
        boost::array<char, 128> buf;

        // io_service を介して OS の IO を使う
        boost::asio::io_service io_service;

        // ソケットを作る
        boost::asio::ip::tcp::socket socket(io_service);

        // 名前解決をする
        // (ホスト名とサービス名から、 IP とポートを求める)
        std::cout << "resolving hostname ..." << std::endl;
        boost::asio::ip::tcp::resolver resolver(io_service);
        boost::asio::ip::tcp::resolver::query query("time.nist.gov", "daytime");
        boost::asio::ip::tcp::endpoint endpoint(*resolver.resolve(query));

        // つなぐ
        std::cout << "connecting to " << endpoint << "..." << std::endl;
        socket.async_connect(
                endpoint,
                boost::bind(
                        accept_handler,
                        _1,
                        boost::ref(socket),
                        boost::ref(buf)
                )
        );

        // スレッドが終わるのを待つよ
        io_service.run();

        // 正常終了
        return 0;
}

でけたー

うーん

難しいなー