GNU/Linux >> Linux の 問題 >  >> Linux

sock send() コマンドで「リソースが一時的に利用不可」になる原因

"Resource temporarily unavailable" EAGAIN に対応するエラー メッセージです。 、これは、操作がブロックされたが、非ブロック操作が要求されたことを意味します。 send() の場合 、それは次のいずれかが原因である可能性があります:

  • fcntl() を使用してファイル記述子を非ブロックとして明示的にマークする;または
  • MSG_DONTWAIT を渡す send() へのフラグ;または
  • SO_SNDTIMEO で送信タイムアウトを設定する ソケット オプション。

例を挙げましょう:

<オール>
  • クライアントはサーバーに接続し、1 秒ごとに 1MB のデータをサーバーに送信します。

  • サーバー側は接続を受け入れ、クライアントからの recv msg なしで 20 秒間スリープします。したがって、 tcp send buffer クライアント側がいっぱいになります。

  • クライアント側のコード:

    #include <arpa/inet.h>
    #include <sys/socket.h>
    #include <stdio.h>
    #include <errno.h>
    #include <fcntl.h>
    #include <stdlib.h>
    #include <string.h>
    #define exit_if(r, ...)                                                                          \
        if (r) {                                                                                     \
            printf(__VA_ARGS__);                                                                     \
            printf("%s:%d error no: %d error msg %s\n", __FILE__, __LINE__, errno, strerror(errno)); \
            exit(1);                                                                                 \
        }
    
    void setNonBlock(int fd) {
        int flags = fcntl(fd, F_GETFL, 0);
        exit_if(flags < 0, "fcntl failed");
        int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
        exit_if(r < 0, "fcntl failed");
    }
    
    void test_full_sock_buf_1(){
        short port = 8000;
        struct sockaddr_in addr;
        memset(&addr, 0, sizeof addr);
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        addr.sin_addr.s_addr = INADDR_ANY;
    
    
        int fd = socket(AF_INET, SOCK_STREAM, 0);
        exit_if(fd<0, "create socket error");
    
        int ret = connect(fd, (struct sockaddr *) &addr, sizeof(struct sockaddr));
        exit_if(ret<0, "connect to server error");
        setNonBlock(fd);
    
        printf("connect to server success");
    
        const int LEN = 1024 * 1000;
        char msg[LEN];  // 1MB data
        memset(msg, 'a', LEN);
    
        for (int i = 0; i < 1000; ++i) {
            int len = send(fd, msg, LEN, 0);
            printf("send: %d, erron: %d, %s \n", len, errno, strerror(errno));
            sleep(1);
        }
    
    }
    
    int main(){
        test_full_sock_buf_1();
    
        return 0;
    }
    

    サーバー側のコード:

        #include <arpa/inet.h>
        #include <sys/socket.h>
        #include <stdio.h>
        #include <errno.h>
        #include <fcntl.h>
        #include <stdlib.h>
        #include <string.h>
        #define exit_if(r, ...)                                                                          \
            if (r) {                                                                                     \
                printf(__VA_ARGS__);                                                                     \
                printf("%s:%d error no: %d error msg %s\n", __FILE__, __LINE__, errno, strerror(errno)); \
                exit(1);                                                                                 \
            }
    void test_full_sock_buf_1(){
    
        int listenfd = socket(AF_INET, SOCK_STREAM, 0);
        exit_if(listenfd<0, "create socket error");
    
        short port = 8000;
        struct sockaddr_in addr;
        memset(&addr, 0, sizeof addr);
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        addr.sin_addr.s_addr = INADDR_ANY;
    
        int r = ::bind(listenfd, (struct sockaddr *) &addr, sizeof(struct sockaddr));
        exit_if(r<0, "bind socket error");
    
        r = listen(listenfd, 100);
        exit_if(r<0, "listen socket error");
    
        struct sockaddr_in raddr;
        socklen_t rsz = sizeof(raddr);
        int cfd = accept(listenfd, (struct sockaddr *) &raddr, &rsz);
        exit_if(cfd<0, "accept socket error");
    
        sockaddr_in peer;
        socklen_t alen = sizeof(peer);
        getpeername(cfd, (sockaddr *) &peer, &alen);
    
        printf("accept a connection from %s:%d\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port));
    
        printf("but now I will sleep 15 second, then exit");
        sleep(15);
    }
    

    サーバー側を開始してから、クライアント側を開始します。

    サーバー側の出力:

    accept a connection from 127.0.0.1:35764
    but now I will sleep 15 second, then exit
    Process finished with exit code 0
    

    クライアント側の出力:

    connect to server successsend: 1024000, erron: 0, Success 
    send: 1024000, erron: 0, Success 
    send: 1024000, erron: 0, Success 
    send: 552190, erron: 0, Success 
    send: -1, erron: 11, Resource temporarily unavailable 
    send: -1, erron: 11, Resource temporarily unavailable 
    send: -1, erron: 11, Resource temporarily unavailable 
    send: -1, erron: 11, Resource temporarily unavailable 
    send: -1, erron: 11, Resource temporarily unavailable 
    send: -1, erron: 11, Resource temporarily unavailable 
    send: -1, erron: 11, Resource temporarily unavailable 
    send: -1, erron: 11, Resource temporarily unavailable 
    send: -1, erron: 11, Resource temporarily unavailable 
    send: -1, erron: 11, Resource temporarily unavailable 
    send: -1, erron: 11, Resource temporarily unavailable 
    send: -1, erron: 104, Connection reset by peer 
    send: -1, erron: 32, Broken pipe 
    send: -1, erron: 32, Broken pipe 
    send: -1, erron: 32, Broken pipe 
    send: -1, erron: 32, Broken pipe 
    send: -1, erron: 32, Broken pipe 
    

    サーバー側はクライアントからのデータを受信しないため、クライアント側が tcp buffer いっぱいになりますが、まだデータを送信しているため、Resource temporarily unavailable を取得する可能性があります エラーです。


    non-blocking を使用しているためです。 ソケットと出力バッファがいっぱいです.

    send() から マニュアルページ

       When the message does not fit into  the  send  buffer  of  the  socket,
       send() normally blocks, unless the socket has been placed in non-block-
       ing I/O mode.  In non-blocking mode it  would  return  EAGAIN  in  this
       case.  
    

    またね 「リソースが一時的に利用できません」に関連付けられているエラー コードです。

    select() の使用を検討してください この動作をより適切に制御するには


    Linux
    1. ls コマンドからディレクトリを除外するにはどうすればよいですか

    2. Docker コマンドが Docker デーモンに接続できない

    3. Linux コマンドラインから gpg 暗号化メールを自動的に送信するにはどうすればよいですか?

    1. 起動後にコマンドを実行するにはどうすればよいですか?

    2. cd ` (バックティック) の目的は何ですか?

    3. 自分のネットワークをテストできますか?

    1. 何をしますか。 〜/.bashrcコマンド実行??

    2. スワップ パーティション内のすべてを強制的に解放してメモリに戻すには、どのコマンドを使用できますか?

    3. シグナル 11 の原因は何ですか?