Linux 什么会导致 sock send() 命令出现“资源暂时不可用”

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14370489/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 18:42:20  来源:igfitidea点击:

What can cause a “Resource temporarily unavailable” on sock send() command

clinuxsocketsunix

提问by giroy

What can cause a Resource temporarily unavailableerror on a socket send()command? The socket is setup as AF_UNIX, SOCK_STREAM. It works most of the time, but occasionally gets this error. The receiving end of the socket appears to be working properly.

什么会导致Resource temporarily unavailable套接字send()命令出错?套接字设置为AF_UNIX, SOCK_STREAM. 它大部分时间都有效,但偶尔会出现此错误。套接字的接收端似乎工作正常。

I know this isn't very detailed, but I'm just looking for general ideas. Thanks!

我知道这不是很详细,但我只是在寻找一般性的想法。谢谢!

采纳答案by caf

"Resource temporarily unavailable"is the error message corresponding to EAGAIN, which means that the operation would have blocked but nonblocking operation was requested. For send(), that could be due to any of:

"Resource temporarily unavailable"是对应于 的错误消息EAGAIN,这意味着该操作会被阻塞,但请求的是非阻塞操作。对于send(),这可能是由于:

  • explicitly marking the file descriptor as nonblocking with fcntl(); or
  • passing the MSG_DONTWAITflag to send(); or
  • setting a send timeout with the SO_SNDTIMEOsocket option.
  • 显式地将文件描述符标记为非阻塞fcntl();或者
  • MSG_DONTWAIT旗帜传递给send(); 或者
  • 使用SO_SNDTIMEO套接字选项设置发送超时。

回答by Davide Berra

That's because you're using a non-blockingsocket and the output buffer is full.

那是因为您正在使用non-blocking套接字并且输出缓冲区已满。

From the send()man page

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.  

EAGAINis the error code tied to "Resource temporarily unavailable"

EAGAIN是与“资源暂时不可用”相关的错误代码

Consider using select()to get a better control of this behaviours

考虑使用select()以更好地控制此行为

回答by Jayhello

Let'e me give an example:

让我举个例子:

  1. client connect to server, and send 1MB data to server every 1 second.

  2. server side accept a connection, and then sleep 20 second, without recv msg from client.So the tcp send bufferin the client side will be full.

  1. 客户端连接到服务器,每 1 秒发送 1MB 数据到服务器。

  2. 服务器端接受一个连接,然后休眠 20 秒,没有来自tcp send buffer客户端的recv msg 。所以客户端会被填满。

Code in client side:

客户端代码:

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

Code in server side:

服务器端的代码:

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

Start server side, then start client side.

启动服务器端,然后启动客户端。

server side may output:

服务器端可能会输出:

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

enter image description here

在此处输入图片说明

client side may output:

客户端可能会输出:

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 

enter image description here

在此处输入图片说明

You can see, as the server side doesn't recv the data from client, so when the client side tcp bufferget full, but you still send data, so you may get Resource temporarily unavailableerror.

可以看到,由于服务器端没有从客户端接收数据,所以当客户端tcp buffer已满时,您仍然发送数据,因此可能会Resource temporarily unavailable出错。