【问题标题】:Would UDP sendto in linux return ENOBUFS?linux中的UDP sendto会返回ENOBUFS吗?
【发布时间】:2020-03-09 12:57:21
【问题描述】:

我已经跟踪了整个 udp 发送过程中的 linux 源代码。

我认为 sendto() 的返回值是由rc = q->enqueue(skb, q, &to_free) & NET_XMIT_MASK; 返回的。当 qdisc 满时,它会丢弃数据包并返回NET_XMIT_DROP,然后将值转换为ENOBUFS

以下是我的问题。 我写了一个 udp 客户端应用程序。它发出udp数据包,我让它连续接收PFC暂停帧,导致网卡的数据包缓冲区,驱动程序的txring和qdisc都满了。不过,

  • sendto() 在阻塞模式下被阻塞,在非阻塞模式下得到 errno EAGAIN,表示 UDP 发送套接字缓冲区已满。但是,完整的 qdisc 只是在数据包入队时丢弃数据包,为什么 UDP 发送缓冲区会满?
  • 没有返回ENOBUFS,即使我将qdisc的长度设置为小ifconfig txqueuelen 10。该程序执行了 setsockopt(sockfd, SOL_IP, IP_RECVERR,(char*)&val, sizeof(val)); ,但没有返回错误。因为有一些关于IP_RECVERR用法的例子,如果我的代码中有任何错误的用法,请告诉我。非常感谢!
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/errno.h>
#include <fcntl.h>
#include <time.h>
#include <linux/errqueue.h>


#define UDP_TEST_PORT       5000
#define UDP_SERVER_IP       "10.0.10.34"

int ip_control_msg( struct cmsghdr *msg )
{
    int ret = 0;
    switch( msg->cmsg_type ){
    case IP_RECVERR:
        {
            struct sock_extended_err *exterr;
            exterr = (struct sock_extended_err *)(CMSG_DATA(msg));
            printf("ee_errno: %u\n", exterr->ee_errno );
            printf("ee_origin: %u\n", exterr->ee_origin );
            printf("ee_type: %u\n", exterr->ee_type );
            printf("ee_code: %u\n", exterr->ee_code );
            printf("ee_pad: %u\n", exterr->ee_pad );
            printf("ee_info: %u\n", exterr->ee_info );
            printf("ee_data: %u\n", exterr->ee_data );
        }
        ret = -1;
        break;
    default:
        break;
    }
    return ret;
}

int control_msg( struct msghdr *msg )
{
    int ret = 0;
    struct cmsghdr *control_msg = CMSG_FIRSTHDR( msg );
    while( control_msg != NULL ){
        switch( control_msg->cmsg_level ){
        case SOL_IP:
            ret = ip_control_msg( control_msg );
            break;
        default:
            break;
        }
        control_msg = CMSG_NXTHDR( msg, control_msg );
    }
    return ret;
}

int main(int argC, char* arg[])
{
    struct sockaddr_in addr;
    int sockfd, len = 0;    
    int addr_len = sizeof(struct sockaddr_in);      
    char buffer[4096];

    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("socket");
        exit(1);
    }

    fcntl(sockfd,F_SETFL,O_NONBLOCK);

    int val = 1;
    setsockopt(sockfd, SOL_IP, IP_RECVERR,(char*)&val, sizeof(val));

    bzero(&addr, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(UDP_TEST_PORT);
    addr.sin_addr.s_addr = inet_addr(UDP_SERVER_IP);

    int rtn = 0;
    while(1) {
        bzero(buffer, sizeof(buffer));
        sprintf(buffer,"%d",cnt);
        rtn = sendto(sockfd, buffer, strlen(buffer), 0, (struct sockaddr *)&addr, addr_len);
        if(rtn<0){
           printf("%d\n",errno);
        }

        char buf[1024];
        char control_buf[1024];
        struct msghdr msg;
        struct iovec iov = { buf, 1024 };
        memset( &msg, 0, sizeof(msg) );
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
        msg.msg_control = &control_buf;
        msg.msg_controllen = 1024;

        int bread = recvmsg( sockfd, &msg, MSG_ERRQUEUE );
        if( bread == -1 ){
            continue;
        }
        if( control_msg( &msg ) >= 0 )
            printf("successed!\n");
        else
            printf("failed!\n");
    }

    return 0;
}

【问题讨论】:

    标签: c++ c linux sockets udp


    【解决方案1】:

    是的,它可以,并且您的代码可能没有任何问题,但您仍然可以获得 ENOBUFS。因为系统可能会被其他进程超载。看看这个:https://www.unix.com/programming/237027-how-avoid-no-buffer-space-available-c-socket.html

    【讨论】:

      猜你喜欢
      • 2013-05-09
      • 1970-01-01
      • 2015-12-07
      • 1970-01-01
      • 1970-01-01
      • 2020-11-21
      • 1970-01-01
      • 2012-08-02
      • 1970-01-01
      相关资源
      最近更新 更多