【问题标题】:non-blocking I/O with select()使用 select() 的非阻塞 I/O
【发布时间】:2013-10-15 09:56:39
【问题描述】:

谁能告诉我为什么下面的代码不起作用?

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>

int main(int argc, char **argv)
{
    int sockfd, i, new = 0, maxfd, nready, on = 1;
    struct sockaddr_in saddr;
    ssize_t nbytes;
    fd_set rfds, master;
    char buffer[BUFSIZ];

    if(-1 == (sockfd = socket(PF_INET, SOCK_STREAM, 0)))
    {
        perror("socket()");
        exit(EXIT_FAILURE);
    }

    if(-1 == (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on))))
    {
        perror("setsockopt()");
        exit(EXIT_FAILURE);
    }

    if(-1 == (fcntl(sockfd, F_SETFD, O_NONBLOCK)))
    {
        perror("fcntl()");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    (void)memset(&saddr, '\0', sizeof(struct sockaddr_in));

    saddr.sin_port = htons(1234);
    saddr.sin_addr.s_addr = htonl(INADDR_ANY);
    saddr.sin_family = AF_INET;

    if(-1 == (bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr))))
    {
        perror("bind()");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    if(-1 == (listen(sockfd, 32)))
    {
        perror("listen()");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    FD_ZERO(&rfds);
    FD_ZERO(&master);
    maxfd = sockfd;
    FD_SET(sockfd, &master);

    printf("maxfd = %d\n", maxfd);

    for(;;)
    {
        (void)printf("select()ing...\n");

        (void)memcpy(&rfds, &master, sizeof(master));

        if(-1 == (nready = select(maxfd+1, &rfds, NULL, NULL, NULL)))
        {
            perror("select()");
            close(sockfd);
            exit(EXIT_FAILURE);
        }

        for(i=0; i<FD_SETSIZE; i++)
        {
            if(FD_ISSET(i, &rfds))
            {
                if(i == sockfd)
                {
                    (void)printf("trying to accept new connection(s)\n");

                    for(;;)
                    {
                        new = accept(sockfd, NULL, NULL);

                        if(new < 0)
                        {
                            if(errno != EWOULDBLOCK)
                            {
                                perror("accept()");
                                exit(EXIT_FAILURE);
                            }

                            /* no incomming connection */
                            break;
                        }

                        FD_SET(new, &master);

                        if(maxfd < new)
                            new = maxfd;

                        (void)printf("%d was added to set\n", new);

                    }

                }

                else
                {
                    for(;;)
                    {
                        (void)printf("trying to read data from ready socket(s)\n");

                        while((nbytes = recv(i, buffer, sizeof(buffer), 0)) > 0)
                            printf("%s", buffer);

                        if(nbytes < 0)
                        {
                            if(nbytes != EWOULDBLOCK)
                            {
                                perror("recv()");
                                exit(EXIT_FAILURE);
                            }

                            break;
                        }

                        if(0 == nbytes)
                        {
                            close(i);
                            break;
                        }

                        FD_CLR (i, &master);
                    }

                }


            }
        }


    }

    return 0;
}

它在 *:1234 上侦听新连接。它构建良好,没有任何错误或警告,但它不能从准备好的描述符中接收数据。我找不到问题的根源,谁能帮帮我?

【问题讨论】:

  • 请不要从零循环到FD_SETSIZE。而是将连接的套接字保存在某个集合中,并仅循环该(有限的)集合。
  • 为什么在每次 recv() 调用后都假设 'buffer' 包含一个 C 风格的以空字符结尾的字符串?

标签: c sockets select nonblocking


【解决方案1】:

问题在于读取循环,当您读取接收到的字节时,但完成后您尝试再次接收,导致recv 阻塞。您需要使套接字非阻塞。不,阻塞/非阻塞状态不是继承自被动监听套接字。

【讨论】:

  • '不,阻塞/非阻塞状态不是从被动监听套接字继承的' - 嘿,我正要评论我认为是的,然后你添加了你的编辑:)
  • 您可以在 Linux 上使用带有 SOCK_NONBLOCK 标志的 accept4() 来避免两步 accept() 和 fcntl()。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-24
  • 2020-05-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多