

C++でUDPソケットを作成しています.  
現在のものは, サーバがバッファにメッセージを貯めてまとめて表示するようになっています. デバッグのために受信した都度コンソールに表示をしたいです.
これを実現するには下のコードをどのように修正したら良いのか教えていただきたいです.

環境：Ubuntu 20.04

### ソースコード

**server.cpp**

[code]

    #include <stdio.h>
    #include <string.h>
    #include "udp_socket.hpp"
    #include <unistd.h>
    
    const int PortNumber = 50000; //49152~65535
    const char *IPaddress = "172.0.0.1"; //recv側のip
    
    const int recv_max_size = 10;
    const int64_t timeout_setting = 10;
    
    int main(int argc, char** argv) {
        UDPSocket udp0(IPaddress, PortNumber);
    
        udp0.udp_bind();
    
        for(int i = 0; i < recv_max_size; i++) {
            if(udp0.timeout(timeout_setting) == 0) {
                perror("time out");
                break;
            }
            std::string data = udp0.udp_recv();
            printf("%s\n", data.c_str());
        }
    
        return 0;
    }
    
[/code]

**udp_socket.hpp** (サーバー, クライアントの共通部分をまとめたもの)

[code]

    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <string>
    #include <string.h>
    
    class UDPSocket{
        int sock;
        struct sockaddr_in addr;
    public:
        UDPSocket(std::string address, int port){
            sock = socket(AF_INET, SOCK_DGRAM, 0);
            addr.sin_family = AF_INET;
            addr.sin_addr.s_addr = inet_addr(address.c_str());
            addr.sin_port = htons(port);
        }
        void udp_send(std::string word){
            sendto(sock, word.c_str(), word.length(), 0, (struct sockaddr *)&addr, sizeof(addr));
        }
    
        void udp_bind(){
            bind(sock, (const struct sockaddr *)&addr, sizeof(addr));
        }
    
        int timeout(int64_t sec) {
            FD_ZERO(&readfds);
            FD_SET(sd, &readfds);
            tv.tv_sec = sec;
            return select(sd + 1, &readfds, NULL, NULL, &tv);
        }
    
        std::string udp_recv(){
            #define BUFFER_MAX 400
            char buf[BUFFER_MAX];
            memset(buf, 0, sizeof(buf));
            recv(sock, buf, sizeof(buf), 0);
            return std::string(buf);
        }
        void udp_recv(char *buf, int size){
            memset(buf, 0, size);
            recv(sock, buf, size, 0);
        }
    
        ~UDPSocket(){
            close(sock);
        }
    private:
        int sd;
        struct sockaddr_in addr;
        socklen_t sin_size;
        struct sockaddr_in from_addr;
        fd_set readfds;
        struct timeval tv;
    };
    
[/code]

下が現在のサーバに`sendto`したときのログです.  
`close`した後にまとめて`write`しています. これを`recvfrom`するたびに`write`するようにしたいです.

