An Embedded Engineer’s Blog

とある組み込みエンジニアの備忘録的なブログです。

C++でソケット通信 (UDPマルチキャスト/ブロードキャスト編)

まえがき

ソケットを使ったEthernet通信の実装方法メモです。
今回は、前回に引き続きC++UDPマルチキャスト/ブロードキャスト通信を行うための機能を実装します。

ソケット通信は、OSによって実装方法が異なるので、Windows / Mac / Linuxそれぞれの実装を行い、環境によって自動的に切り替わるようにしています。

前提条件

今回実装したソースは以下の環境でビルド、動作確認しています。

OS Ver Compiler Remarks
Windows 10 Visual Studio 2019
macOS 10.15.6 Clang 11.0
Ubuntu 18.04 GCC 7.5.0


また、プロジェクトソース一式は GitHub に置いてあります。

インタフェース

前回作成したSocketAdapterクラスに、マルチキャスト / ブロードキャスト通信用インタフェースを追加します。

コンセプト

SocketAdapterクラスに追加するインタフェース一覧を以下の表に示します。

Interface Type Detail Remarks
OpenUdpMultiTxSocket Instance UDマルチキャスト送信用ソケットオープン
OpenUdpMultiRxSocket Instance UDPマルチキャスト受信用ソケットオープン
OpenUdpBroadTxSocket Instance UDPブロードキャスト送信用ソケットオープン
OpenUdpBroadRxSocket Instance UDPブロードキャスト受信用ソケットオープン


ソケット通信では、ソケットオープン時のオプションと送信先IPアドレスによって、ユニキャスト / マルチキャスト / ブロードキャストを切り替えることが出来ます。
そのため、マルチキャスト、ブロードキャストそれぞれ用のソケットオープン用メソッドを追加します。

OpenUdpMultiTxSocket / OpenUdpMultiRxSocketメソッドはそれぞれマルチキャスト送受信用のソケットオープン用メソッドです。
OpenUdpBroadTxSocket / OpenUdpBroadRxSocketメソッドはそれぞれブロードキャスト送受信用のソケットオープン用メソッドです。


実装

ソケットアダプタ

SocketAdapterクラスに、マルチキャスト / ブロードキャスト通信用インタフェースを追加します。
以下に、前回からの差分コードを示します。

【SocketAdapter.h】

/* Socket Adapterクラス宣言 */
class SocketAdapter final
{
public:
    /* 〜省略〜 */

    /* UDPマルチキャスト送信用ソケットオープン */
    void OpenUdpMultiTxSocket(const std::string& multicast_ip, const std::string& local_ip, const uint16_t multicast_port, const int32_t ttl);
    /* UDPマルチキャスト受信用ソケットオープン */
    void OpenUdpMultiRxSocket(const std::string& multicast_ip, const uint16_t multicast_port);
    /* UDPブロードキャスト送信用ソケットオープン */
    void OpenUdpBroadTxSocket(const std::string& remote_ip, const uint16_t remote_port);
    /* UDPブロードキャスト受信用ソケットオープン */
    void OpenUdpBroadRxSocket(const uint16_t local_port);

    /* 〜省略〜 */
};


【SocketAdapter.cpp】

/* 〜省略〜 */

/* UDPマルチキャスト送信用ソケットオープン */
void SocketAdapter::OpenUdpMultiTxSocket(const std::string& multicast_ip, const std::string& local_ip, const uint16_t multicast_port, const int32_t ttl)
{
    this->m_Impl->OpenUdpMultiTxSocket(multicast_ip, local_ip, multicast_port, ttl);
}

/* UDPマルチキャスト受信用ソケットオープン */
void SocketAdapter::OpenUdpMultiRxSocket(const std::string& multicast_ip, const uint16_t multicast_port)
{
    this->m_Impl->OpenUdpMultiRxSocket(multicast_ip, multicast_port);
}

/* UDPブロードキャスト送信用ソケットオープン */
void SocketAdapter::OpenUdpBroadTxSocket(const std::string& remote_ip, const uint16_t remote_port)
{
    this->m_Impl->OpenUdpBroadTxSocket(remote_ip, remote_port);
}

