Pocoライブラリを用いたC++ UDPネットワークプログラミング

Pocoライブラリは、ネイティブソケットの上位レイヤーに位置するC++用ネットワークフレームワークです。生のソケットと比較して性能面ではやや劣るものの、安全性と実用性に優れ、簡潔なAPIを提供します。UDPプロトコルでは、受信側がデータを適時に取得できない場合、そのデータは失われます。

環境セットアップ

sudo apt-get install libpoco-dev

ポート514に関する注意点

多くのサンプルコード(公式資料を含む)でポート514が使われていますが、このポートで受信を行うと I/O error が発生する可能性があります。これは、ポート514がsyslogサービス用に予約されており、バインドにはスーパーユーザー権限が必要だからです。送信自体は制限なく行えます。回避策として、ポート番号を変更するか、管理者権限で実行してください。

UDP送信サンプル

#include "Poco/Net/DatagramSocket.h"
#include "Poco/Net/SocketAddress.h"
#include "Poco/Timestamp.h"
#include "Poco/DateTimeFormatter.h"

int main(int argc, char **argv)
{
    Poco::Net::SocketAddress addr("localhost", 1101);
    Poco::Net::DatagramSocket sock;
    sock.connect(addr);
    
    Poco::Timestamp ts;
    std::string payload = Poco::DateTimeFormatter::format(ts,
        "<14>%w %f %H:%M:%S Hello, world!");
    sock.sendBytes(payload.data(), payload.size());
    return 0;
}

UDP受信サンプル

#include "Poco/Net/DatagramSocket.h"
#include "Poco/Net/SocketAddress.h"
#include <iostream>

int main(int argc, char **argv)
{
    Poco::Net::SocketAddress bindAddr("localhost", 1101);
    Poco::Net::DatagramSocket rxSocket(bindAddr);
    
    char buf[1024];
    for (;;)
    {
        Poco::Net::SocketAddress srcAddr;
        int recvLen = rxSocket.receiveFrom(buf, sizeof(buf) - 1, srcAddr);
        buf[recvLen] = '\0';
        std::cout << srcAddr.toString() << ": " << buf << std::endl;
    }
    return 0;
}

コンパイルコマンド

g++ sender.cpp -lPocoNet -lPocoFoundation -o sender
g++ receiver.cpp -lPocoNet -lPocoFoundation -o receiver

マルチキャスト送信例

#include "Poco/Net/SocketAddress.h"
#include "Poco/Net/MulticastSocket.h"
#include "Poco/DateTimeFormatter.h"
#include <iostream>
#include <cstring>

int main(int argc, char *argv[])
{
    try
    {
        Poco::Net::SocketAddress mcastAddr("239.255.255.250", 1902);
        Poco::Net::MulticastSocket mcastSock(
            Poco::Net::SocketAddress(
                Poco::Net::IPAddress(), mcastAddr.port()));
        mcastSock.joinGroup(mcastAddr.host());
        
        Poco::Net::SocketAddress senderAddr;
        char buffer[512] = "";
        
        Poco::Timestamp now;
        std::string msg = Poco::DateTimeFormatter::format(now,
            "%Y-%m-%d %H:%M:%S.%i send: Hello, world!");
        mcastSock.sendTo(msg.data(), msg.size(), mcastAddr);
        
        int recvLen = mcastSock.receiveFrom(buffer, sizeof(buffer), senderAddr);
        std::cout << "Received: " << buffer << " from " << senderAddr.toString() << std::endl;
    }
    catch (Poco::Exception &e)
    {
        std::cerr << "Exception: " << e.displayText() << std::endl;
        return 1;
    }
    return 0;
}

マルチキャストの注意点

  • 「239.255.255.250」とポート1902の組み合わせはユニークな識別子として機能し、パケットキャプチャでフィルタリング可能
  • 使用するポートに不要なトラフィックがないか事前確認が必要
  • 同一ネットワークセグメント内かつ同一のグループアドレスを使用しているノード間でのみ通信可能
  • sender.toString() で送信元IPアドレスが取得可能

マルチキャスト受信例

#include <Poco/Net/DatagramSocket.h>
#include <Poco/Net/SocketAddress.h>
#include <Poco/Net/MulticastSocket.h>
#include <Poco/Net/NetworkInterface.h>
#include <iostream>
#include <vector>

int main() {
    try {
        Poco::Net::SocketAddress groupAddr("239.1.1.5", 9200);
        Poco::Net::MulticastSocket rxSocket(Poco::Net::IPAddress::IPv4);
        rxSocket.bind(Poco::Net::SocketAddress(Poco::Net::IPAddress(), groupAddr.port()), true);
        
        Poco::Net::NetworkInterface netIf = Poco::Net::NetworkInterface::forName("eth0.10");
        rxSocket.joinGroup(groupAddr.host(), netIf);
        
        std::vector<char> dataBuf(1024);
        std::cout << "Waiting for multicast on " << groupAddr.toString() << " ..." << std::endl;
        
        while (true) {
            Poco::Net::SocketAddress remoteAddr;
            int bytesRead = rxSocket.receiveFrom(&dataBuf[0], dataBuf.size(), remoteAddr);
            if (bytesRead > 0) {
                std::string message(dataBuf.begin(), dataBuf.begin() + bytesRead);
                std::cout << "From " << remoteAddr.toString() << ": " << message << std::endl;
            }
        }
        rxSocket.leaveGroup(groupAddr.host(), netIf);
    } catch (Poco::Exception& ex) {
        std::cerr << "Poco Error: " << ex.displayText() << std::endl;
    } catch (std::exception& ex) {
        std::cerr << "Standard Error: " << ex.what() << std::endl;
    } catch (...) {
        std::cerr << "Unknown error occurred!" << std::endl;
    }
    return 0;
}

タイムスタンプユーティリティ

Poco::Timestamp currentTime;
std::string timeStr = Poco::DateTimeFormatter::format(currentTime, "%Y-%m-%d %H:%M:%S.%i ");
Poco::Int64 msSinceEpoch = currentTime.epochMicroseconds() / 1000;

// 数値を固定長文字列に変換
std::string numStr = std::to_string(msSinceEpoch);
std::stringstream ss;
ss << std::setw(13) << std::setfill('0') << numStr;

UDPパケットの優先度設定

Poco::Net::SocketAddress mcastAddr(MULTICAST_IP, MULTICAST_PORT);
Poco::Net::MulticastSocket prioritySocket(
    Poco::Net::SocketAddress(LOCAL_IP, mcastAddr.port()));
int prioLevel = 4;
prioritySocket.setOption(SOL_SOCKET, SO_PRIORITY, prioLevel);

タグ: Poco C++ udp マルチキャスト ネットワークプログラミング

6月28日 18:23 投稿