GNU/Linux >> Belajar Linux >  >> Linux

Apa yang dapat menyebabkan "Sumber daya sementara tidak tersedia" pada perintah sock send()

"Resource temporarily unavailable" adalah pesan kesalahan yang sesuai dengan EAGAIN , yang berarti bahwa operasi akan diblokir tetapi operasi nonblocking diminta. Untuk send() , yang dapat disebabkan oleh salah satu dari:

  • secara eksplisit menandai deskriptor file sebagai nonblocking dengan fcntl(); atau
  • melewati MSG_DONTWAIT tandai ke send(); atau
  • menyetel waktu tunggu pengiriman dengan SO_SNDTIMEO opsi soket.

Mari saya beri contoh:

  1. klien terhubung ke server, dan mengirim data 1MB ke server setiap 1 detik.

  2. sisi server menerima koneksi, lalu tidur 20 detik, tanpa menerima pesan dari klien. Jadi tcp send buffer di sisi klien akan penuh.

Kode di sisi klien:

#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;
}

Kode di sisi server:

    #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);
}

Mulai sisi server, lalu mulai sisi klien.

sisi server dapat menampilkan:

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

sisi klien dapat menampilkan:

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 

Anda dapat melihat, karena sisi server tidak menerima data dari klien, jadi ketika sisi klien tcp buffer penuh, tetapi Anda tetap mengirim data, jadi Anda mungkin mendapatkan Resource temporarily unavailable kesalahan.


Itu karena Anda menggunakan non-blocking socket dan buffer keluaran penuh.

Dari send() halaman manual

   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.  

MULAI apakah kode kesalahan terkait dengan "Sumber daya sementara tidak tersedia"

Pertimbangkan untuk menggunakan select() untuk mendapatkan kontrol yang lebih baik atas perilaku ini


Linux
  1. Apa yang dapat menyebabkan SIGHUP dihasilkan?

  2. Perintah Docker tidak dapat terhubung ke daemon Docker

  3. Apa yang harus dilakukan ketika Ctrl + C tidak dapat menghentikan proses?

  1. Apa yang menyebabkan perintah file di Linux melaporkan file teks sebagai data biner?

  2. Untuk apa Linux test -a command test?

  3. Bisakah saya menguji jaringan saya sendiri?

  1. Apa yang dapat dilakukan shell dotfile untuk Anda

  2. Perintah apa yang dapat digunakan untuk memaksa melepaskan semua yang ada di partisi swap kembali ke memori?

  3. Apa yang dapat menyebabkan sinyal 11?