/* UDPブロードキャスト受信用ソケットオープン */
void SocketAdapter::OpenUdpBroadRxSocket(const uint16_t local_port)
{
    this->m_Impl->OpenUdpBroadRxSocket(local_port);
}
/* 〜省略〜 */


Windows版(WinSock2)

Windowsにおけるマルチキャスト / ブロードキャスト通信用インタフェースの実装を行います。 以下に、前回からの差分コードを示します。

【SocketAdapterImpl_WinSock.h】

/* Socket Adapter Implクラス定義 */
class SocketAdapterImpl final
{
public:
    /* 〜省略〜 */

    /* コンストラクタ */
    SocketAdapterImpl()
        : m_Socket(0)
        , m_Address()
        , m_LocalIpAddress(0)   // ★追加
        , m_TTL(0)              // ★追加
        , m_MulticastRequest()  // ★追加
        , m_IsSocketOpened(false)
    {
        /* Nothing to do */
    }

    /* 〜省略〜 */

    /* UDPマルチキャスト送信用ソケットオープン */
    void OpenUdpMultiTxSocket(const std::string& multicast_ip, const std::string& local_ip, const uint16_t multicast_port, const int32_t ttl)
    {
        /* UDP用ソケットをオープン */
        this->m_Socket = socket(AF_INET, SOCK_DGRAM, 0);

        /* ソケットオープン失敗時のエラー処理 */
        if (this->m_Socket == INVALID_SOCKET)
        {
            /* エラーコードセット */
            SocketAdapterImpl::s_ErrorCode = WSAGetLastError();

            /* ソケット例外送出 */
            throw SocketException(SocketAdapterImpl::GetErrorMessage("UDP Multicast Tx Socket Open Failed", SocketAdapterImpl::s_ErrorCode), SocketAdapterImpl::s_ErrorCode);
        }

        /* UDPマルチキャスト送信用アドレス情報セット */
        this->m_Address.sin_family = AF_INET;
        this->m_Address.sin_port = htons(multicast_port);
        this->m_Address.sin_addr.S_un.S_addr = this->ConvertIpStrToNum(this->m_Address.sin_family, multicast_ip);

        /* ローカルIPアドレスセット */
        this->m_LocalIpAddress = this->ConvertIpStrToNum(this->m_Address.sin_family, local_ip);

        /* UDPマルチキャストソケットオプションセット */
        int set_opt_result = setsockopt(this->m_Socket, IPPROTO_IP, IP_MULTICAST_IF, (char*)&this->m_LocalIpAddress, sizeof(this->m_LocalIpAddress));

        /* ソケットオプションセット失敗時のエラー処理 */
        if (set_opt_result != 0)
        {
            /* エラーコードセット */
            SocketAdapterImpl::s_ErrorCode = errno;

            /* ソケット例外送出 */
            throw SocketException(SocketAdapterImpl::GetErrorMessage("UDP Multicast Tx Socket Option Set Failed", SocketAdapterImpl::s_ErrorCode), SocketAdapterImpl::s_ErrorCode);
        }

        /* UDPマルチキャストTTLセット */
        this->m_TTL = (int)ttl;

        /* UDPマルチキャストTTLソケットオプションセット */
        int set_ttl_result = setsockopt(this->m_Socket, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&this->m_TTL, sizeof(this->m_TTL));

        /* ソケットオプションセット失敗時のエラー処理 */
        if (set_ttl_result != 0)
        {
            /* エラーコードセット */
            SocketAdapterImpl::s_ErrorCode = errno;

            /* ソケット例外送出 */
            throw SocketException(SocketAdapterImpl::GetErrorMessage("UDP Multicast Tx TTL Socket Option Set Failed", SocketAdapterImpl::s_ErrorCode), SocketAdapterImpl::s_ErrorCode);
        }

        /* ソケットオープン状態更新 */
        this->m_IsSocketOpened = true;
    }

