Linux CでUDPのRequest/Responseクライアント通信

2012/02/16 | By KUMA | Filed in: 技術メモ.

自社の製品でUDPを使ってRequest/Responseのデータを返す機器を採用することになったのだが、RequestのSourcePortにResponseを返す作りになっていたので、その時の実装をメモ。

ここここをベースにサンプルを作成。エラー処理も同様に割愛。

要は送受信のsocketを共用にして、bindする前にPortを指定しなければいい話。

サンプルコードは送信は1回だけ。★が今回書きたかったところ。

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int
main()
{
    int sock;
    struct sockaddr_in recvaddr;
    fd_set fds, readfds;
    char buf[2048];

    /* 受信ソケットを2つ作ります */
    sock = socket(AF_INET, SOCK_DGRAM, 0);

    recvaddr.sin_family = AF_INET;
    recvaddr.sin_addr.s_addr = INADDR_ANY;

    // ★recvaddrにポートを指定しない
    bind(sock, (struct sockaddr *)&recvaddr, sizeof(recvaddr));

    /* fd_setの初期化します */
    FD_ZERO(&readfds);
    FD_SET(sock, &readfds);

    /*
        送信処理
        意味をはっきり分けるためブロック化
    */
    {
        struct sockaddr_in sendaddr;

        sendaddr.sin_family = AF_INET;
        /* 宛先ポート */
        sendaddr.sin_port = htons(12345);
        /* 宛先IP */
        sendaddr.sin_addr.s_addr = inet_addr("127.0.0.1");

        // ★受信socketを使用して送信
        sendto(sock, "HELLO", 5, 0, (struct sockaddr *)&sendaddr, sizeof(sendaddr));
    }

    /*
        無限ループです
        このサンプルでは、この無限ループを抜けません
    */
    while (1) {
        /* 読み込み用fd_setの初期化 */
        /* selectが毎回内容を上書きしてしまうので、毎回初期化します */
        memcpy(&fds, &readfds, sizeof(fd_set));

        /* fdsに設定されたソケットが読み込み可能になるまで待ちます */
        /* 一つ目の引数はファイルディスクリプタの最大値+1にします */
        select(sock+1, &fds, NULL, NULL, NULL);

        /* sockに読み込み可能データがある場合 */
        if (FD_ISSET(sock, &fds)) {
            /* sockからデータを受信して表示します */
            memset(buf, 0, sizeof(buf));
            recv(sock, buf, sizeof(buf), 0);
            printf("%s\n", buf);
        }

    }

    /* このサンプルでは、ここへは到達しません */
    close(sock);

    return 0;
}

個人的にRequest/Responseの形にするならTCPを使ったほうがいいと思うし、UDPでSourcePortに送るって仕組みがなんかしっくりこない。
UDPならそれぞれ決めたDestinationPortでやり取りするのが自然な気がする。
ともあれ久々に生のUDPを叩いた記念のメモでした。


Tags: , ,

Comments are closed here.