为啥getsockopt optlen 为零?

Posted

技术标签:

【中文标题】为啥getsockopt optlen 为零?【英文标题】:Why getsockopt optlen is zero?为什么getsockopt optlen 为零? 【发布时间】:2021-01-05 19:07:22 【问题描述】:

我使用非阻塞套接字和事件库。我刚刚注意到,当我调用快速连接到我的本地 IP 端口时,getsockopt(fd, SOL_SOCKET, SO_ERROR, &val, &optlen); 的 optlen 随机变为 0。

我尝试制作能够显示问题的更小的代码。我在这段代码中使用了 epoll,但同样的问题也发生在其他事件库中。

#define nconnect_d 500
#define ebuffer_d 64

#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>

struct sockaddr_in initaddr(uint32_t ip, uint16_t port)
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = htonl(ip);
    memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
    return addr;


int getconnectfd(uint32_t sip, uint16_t sport, uint32_t dip, uint16_t dport)
    int fd;
    if((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
        return -1;
    if(setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (int[])1, sizeof(int)) == -1)
        return -1;
    if(fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) == -1)
        return -1;
    struct sockaddr_in saddr = initaddr(sip, sport);
    if(bind(fd, (const struct sockaddr *)&saddr, (socklen_t)sizeof(struct sockaddr_in)) == -1)
        return -1;
    struct sockaddr_in daddr = initaddr(dip, dport);
    if(connect(fd, (const struct sockaddr*)&daddr, (socklen_t)sizeof(struct sockaddr_in)) == -1 && errno != EINPROGRESS)
        return -1;
    return fd;


int epolltouch(int efd, int sfd, uint32_t flag)
    struct epoll_event event;
    event.data.fd = sfd;
    event.events = flag;
    if(epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &event) == -1)
        return -1;
    return 0;


int epollrm(int efd, int sfd)
    if(epoll_ctl(efd, EPOLL_CTL_DEL, sfd, NULL) == -1)
        return -1;
    return 0;


int main()
    int efd = epoll_create1(0);
    assert(efd != -1);
    for(uint32_t i = 0; i < nconnect_d; i++)
        int sfd = getconnectfd(INADDR_ANY, 12420, 0x7f000001, 2048 + i);
        assert(sfd != -1);
        epolltouch(efd, sfd, EPOLLOUT | EPOLLET);
    
    struct epoll_event events[ebuffer_d];
    uint32_t iconnect = nconnect_d;
    while(iconnect)
        int n = epoll_wait(efd, events, ebuffer_d, -1);
        assert(n != -1);
        for(uint32_t i = 0; i < n; i++)
            int evfd = events[i].data.fd;
            int opt, optlen;
            assert(getsockopt(evfd, SOL_SOCKET, SO_ERROR, &opt, &optlen) != -1);
            assert(optlen == sizeof(int));
            switch(opt)
                case 0:
                    /* connection has been established */
                    break;
                
                case ECONNREFUSED:
                case EHOSTUNREACH:
                case ENETUNREACH:
                /* can be more valid case isn't it? */
                
                    /* connection has failed */
                    break;
                
                default:
                    assert(0);
                
            
            iconnect--;
            epollrm(efd, evfd);
            close(evfd);
        
    
    return 0;

当我跑步时;

$ ./a.out
a.out: temp.c:72: main: Assertion `optlen == sizeof(int)' failed.
Aborted (core dumped)

optlen 0 是否意味着套接字因某种原因关闭?之后需要关闭socket吗?

【问题讨论】:

【参考方案1】:

调用getsockopt()需要你向系统提供缓冲区,这意味着你必须在调用之前设置optlen

int opt;
socklen_t optlen = sizeof opt;

... getsockopt(evfd, SOL_SOCKET, SO_ERROR, &opt, &optlen);

这样,getsockopt() 知道允许写入“缓冲​​区”的数量,在这种情况下它只是一个整数,并使用它实际写入的字节数更新 optlen

手册页指出,optlen 是一个值/结果,这意味着您将其设置进去并期望得到另一个值,但 opt 只是一个结果,所以不关心值是什么在是。

EDIT 将 optlen 的类型固定为 socklen_t,h/t 为 prog-fh

【讨论】:

【参考方案2】:

使用getsockopt()时最后一个参数必须是地址 初始化 socklen_t。 在调用之前,您应该使用存储的大小对其进行初始化 获取结果值(此处为sizeof(opt))。 它是按地址传递的,因为如果这个存储空间太大 对于实际结果,然后系统调用将其调整为此 确切的大小。

【讨论】:

以上是关于为啥getsockopt optlen 为零?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 ubiquityIdentityToken 总是为零?

为啥标签的高度为零?

为啥 applicationState 被返回为零?

为啥 searchFetchedResultsController 设置为零?

为啥 AppDelegate 中的窗口为零

为啥我的 UILabel 变量总是为零?