    /* UDPマルチキャスト受信用ソケットオープン */
    void OpenUdpMultiRxSocket(const std::string& multicast_ip, const uint16_t multicast_port)
    {
        /* UDP用ソケットをオープン */
        this->m_Socket = socket(AF_INET, SOCK_DGRAM, 0);

        /* ソケットオープン失敗時のエラー処理 */
        if (this->m_Socket == INVALID_SOCKET)
        {
            /* エラーコードセット */
            SocketAdapterImpl::s_ErrorCode = WSAGetLastError();

            /* ソケット例外送出 */
            throw SocketException(SocketAdapterImpl::GetErrorMessage("UDP Multicast Rx Socket Open Failed", SocketAdapterImpl::s_ErrorCode), SocketAdapterImpl::s_ErrorCode);
        }

        /* UDPマルチキャスト受信用アドレス情報セット */
        this->m_Address.sin_family = AF_INET;
        this->m_Address.sin_port = htons(multicast_port);
        this->m_Address.sin_addr.S_un.S_addr = INADDR_ANY;

        /* ソケットにアドレス情報をバインド */
        int bind_result = bind(this->m_Socket, (struct sockaddr*)&this->m_Address, sizeof(this->m_Address));

        /* ソケットバインド失敗時のエラー処理 */
        if (bind_result != 0)
        {
            /* エラーコードセット */
            SocketAdapterImpl::s_ErrorCode = WSAGetLastError();

            /* ソケット例外送出 */
            throw SocketException(SocketAdapterImpl::GetErrorMessage("UDP Multicast Rx Socket Bind Failed", SocketAdapterImpl::s_ErrorCode), SocketAdapterImpl::s_ErrorCode);
        }

        /* マルチキャストリクエストのセット */
        memset(&this->m_MulticastRequest, 0, sizeof(this->m_MulticastRequest));
        this->m_MulticastRequest.imr_interface.S_un.S_addr = INADDR_ANY;
        this->m_MulticastRequest.imr_multiaddr.S_un.S_addr = this->ConvertIpStrToNum(this->m_Address.sin_family, multicast_ip);

        /* UDPマルチキャストソケットオプションセット */
        int set_opt_result = setsockopt(this->m_Socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&this->m_MulticastRequest, sizeof(this->m_MulticastRequest));

        /* ソケットオプションセット失敗時のエラー処理 */
        if (set_opt_result != 0)
        {
            /* エラーコードセット */
            SocketAdapterImpl::s_ErrorCode = WSAGetLastError();

            /* ソケット例外送出 */
            throw SocketException(SocketAdapterImpl::GetErrorMessage("UDP Multicast Rx Socket Option Set Failed", SocketAdapterImpl::s_ErrorCode), SocketAdapterImpl::s_ErrorCode);
        }

        /* ソケットオープン状態更新 */
        this->m_IsSocketOpened = true;
    }

    /* UDPブロードキャスト送信用ソケットオープン */
    void OpenUdpBroadTxSocket(const std::string& remote_ip, const uint16_t remote_port)
    {
        /* UDP用ソケットをオープン */
        this->m_Socket = socket(AF_INET, SOCK_DGRAM, 0);

        /* ソケットオープン失敗時のエラー処理 */
        if (this->m_Socket == INVALID_SOCKET)
        {
            /* エラーコードセット */
            SocketAdapterImpl::s_ErrorCode = WSAGetLastError();

            /* ソケット例外送出 */
            throw SocketException(SocketAdapterImpl::GetErrorMessage("UDP Broadcast Tx Socket Open Failed", SocketAdapterImpl::s_ErrorCode), SocketAdapterImpl::s_ErrorCode);
        }

        /* UDPブロードキャスト送信用アドレス情報セット */
        this->m_Address.sin_family = AF_INET;
        this->m_Address.sin_port = htons(remote_port);
        this->m_Address.sin_addr.S_un.S_addr = this->ConvertIpStrToNum(this->m_Address.sin_family, remote_ip);

        /* UDPブロードキャスト送信用ソケットオプションセット */
        BOOL yes = 1;
        int set_opt_result = setsockopt(this->m_Socket, SOL_SOCKET, SO_BROADCAST, (char*)&yes, sizeof(yes));

        /* ソケットオプションセット失敗時のエラー処理 */
        if (set_opt_result != 0)
        {
            /* エラーコードセット */
            SocketAdapterImpl::s_ErrorCode = WSAGetLastError();

            /* ソケット例外送出 */
            throw SocketException(SocketAdapterImpl::GetErrorMessage("UDP Broadcast Tx Socket Option Set Failed", SocketAdapterImpl::s_ErrorCode), SocketAdapterImpl::s_ErrorCode);
        }

        /* ソケットオープン状態更新 */
        this->m_IsSocketOpened = true;
    }

