2014年3月31日月曜日

boostのsocketでUDP通信 超シンプルなサンプルコード

Socket通信を始めるとき、まず初めに試してみるとよいのがUDP方式による通信。 接続を確保して欠落なくデータを送るTPCと 一方的にデータを送りつけるだけのUDPを比べると、 UDPの方が単純なのでプログラムも分かりやすい。 C++ライブラリであるBoostを使えば簡単に実現できるが、Boost本家のサンプルコードはいまいち分かりにくかったので、 導入用のサンプロコードをここにメモしておく。

サンプルコード(Visual Studio2010):
https://github.com/r168xr169/BoostUdpSocket

送信側

送信側がやることは、ソケットをオープンしたら、ただただデータを送りつけるだけ。 データを送りつける先のIPアドレス(192.168.0.100など)、ポート番号とプロトコル(IPv4/IPv6)を指定する必要がある。 ここでは送信データとして「BoostSocketUDP 001\n」などの番号入りのテキストを送ることにする。 for文を無限ループにしているので、このテキスト内の番号がインクリメントされる仕組みである。 また、送信先のIPv4アドレスは192.168.0.100、ポート番号は1234とした。

#include <string>
#include <iostream>
#include <boost/asio.hpp>
#include <boost/format.hpp>
#include <boost/thread.hpp>

int main(int argc, char * argv[])
{
    using namespace boost::asio::ip;

    //ソケットオープン
    boost::asio::io_service io_service;
    udp::socket sock(io_service, udp::endpoint(udp::v4(), 1234));
    
    //繰り返し送信する 
    for(int i=0; true; i++)
    {
         //テキストの送信(相手の状態に関わらず送信し終えたらすぐに次の処理に移る)
         std::string str = (boost::format("BoostSocketUDP %03d\n") % i).str();
         sock.send_to
         (
            boost::asio::buffer(str),
            udp::endpoint(address::from_string("192.168.0.100"), 1234)
         );

        //1秒待機(送信しすぎないように)
        std::cout << str;
        boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
    }

    return 0;
}

受信側

受信側でもやはりソケットをオープンした後、ポートとプロトコルを指定すれば、 あとはreceive_from()関数で受信すれば良い。 こちらもIPv4、ポート番号1234とした。

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

int main(int argc, char * argv[])
{     
    using namespace boost::asio::ip;

    //ソケットオープン
    boost::asio::io_service io_service;
    udp::socket sock(io_service, udp::endpoint(udp::v4(), 1234));

    //繰り返しテキストを受信する
    for(int i=0; true; i++)
    {
        //受信(受信できるまで待機される)
        boost::array<char, 128> recv_buf;
        udp::endpoint endpoint;
        size_t len = sock.receive_from(boost::asio::buffer(recv_buf), endpoint);

        //受信内容を表示
        std::cout.write(recv_buf.data(), len);
    }

    return 0;
}

上手くいかないとき・・・

  • OSやウィルスソフトのファイヤーウォールでブロックされていないか確認する。二重にブロックされていることもある。
  • 同じプライベートIPアドレス(192.168.*.*)に見えても異なる無線LANのアクセスポイントに接続されている場合は、セグメントが異なるのでプライベートIPアドレスどうしでは通信できない。
  • 同じPC上で上記の2つのプログラムを動作させることはできない。
  • WindowsならMicrosoft Network Monitorで通信内容をキャプチャできるので、デバッグに使える。

参考