加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > Linux > 正文

选择并接受Linux中的延迟

发布时间:2020-12-13 23:03:26 所属栏目:Linux 来源:网络整理
导读:我创建了一个简单的应用程序,使用select()和accept()接受IPv4 TCP连接. 我使用python脚本来测试它.它按顺序打开100个连接.即: for i in range(100): s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) print s.connect((IP,PORT)) s.send("Testrn")
我创建了一个简单的应用程序,使用select()和accept()接受IPv4 TCP连接.

我使用python脚本来测试它.它按顺序打开100个连接.即:

for i in range(100):
    s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    print s.connect((IP,PORT))
    s.send("Testrn")

我观察到的是我的应用程序在第一个X连接之后被卡在select()中2秒钟.
strace的输出:

1344391414.452208 select(30,[3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29],NULL,NULL) = 1 (in [3])
1344391416.742843 accept(3,NULL)    = 30

我的代码如下.知道我做错了什么吗?

#include <assert.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/select.h>

int
fd_create (void)
{
    int fd;
    int set = true;
    struct sockaddr_in addr;

    fd = socket(AF_INET,SOCK_STREAM,0);

    setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&set,sizeof(set));

    memset(&addr,sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(1999);
    addr.sin_addr.s_addr = INADDR_ANY;

    bind(fd,(struct sockaddr *)&addr,sizeof(addr));

    listen(fd,1024);

    return (fd);
}

int
fd_echo (int fd)
{
    int n;
    char buffer[128 + 1];

    while ((n = recv(fd,buffer,128,0)) > 0);

    return (n);
}

int
main (void)
{
    int listen_fd;
    fd_set working;
    fd_set master;
    int max_fd;
    int i;
    int new_fd;
    int rc;
    int con;

    FD_ZERO(&master);
    listen_fd = fd_create();
    fcntl(listen_fd,F_SETFL,fcntl(listen_fd,F_GETFL) | O_NONBLOCK);

    max_fd = listen_fd;
    printf("%dn",listen_fd);
    FD_SET(listen_fd,&master);
    con = 0;
    for (;;) {
    memcpy(&working,&master,sizeof(fd_set));
    select(max_fd + 1,&working,NULL);

    for (i = 0; i <= max_fd; i++) {
        if (FD_ISSET(i,&working)) {
        if (i == listen_fd) {
            while ((new_fd = accept(i,NULL)) >= 0) {
            fcntl(new_fd,fcntl(new_fd,F_GETFL) | O_NONBLOCK);
            FD_SET(new_fd,&master);
            if (max_fd < new_fd) {
                max_fd = new_fd;
            }
            printf("New connection %d (%d)n",new_fd,++con);
            }
            if ((new_fd == -1) && (errno != EAGAIN && errno != EWOULDBLOCK)) {
            return(0);
            }
        } else {
            rc = fd_echo(i);
            if ((rc == 0) ||
            ((rc == -1) && ((errno != EAGAIN && errno != EWOULDBLOCK)))) {
            close(i);
            FD_CLR(i,&master);
            }
        }
        }
    }
    }
    return (0);
}

解决方法

更新/警告:虽然试图证明这个答案适用,但我发现它可能没有.我跑了测试并且在没有max_fd高于300的情况下得到延迟.我也得到了poll()的延迟.所以我尝试了tcpdump并且有重传.看起来甚至127.0.0.1都可以丢弃数据包,当你快速扔掉它时.在这里留下答案是因为这是一个真正的问题,即使它不是最紧迫的问题.

所以这涉及很多文件描述符,它适用于poll而不是select.有了这些线索,我可以看到解释:你已经超过了FD_SETSIZE限制.

POSIX的官方声明是(指FD_ZERO / FD_SET / FD_CLR / FD_ISSET):

The behavior of these macros is undefined if the fd argument is less than 0 or greater than or equal to FD_SETSIZE,or if fd is not a valid file descriptor,or if any of the arguments are expressions with side-effects.

(从
http://pubs.opengroup.org/onlinepubs/9699919799/functions/select.html)

要真正了解发生了什么,您必须比官方规范更深入地了解fd_set类型的实际实现.它具有分裂的个性.在实现select的内核中,它被视为可变长度的位数组. select的第一个参数用于决定数组的结束位置.如果调用select(2048,…),内核将期望每个非NULL fd_set *指向256字节(2048位)的数组.

但在用户空间中,fd_set是一个固定大小的结构.大小是FD_SETSIZE位,在我的系统上是1024,也可能是你的. FD_SET和其他宏基本上只是对数组元素进行赋值,只是它们有点复杂,因为它们必须处理作为单个位的概念数组元素.因此,如果您的一个文件描述符是1024并且您尝试FD_SET它,那么您已经完成了相同的操作

int array[1024];
array[1024] = 1;

换句话说,你在fd_set之后破坏了内存中的任何内容,导致以后发生奇怪的事情.

有办法解决这个问题.在包含定义fd_set的标头之前,我已经看到了执行#define FD_SETSIZE somebignumber的旧代码.我不知道哪些操作系统有效;我刚尝试过,glibc似乎忽略了它.

更好的可能性是做旧的“struct hack”,你可以在其中分配一个内存大于sizeof的结构,并且额外的内存可以作为结构的最后一个成员的数组中的额外元素使用.

fd_set *rfds = malloc(128+sizeof *foo); /* can hold fds up to FD_SETSIZE+128*8-1 */

当然,你需要记住在完成它之后释放它,并传递rfds而不是& rfds来选择和FD_ *宏,并做你自己的memset而不是FD_ZERO,并希望内核实现没有因为你现在变得非常亲密,所以不要改变.但它现在有效……

使用民意调查实际上可能是正确答案.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读