    /* UDPブロードキャスト受信用ソケットオープン */
    void OpenUdpBroadRxSocket(const uint16_t local_port)
    {
        /* UDP用ソケットをオープン */
        this->m_Socket = socket(AF_INET, SOCK_DGRAM, 0);

        /* ソケットオープン失敗時のエラー処理 */
        if (this->m_Socket == INVALID_SOCKET)
        {
            /* エラーコードセット */
            SocketAdapterImpl::s_ErrorCode = WSAGetLastError();

            /* ソケット例外送出 */
            throw SocketException(SocketAdapterImpl::GetErrorMessage("UDP Broadcast Rx Socket Open Failed", SocketAdapterImpl::s_ErrorCode), SocketAdapterImpl::s_ErrorCode);
        }

        /* UDPブロードキャスト受信用アドレス情報セット */
        this->m_Address.sin_family = AF_INET;
        this->m_Address.sin_port = htons(local_port);
        this->m_Address.sin_addr.S_un.S_addr = INADDR_ANY;

        /* ソケットにアドレス情報をバインド */
        int bind_result = bind(this->m_Socket, (struct sockaddr*)&this->m_Address, sizeof(this->m_Address));

        /* ソケットバインド失敗時のエラー処理 */
        if (bind_result != 0)
        {
            /* エラーコードセット */
            SocketAdapterImpl::s_ErrorCode = WSAGetLastError();

            /* ソケット例外送出 */
            throw SocketException(SocketAdapterImpl::GetErrorMessage("UDP Broadcast Rx Socket Bind Failed", SocketAdapterImpl::s_ErrorCode), SocketAdapterImpl::s_ErrorCode);
        }

        /* ソケットオープン状態更新 */
        this->m_IsSocketOpened = true;
    }

    /* 〜省略〜 */
private:
    /* ソケット */
    SOCKET m_Socket;
    /* ソケットアドレス情報 */
    struct sockaddr_in m_Address;
    /* ローカルIPアドレス */
    ULONG m_LocalIpAddress;             // ★追加
    /* マルチキャストTTL */
    int m_TTL;                          // ★追加
    /* マルチキャストリクエスト */
    ip_mreq m_MulticastRequest;         // ★追加
    /* ソケットオープン状態 */
    bool m_IsSocketOpened;
};


Mac / Linux版(Socket)

MacおよびLinuxにおけるマルチキャスト / ブロードキャスト通信用インタフェースの実装を行います。 以下に、前回からの差分コードを示します。

【SocketAdapterImpl_Socket.h】

/* Socket Adapter Implクラス定義 */
class SocketAdapterImpl final
{
public:
     /* 〜省略〜 */

    /* コンストラクタ */
    SocketAdapterImpl()
        : m_Socket(0)
        , m_Address()
        , m_LocalIpAddress(0)       // ★追加
        , m_TTL(0)                  // ★追加
        , m_MulticastRequest()      // ★追加
        , m_IsSocketOpened(false)
    {
        /* Nothing to do */
    }

    /* 〜省略〜 */