[code]

    11:38:10.804218 select(9, [8], NULL, NULL, {tv_sec=140262861, tv_usec=519539}) = 1 (in [8], left {tv_sec=140262859, tv_usec=184927}) <2.334631>
    11:38:13.139160 recvfrom(8, "hello wolrd", 1500, 0, {sa_family=AF_INET, sin_port=htons(51618), sin_addr=inet_addr("127.0.0.1")}, [32657->16]) = 11 <0.000007>
    11:38:13.139263 fstat(1, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0 <0.000009>
    11:38:13.139312 select(9, [8], NULL, NULL, {tv_sec=5, tv_usec=184927}) = 1 (in [8], left {tv_sec=4, tv_usec=682655}) <0.502286>
    11:38:13.641707 recvfrom(8, "hello wolrd", 1500, 0, {sa_family=AF_INET, sin_port=htons(51618), sin_addr=inet_addr("127.0.0.1")}, [16]) = 11 <0.000011>
    11:38:13.641792 select(9, [8], NULL, NULL, {tv_sec=5, tv_usec=682655}) = 1 (in [8], left {tv_sec=4, tv_usec=745982}) <0.936685>
    11:38:14.578538 recvfrom(8, "hello wolrd", 1500, 0, {sa_family=AF_INET, sin_port=htons(51618), sin_addr=inet_addr("127.0.0.1")}, [16]) = 11 <0.000007>
    11:38:14.578597 select(9, [8], NULL, NULL, {tv_sec=5, tv_usec=745982}) = 1 (in [8], left {tv_sec=5, tv_usec=478603}) <0.267391>
    11:38:14.846041 recvfrom(8, "hello wolrd", 1500, 0, {sa_family=AF_INET, sin_port=htons(51618), sin_addr=inet_addr("127.0.0.1")}, [16]) = 11 <0.000007>
    11:38:14.846098 select(9, [8], NULL, NULL, {tv_sec=5, tv_usec=478603}) = 1 (in [8], left {tv_sec=5, tv_usec=278146}) <0.200496>
    11:38:15.046662 recvfrom(8, "hello wolrd", 1500, 0, {sa_family=AF_INET, sin_port=htons(51618), sin_addr=inet_addr("127.0.0.1")}, [16]) = 11 <0.000008>
    11:38:15.046730 select(9, [8], NULL, NULL, {tv_sec=5, tv_usec=278146}) = 1 (in [8], left {tv_sec=2, tv_usec=754525}) <2.523639>
    11:38:17.570431 recvfrom(8, "hello wolrd", 1500, 0, {sa_family=AF_INET, sin_port=htons(51618), sin_addr=inet_addr("127.0.0.1")}, [16]) = 11 <0.000011>
    11:38:17.570497 select(9, [8], NULL, NULL, {tv_sec=5, tv_usec=754525}) = 1 (in [8], left {tv_sec=4, tv_usec=82849}) <1.671736>
    11:38:19.242312 recvfrom(8, "hello wolrd", 1500, 0, {sa_family=AF_INET, sin_port=htons(51618), sin_addr=inet_addr("127.0.0.1")}, [16]) = 11 <0.000008>
    11:38:19.242372 select(9, [8], NULL, NULL, {tv_sec=5, tv_usec=82849}) = 1 (in [8], left {tv_sec=3, tv_usec=527405}) <1.555459>
    11:38:20.797890 recvfrom(8, "hello wolrd", 1500, 0, {sa_family=AF_INET, sin_port=htons(51618), sin_addr=inet_addr("127.0.0.1")}, [16]) = 11 <0.000008>
    11:38:20.797968 select(9, [8], NULL, NULL, {tv_sec=5, tv_usec=527405}) = 1 (in [8], left {tv_sec=3, tv_usec=153375}) <2.374050>
    11:38:23.172093 recvfrom(8, "hello wolrd", 1500, 0, {sa_family=AF_INET, sin_port=htons(51618), sin_addr=inet_addr("127.0.0.1")}, [16]) = 11 <0.000010>
    11:38:23.172182 select(9, [8], NULL, NULL, {tv_sec=5, tv_usec=153375}) = 1 (in [8], left {tv_sec=2, tv_usec=996190}) <2.157256>
    11:38:25.329508 recvfrom(8, "hello wolrd", 1500, 0, {sa_family=AF_INET, sin_port=htons(51618), sin_addr=inet_addr("127.0.0.1")}, [16]) = 11 <0.000008>
    11:38:25.329580 close(8)                = 0 <0.000016>
    11:38:25.329626 write(1, "hello wolrd\nhello wolrd\nhello wo"..., 120) = 120 <0.000012>
    11:38:25.329682 exit_group(0)           = ?
    11:38:25.329806 +++ exited with 0 +++
    
[/code]


CC BY-SA 4.0
2022-11-23T11:19:14.937
92395
2022-11-27T01:59:53.730
2022-11-24T07:03:57.597
4236
54466
question
c++ linux c
UDPソケットで受信のたびにコンソール表示したい


やっと質問の意味が分かりました。

[code]

    std::string data = udp0.udp_recv();
    printf("%s\n", data.c_str());
    
[/code]

と「都度`printf`を呼び出しているのになぜか出力されない」という`stdout`の挙動に関する質問でしょうか？
そうであればUDPは全く関係ありません。尋ねたい内容を適切に質問してください。

原因はバッファリングされているためです。ここでバッファリングの既定値は
[setvbuf(3)](https://manpages.ubuntu.com/manpages/focal/en/man3/setvbuf.3.html)
に

> Normally all files are block buffered. If a stream refers to a terminal (as
> stdout normally does), it is line buffered. The standard error stream stderr
> is always unbuffered by default.

と説明がありまして

  * ファイルはブロックバッファリングする
  * `stdout`は行バッファリングする
  * `stderr`はバッファリングしない

となっています。`"%s\n"`を改行を含んでいるので行バッファリングは行われますが、本来は都度表示されるはずです。表示されないのは、リダイレクトやパイプなど直接コンソールに出力しておらず、ブロックバッファリングされているからではないでしょうか？

解決策は

[code]

    setvbuf(stdout, NULL, _IOLBF, 0);
    
[/code]

と行バッファリングに変更することでしょうか。

* * *

質問へのoririさんのコメントより

> `write`の説明もお願いします(質問へ)。マークダウンでのこの表記は `write` 関数と捉えることができ, ログの書き込み処理か何か, あるいは
> send/sendtoの代わりに `write`使用しているなど複数の意味に取れます。あるいは単なる表示を意味しているのなら表示もしくは
> `printf` のほうが伝わりやすいかも

誤解があるようなので説明しておきます。dameoさんもコメントされていますが、質問文末尾のログは[`strace(1)`コマンド](https://manpages.ubuntu.com/manpages/focal/en/man1/strace.1.html)によるシステムコールのログです。  
このためログに登場する

>
[code]

>     11:38:25.329626 write(1, "hello wolrd\nhello wolrd\nhello wo"..., 120) =
> 120 <0.000012>
>  
[/code]

はlibcに含まれる[`write(3)`関数](https://manpages.ubuntu.com/manpages/focal/en/man3/write.3posix.html)ではなく、[`write(2)`システムコール](https://manpages.ubuntu.com/manpages/focal/en/man2/write.2.html)を指します。`printf`関数他、書き込みを行う関数は最終的に`write`システムコールを呼び出すことになります。  
また、第一引数の値が `1` となっていますが、これは歴史的に[`stdin = 0`、`stdout = 1`、`stderr =
2`](https://manpages.ubuntu.com/manpages/focal/en/man3/stdout.3.html)と定められているため、`stdout`であることが読み取れます。

* * *

回答へのdameoさんのコメントより

> 書き込まれるのがそこだけとは限らないし、対策はfflush(stdout)の方が簡単で十分かと

仮に私の推測が正しく意図しないバッファリングが原因だった場合、`stdout`使用箇所すべてにいちいち`fflush(stdout)`を記述するのは、煩雑で記述漏れの懸念もあり、対策として適切ではないと考えます。個人としてそのような対応をとるのは構いませんが、Q&Aとしてはやはり、バッファリングが原因であればバッファリングを制御すべきです。（小泉構文）


CC BY-SA 4.0
2022-11-24T07:03:33.423
92401
2022-11-27T01:12:20.657
2022-11-27T01:12:20.657
4236
4236
92395
answer


# 回答ではありません

<https://ja.stackoverflow.com/a/92401/54957>  
についてですが、コメントの依頼を聞き入れて頂けなかったので、動かして確認できない話というのは分かりにくいだろうということもあり、同根の簡易な現象再現/確認合わせてコードにしてみました。

[code]

    do_test() {
            echo '[console]'
            strace ./fwrite 3>&2 2>&1 1>&3 | grep ^write
            echo '[redirect >/dev/null]'
            strace ./fwrite 2>&1 1>/dev/null | grep ^write
    }
    cat >fwrite.cpp <<EOF
    #include <stdio.h>
    int main() {
            const char str[] = {"hello\n"};
            fwrite(str, sizeof(str[0]), sizeof(str)/sizeof(str[0]) - 1, stdout);
    #ifdef FLUSH
            fflush(stdout);
    #endif
            fwrite(str, sizeof(str[0]), sizeof(str)/sizeof(str[0]) - 1, stdout);
            return 0;
    }
    EOF
    g++ -g -Wall -Wpedantic fwrite.cpp -o fwrite
    echo '== without flush =='
    do_test
    g++ -g -Wall -Wpedantic -DFLUSH fwrite.cpp -o fwrite
    echo '== with flush =='
    do_test
    
[/code]

上記スクリプトの出力例は以下のとおりです。

[code]

    $ sh fwrite.sh 
    == without flush ==
    [console]
    hello
    write(1, "hello\n", 6)                  = 6
    hello
    write(1, "hello\n", 6)                  = 6
    [redirect >/dev/null]
    write(1, "hello\nhello\n", 12)          = 12
    == with flush ==
    [console]
    hello
    write(1, "hello\n", 6)                  = 6
    hello
    write(1, "hello\n", 6)                  = 6
    [redirect >/dev/null]
    write(1, "hello\n", 6)                  = 6
    write(1, "hello\n", 6)                  = 6
    $
    
[/code]


CC BY-SA 4.0
2022-11-25T03:53:07.233
92420
2022-11-27T01:59:53.730
2022-11-27T01:59:53.730
54957
54957
92395
answer