Linux 使用非阻塞套接字接收

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15108122/
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 19:08:22  来源:igfitidea点击:

recv with non-blocking socket

clinuxsocketsnonblockingrecv

提问by user1016711

I am trying to implement non-blocking for socket recvand the problem is that I got an error -1 when there in no data but I expect to get EAGAINerror.

我正在尝试为套接字实现非阻塞,recv问题是当没有数据时我收到错误 -1 但我希望得到EAGAIN错误。

Socket is set definitely to non-blocking state, I checked flags = fcntl(s, F_GETFL, 0)for O_NONBLOCKflag.

套接字绝对设置为非阻塞状态,我检查flags = fcntl(s, F_GETFL, 0)O_NONBLOCK标志。

Thanks a lot in advance!

非常感谢!

#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/ether.h>
#include <unistd.h>
#include <fcntl.h>
#include <asm-generic/errno-base.h>
#include <assert.h>

#define ETH_FRAME_LEN_MY 1400

void print(void *buf, int length)
{
    int i;
        for (i = 0; i < length; ++i)
            putchar(((char *)buf)[i]);
    printf("\n");
}

int main(){

    int flags, s, r, err; 

    struct sockaddr_ll socket_addr;
        char ifName[IFNAMSIZ] = "eth0";

        struct ifreq if_idx;
        struct ifreq if_mac;

    s = socket(PF_PACKET, SOCK_RAW, htons(0x88b6));
    if (s == -1) { perror("socket"); }

    flags = fcntl(s,F_GETFL,0);
    assert(flags != -1);
    fcntl(s, F_SETFL, flags | O_NONBLOCK);

    flags = fcntl(s, F_GETFL, 0);
        if ((flags & O_NONBLOCK) == O_NONBLOCK) {
            printf("it's nonblocking");
        }
        else {
            printf("it's blocking.");
        }

    /* Get the index of the interface to send on */
    memset(&if_idx, 0, sizeof(struct ifreq));
    strncpy(if_idx.ifr_name, ifName, IFNAMSIZ-1);
    if (ioctl(s, SIOCGIFINDEX, &if_idx) < 0)
        {perror("SIOCGIFINDEX");}

        memset(&socket_addr, 0, sizeof(socket_addr));
        socket_addr.sll_ifindex = if_idx.ifr_ifindex;
        socket_addr.sll_protocol = htons(0x88b5);
        socket_addr.sll_family = PF_PACKET;
        socket_addr.sll_pkttype = PACKET_OUTGOING;


        r = bind(s, (struct sockaddr*)&socket_addr,
                        sizeof(socket_addr));
    if ( r < 0) { perror("bind"); }

    void* buffer = (void*)malloc(ETH_FRAME_LEN_MY); /*Buffer for ethernet frame*/
    int length = 0; /*length of the received frame*/ 


    while(1){
        printf("1\n");
        length = recv(s, buffer, ETH_FRAME_LEN_MY, 0);
        printf("2\n");
        if (length < 0)
            {
            if (length == -EAGAIN)
                printf("non-blocking succeeded\n");
            else printf("error code %i\n", length);
            }           
        //printf ("buffer %s\n", buffer);
        print(buffer, length);
    }
    return 0;

}

采纳答案by selbie

You need to check errno, not the length value to get the reason why the socket failed to return data. Also, EWOULDBLOCKis the other error code you should check for in addition to EAGAIN. Change your while loop as follows:

您需要检查errno,而不是长度值以获取套接字无法返回数据的原因。此外,EWOULDBLOCK除了EAGAIN. 更改您的 while 循环如下:

while(1)
{
    int err;
    printf("1\n");
    length = recv(s, buffer, ETH_FRAME_LEN_MY, 0);
    err = errno; // save off errno, because because the printf statement might reset it
    printf("2\n");
    if (length < 0)
    {
       if ((err == EAGAIN) || (err == EWOULDBLOCK))
       {
          printf("non-blocking operation returned EAGAIN or EWOULDBLOCK\n");
       }
       else
       {
          printf("recv returned unrecoverable error(errno=%d)\n", err);
          break;
       }
    }           
    //printf ("buffer %s\n", buffer);
    print(buffer, length);
}

Of course this infinite loop will get into a CPU wasting busy cycle while it's waiting for data. There are a variety of ways to be notified of data arriving on a socket without having to call recv()in a spin loop. This includes selectand pollcalls.

当然,这个无限循环会在等待数据时进入一个 CPU 浪费繁忙的周期。有多种方法可以通知数据到达套接字,而无需调用recv()自旋循环。这包括selectpoll调用。

回答by user207421

I got an error -1 when there in no data but I expect to get EAGAIN error.

当没有数据时,我收到错误 -1,但我希望收到 EAGAIN 错误。

-1 tells you there is an error. errnotells you what the error was.

-1 告诉你有错误。errno告诉你错误是什么。