    /* UDPマルチキャスト送信用ソケットオープン */
    void OpenUdpMultiTxSocket(const std::string& multicast_ip, const std::string& local_ip, const uint16_t multicast_port, const int32_t ttl)
    {
        /* UDP用ソケットをオープン */
        this->m_Socket = socket(AF_INET, SOCK_DGRAM, 0);

        /* ソケットオープン失敗時のエラー処理 */
        if (this->m_Socket < 0)
        {
            /* エラーコードセット */
            SocketAdapterImpl::s_ErrorCode = errno;

            /* ソケット例外送出 */
            throw SocketException(SocketAdapterImpl::GetErrorMessage("UDP Multicast Tx Socket Open Failed", SocketAdapterImpl::s_ErrorCode), SocketAdapterImpl::s_ErrorCode);
        }

        /* UDPマルチキャスト送信用アドレス情報セット */
        this->m_Address.sin_family = AF_INET;
        this->m_Address.sin_port = htons(multicast_port);
        this->m_Address.sin_addr.s_addr = inet_addr(multicast_ip.c_str());
#if COM_TYPE == COM_MACSOCK
        this->m_Address.sin_len = sizeof(this->m_Address);
#endif

        /* ローカルIPアドレスセット */
        this->m_LocalIpAddress = inet_addr(local_ip.c_str());

        /* UDPマルチキャスト送信用ソケットオプションセット */
        int set_opt_result = setsockopt(this->m_Socket, IPPROTO_IP, IP_MULTICAST_IF, (char*)&this->m_LocalIpAddress, sizeof(this->m_LocalIpAddress));

        /* ソケットオプションセット失敗時のエラー処理 */
        if (set_opt_result != 0)
        {
            /* エラーコードセット */
            SocketAdapterImpl::s_ErrorCode = errno;

            /* ソケット例外送出 */
            throw SocketException(SocketAdapterImpl::GetErrorMessage("UDP Multicast Tx Socket Option Set Failed", SocketAdapterImpl::s_ErrorCode), SocketAdapterImpl::s_ErrorCode);
        }

        /* UDPマルチキャストTTLセット */
        this->m_TTL = (int)ttl;

        /* UDPマルチキャストTTLソケットオプションセット */
        int set_ttl_result = setsockopt(this->m_Socket, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&this->m_TTL, sizeof(this->m_TTL));

        /* ソケットオプションセット失敗時のエラー処理 */
        if (set_ttl_result != 0)
        {
            /* エラーコードセット */
            SocketAdapterImpl::s_ErrorCode = errno;

            /* ソケット例外送出 */
            throw SocketException(SocketAdapterImpl::GetErrorMessage("UDP Multicast Tx TTL Socket Option Set Failed", SocketAdapterImpl::s_ErrorCode), SocketAdapterImpl::s_ErrorCode);
        }

        /* ソケットオープン状態更新 */
        this->m_IsSocketOpened = true;
    }

    /* UDPマルチキャスト受信用ソケットオープン */
    void OpenUdpMultiRxSocket(const std::string& multicast_ip, const uint16_t multicast_port)
    {
        /* UDP用ソケットをオープン */
        this->m_Socket = socket(AF_INET, SOCK_DGRAM, 0);

        /* ソケットオープン失敗時のエラー処理 */
        if (this->m_Socket < 0)
        {
            /* エラーコードセット */
            SocketAdapterImpl::s_ErrorCode = errno;

            /* ソケット例外送出 */
            throw SocketException(SocketAdapterImpl::GetErrorMessage("UDP Multicast Rx Socket Open Failed", SocketAdapterImpl::s_ErrorCode), SocketAdapterImpl::s_ErrorCode);
        }

        /* UDPマルチキャスト受信用アドレス情報セット */
        this->m_Address.sin_family = AF_INET;
        this->m_Address.sin_port = htons(multicast_port);
        this->m_Address.sin_addr.s_addr = INADDR_ANY;
#if COM_TYPE == COM_MACSOCK
        this->m_Address.sin_len = sizeof(this->m_Address);
#endif

        /* ソケットにアドレス情報をバインド */
        int bind_result = bind(this->m_Socket, (struct sockaddr*)&this->m_Address, sizeof(this->m_Address));

        /* ソケットバインド失敗時のエラー処理 */
        if (bind_result != 0)
        {
            /* エラーコードセット */
            SocketAdapterImpl::s_ErrorCode = errno;

            /* ソケット例外送出 */
            throw SocketException(SocketAdapterImpl::GetErrorMessage("UDP Multicast Rx Socket Bind Failed", SocketAdapterImpl::s_ErrorCode), SocketAdapterImpl::s_ErrorCode);
        }

        /* UDPマルチキャスト受信リクエストのセット */
        memset(&this->m_MulticastRequest, 0, sizeof(this->m_MulticastRequest));
        this->m_MulticastRequest.imr_interface.s_addr = INADDR_ANY;
        this->m_MulticastRequest.imr_multiaddr.s_addr = inet_addr(multicast_ip.c_str());

        /* UDPマルチキャスト受信ソケットオプションセット */
        int set_opt_result = setsockopt(this->m_Socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&this->m_MulticastRequest, sizeof(this->m_MulticastRequest));

        /* ソケットオプションセット失敗時のエラー処理 */
        if (set_opt_result != 0)
        {
            /* エラーコードセット */
            SocketAdapterImpl::s_ErrorCode = errno;

            /* ソケット例外送出 */
            throw SocketException(SocketAdapterImpl::GetErrorMessage("UDP Multicast Rx Socket Option Set Failed", SocketAdapterImpl::s_ErrorCode), SocketAdapterImpl::s_ErrorCode);
        }

        /* ソケットオープン状態更新 */
        this->m_IsSocketOpened = true;
    }

    /* UDPブロードキャスト送信用ソケットオープン */
    void OpenUdpBroadTxSocket(const std::string& remote_ip, const uint16_t remote_port)
    {
        /* UDP用ソケットをオープン */
        this->m_Socket = socket(AF_INET, SOCK_DGRAM, 0);

        /* ソケットオープン失敗時のエラー処理 */
        if (this->m_Socket < 0)
        {
            /* エラーコードセット */
            SocketAdapterImpl::s_ErrorCode = errno;

            /* ソケット例外送出 */
            throw SocketException(SocketAdapterImpl::GetErrorMessage("UDP Broadcast Tx Socket Open Failed", SocketAdapterImpl::s_ErrorCode), SocketAdapterImpl::s_ErrorCode);
        }

        /* UDPブロードキャスト送信用アドレス情報セット */
        this->m_Address.sin_family = AF_INET;
        this->m_Address.sin_port = htons(remote_port);
        this->m_Address.sin_addr.s_addr = inet_addr(remote_ip.c_str());
#if COM_TYPE == COM_MACSOCK
        this->m_Address.sin_len = sizeof(this->m_Address);
#endif

        /* UDPブロードキャスト送信用ソケットオプションセット */
        int yes = 1;
        int set_opt_result = setsockopt(this->m_Socket, SOL_SOCKET, SO_BROADCAST, (char*)&yes, sizeof(yes));

        /* ソケットオプションセット失敗時のエラー処理 */
        if (set_opt_result != 0)
        {
            /* エラーコードセット */
            SocketAdapterImpl::s_ErrorCode = errno;

            /* ソケット例外送出 */
            throw SocketException(SocketAdapterImpl::GetErrorMessage("UDP Broadcast Tx Socket Option Set Failed", SocketAdapterImpl::s_ErrorCode), SocketAdapterImpl::s_ErrorCode);
        }

        /* ソケットオープン状態更新 */
        this->m_IsSocketOpened = true;
    }

    /* UDPブロードキャスト受信用ソケットオープン */
    void OpenUdpBroadRxSocket(const uint16_t local_port)
    {
        /* UDP用ソケットをオープン */
        this->m_Socket = socket(AF_INET, SOCK_DGRAM, 0);

        /* ソケットオープン失敗時のエラー処理 */
        if (this->m_Socket < 0)
        {
            /* エラーコードセット */
            SocketAdapterImpl::s_ErrorCode = errno;

            /* ソケット例外送出 */
            throw SocketException(SocketAdapterImpl::GetErrorMessage("UDP Broadcast Rx Socket Open Failed", SocketAdapterImpl::s_ErrorCode), SocketAdapterImpl::s_ErrorCode);
        }

        /* UDPブロードキャスト受信用アドレス情報セット */
        this->m_Address.sin_family = AF_INET;
        this->m_Address.sin_port = htons(local_port);
        this->m_Address.sin_addr.s_addr = INADDR_ANY;
#if COM_TYPE == COM_MACSOCK
        this->m_Address.sin_len = sizeof(this->m_Address);
#endif

        /* ソケットにアドレス情報をバインド */
        int bind_result = bind(this->m_Socket, (struct sockaddr*)&this->m_Address, sizeof(this->m_Address));

        /* ソケットバインド失敗時のエラー処理 */
        if (bind_result != 0)
        {
            /* エラーコードセット */
            SocketAdapterImpl::s_ErrorCode = errno;

            /* ソケット例外送出 */
            throw SocketException(SocketAdapterImpl::GetErrorMessage("UDP Broadcast Rx Socket Bind Failed", SocketAdapterImpl::s_ErrorCode), SocketAdapterImpl::s_ErrorCode);
        }

        /* ソケットオープン状態更新 */
        this->m_IsSocketOpened = true;
    }

    /* 〜省略〜 */
private:
    /* ソケット */
    int m_Socket;
    /* ソケットアドレス情報 */
    struct sockaddr_in m_Address;
    /* ローカルIPアドレス */
    in_addr_t m_LocalIpAddress;         // ★追加
    /* マルチキャストTTL */
    int m_TTL;                          // ★追加
    /* マルチキャストリクエスト */
    ip_mreq m_MulticastRequest;         // ★追加
    /* ソケットオープン状態 */
    bool m_IsSocketOpened;
};


未対応環境

未対応環境にも同様にマルチキャスト / ブロードキャスト通信用インタフェースの実装を行います。 以下に、前回からの差分コードを示します。

【SocketAdapterImpl_Unknown.h】

/* Socket Adapter Implクラス定義 */
class SocketAdapterImpl final
{
public:
    /* 〜省略〜 */

    /* UDPマルチキャスト送信用ソケットオープン */
    void OpenUdpMultiTxSocket(const std::string& multicast_ip, const std::string& local_ip, const uint16_t multicast_port, const int32_t ttl)
    {
        throw std::runtime_error("Invalid Com Type : " + std::to_string(COM_TYPE));
    }

    /* UDPマルチキャスト受信用ソケットオープン */
    void OpenUdpMultiRxSocket(const std::string& multicast_ip, const uint16_t multicast_port)
    {
        throw std::runtime_error("Invalid Com Type : " + std::to_string(COM_TYPE));
    }

    /* UDPブロードキャスト送信用ソケットオープン */
    void OpenUdpBroadTxSocket(const std::string& remote_ip, const uint16_t remote_port)
    {
        throw std::runtime_error("Invalid Com Type : " + std::to_string(COM_TYPE));
    }

    /* UDPブロードキャスト受信用ソケットオープン */
    void OpenUdpBroadRxSocket(const uint16_t local_port)
    {
        throw std::runtime_error("Invalid Com Type : " + std::to_string(COM_TYPE));
    }

    /* 〜省略〜 */
};


サンプルコード

機能

SocketAdapterクラスを使ったUDPマルチキャスト / ブロードキャスト送受信のサンプルコードを実装します。
サンプルコードでは、Sender/Receiverをそれぞれコンソールから起動することで以下のような動作をします。

【Sender】

  1. UDP送信用ソケットを開く(★)
  2. キーボードからの送信メッセージ入力を受け付ける
  3. キーボードから入力された送信メッセージをReceiverに送信する
  4. キーボードから"exit"が入力されると、ソケットを閉じてプログラムを終了する


【Receiver】

  1. UDP受信用ソケットを開く(★)
  2. Senderからのメッセージ受信を待機する
  3. メッセージを受信したら内容を表示する
  4. 受信メッセージが"exit"の場合、ソケットを閉じてプログラムを終了する


★ ユニキャスト / マルチキャスト / ブロードキャストの切り替えは、ソケットを開く際のインタフェースと受け渡すパラメータによって行います。
今回のサンプルコードでは、以下の環境を想定しています。

項目 備考
送信側IP 192.168.1.10 ★1
受信側IP 192.168.1.11 ★1
マルチキャストIP 224.0.0.32 ★2
マルチキャストTTL 1 ★3
ブロードキャストIP 255.255.255.255 ★4
送受信ポート 50000 ★5

★1 送信側と受信側はスイッチングハブで接続され、同一セグメントに属しているものとします

★2 マルチキャストで送受信する場合は、決められたアドレスの範囲(224.0.0.0 ~ 239.255.255.255)にパケットを送信する必要があります。
また、マルチキャストアドレスは、送受信する範囲(スコープ)によってアドレス範囲が決まっています。 更に、マルチキャストのアドレス割当は、ICANNにより予約されており、一部のアドレスは静的に割り当てられた「Well-Knownアドレス」となり、それ以外は動的に割り当てが可能なアドレスとなります。
今回は、同一セグメント内の通信なので、リンクローカルで動的割当が可能なアドレスとして、「224.0.0.32」を選択しています。

★3 マルチキャストパケットの配信範囲(スコープ)は、TTL(Time to Live)のしきい値によって制御することが出来ます。 今回は、同一セグメント(リンクローカル)通信なので、TTLは通常「1」として配信されます。
そのため、パラメータとして渡すTTLも明示的に「1」を設定します。

★4 ブロードキャストには、自ネットワークのみにブロードキャストする「リミテッドブロードキャスト」と、ブロードキャスト先ネットワークを指定できる「ディレクティッドブロードキャスト」があります。
今回は、同一セグメント(リンクローカル)通信なので、自ネットワークへのブロードキャストとなる「リミテッドブロードキャストアドレス」となる「255.255.255.255」を選択しています。

★5 TCP/UDPで使われるポート番号はIANAによって管理されており、登録済みのポート番号と、動的・プライベートに使用可能なポート番号があります。
今回は、動的に使用可能な範囲のポート番号である「50000」を設定しています。

実装

Sender / Receiverそれぞれのサンプルコードを以下に示します。 変更点はソケットオープン処理のみになります。
また、ユニキャスト / マルチキャスト / ブロードキャストそれぞれのソケットオープン処理をコンパイルスイッチで切り替えられるようにしています。

【CompileSwitch.h】

#define TEST_MODE_UNICAST       (0)
#define TEST_MODE_MULTICAST     (1)
#define TEST_MODE_BROADCAST     (2)

#define TEST_MODE               TEST_MODE_MULTICAST


【Sender.cpp】

int main()
{
    /* 〜省略〜 */

    /* Socket Adapterインスタンス生成 */
    SocketAdapter adapter;

    std::cout << "Socket Adapter Instance Creation Success" << std::endl;

#if TEST_MODE == TEST_MODE_UNICAST
    /* UDPユニキャスト送信用ソケットオープン */
    adapter.OpenUdpUniTxSocket("192.168.1.11", 50000);

    std::cout << "UDP Unicast Tx Socket Open Success" << std::endl;
#elif TEST_MODE == TEST_MODE_MULTICAST
    /* UDPマルチキャスト送信用ソケットオープン */
    adapter.OpenUdpMultiTxSocket("224.0.0.32", "192.168.1.10", 50000, 1);

    std::cout << "UDP Multicast Tx Socket Open Success" << std::endl;
#elif TEST_MODE == TEST_MODE_BROADCAST
    /* UDPブロードキャスト送信用ソケットオープン */
    adapter.OpenUdpBroadTxSocket("255.255.255.255", 50000);

    std::cout << "UDP Broadcast Tx Socket Open Success" << std::endl;
#endif

    /* 〜省略〜 */
}


【Receiver.cpp】

int main()
{
    /* 〜省略〜 */

    /* Socket Adapterインスタンス生成 */
    SocketAdapter adapter;

    std::cout << "Socket Adapter Instance Creation Success" << std::endl;

#if TEST_MODE == TEST_MODE_UNICAST
    /* UDPユニキャスト受信用ソケットオープン */
    adapter.OpenUdpUniRxSocket(50000);

    std::cout << "UDP Unicast Rx Socket Open Success" << std::endl;
#elif TEST_MODE == TEST_MODE_MULTICAST
    /* UDPマルチキャスト受信用ソケットオープン */
    adapter.OpenUdpMultiRxSocket("224.0.0.32", 50000);

    std::cout << "UDP Multicast Rx Socket Open Success" << std::endl;
#elif TEST_MODE == TEST_MODE_BROADCAST
    /* UDPブロードキャスト受信用ソケットオープン */
    adapter.OpenUdpBroadRxSocket(50000);

    std::cout << "UDP Broadcast Rx Socket Open Success" << std::endl;
#endif

    /* 〜省略〜 */
}


あとがき

今回は、C++UDPマルチキャスト / ブロードキャスト通信を行う処理を実装しました。
次回は、大きなサイズのデータを送受信するためのパケット分割を実装したいと思います。