【问题标题】:Enable non-blocking socket启用非阻塞套接字
【发布时间】:2012-06-03 10:21:43
【问题描述】:

我有一个用 C/C++ 编写的服务器。 我将连接的包装器设置如下:

    //START WRAPPER
    void Server::init_address(int port)
    {
memset(&(this->serv_addr), 0, sizeof(this->serv_addr));
this->serv_addr.sin_family = AF_INET;
this->serv_addr.sin_port = htons(port);
this->serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    }


    int Server::w_socket()
    {
int retv;
retv = socket(PF_INET, SOCK_STREAM, 0);
//FIXME
//fcntl(retv, F_SETFL, O_NONBLOCK);
if(retv == -1)
{
    std::string err_msg(strerror(errno));
    err_msg = "[socket] " + err_msg;
    throw err_msg;
}
else
{
    int reuse_opt = 1;

    if(setsockopt(retv, SOL_SOCKET, SO_REUSEADDR, &reuse_opt, sizeof(int))==-1)
    {
        perror("setsockopt error");
        exit(1);
    }
    return retv;
}
    }

    void Server::w_bind()
    {
int retv;
retv = bind(this->sock_fd,
        (struct sockaddr*) &(this->serv_addr),
        sizeof(this->serv_addr));

if(retv == -1)
{
    std::string err_msg(strerror(errno));
    err_msg = "[bind] " + err_msg;
    throw err_msg;
}
    }

    void Server::w_listen()
    {
int retv;
retv = listen(this->sock_fd, 3);
if (retv == -1)
{
    std::string err_msg(strerror(errno));
    err_msg = "[listen] " + err_msg;
    throw err_msg;
}
    }

    int Server::w_accept(struct sockaddr_in* client_addr)
    {
int retv;
int socklen = sizeof(sockaddr_in);

retv = accept(this->sock_fd, (struct sockaddr*)client_addr, (socklen_t*)&socklen);
if(retv == -1)
{
    std::string err_msg(strerror(errno));
    err_msg = "[accept] " + err_msg;
    throw err_msg;
}
else
{
    return retv;
}
            }

    int Server::recvtimeout(int s, char *buf, int len, int timeout)
    {
fd_set fds;
int n;
struct timeval tv;
// set up the file descriptor set
FD_ZERO(&fds);
FD_SET(s, &fds);
// set up the struct timeval for the timeout
tv.tv_sec = timeout;
tv.tv_usec = 0;
// wait until timeout or data received
n = select(s+1, &fds, NULL, NULL, &tv);
if (n == 0){
    return -2; // timeout!
}
if (n == -1){
    return -1; // error
}
// data must be here, so do a normal recv()
return recv(s, buf, len, 0);
    }
    // END WRAPPER

我的目标是启用非阻塞套接字模式。 我已经尝试像 Beej 手册所说的那样执行 fcntl(retv, F_SETFL, O_NONBLOCK);,但我收到错误消息:[accept] 资源暂时不可用 解决这个问题的方法是使用 select 函数,但我已经在 recvtimeout 函数中使用了它,就像 Beej guide 所说的那样。

所以,我不知道如何解决这个问题以启用非阻塞套接字模式。

【问题讨论】:

    标签: c sockets nonblocking asyncsocket


    【解决方案1】:

    您收到错误因为套接字是非阻塞的。

    当您收到该错误时,您要么执行繁忙循环(当acceptEWOULDBLOCKEAGAIN 返回-1 时,请检查errno)。另一个推荐的解决方案是使用select 来查看套接字何时可读,然后您可以调用accept

    编辑:如何使用select

    您需要有一个更高级别的事件循环,以检查是否可以读取侦听套接字或连接的套接字。如果监听的套接字是可读的,那么你可以接受一个新的连接,如果连接的套接字是可读的,那么你可以从中读取。

    类似:

    for (;;)
    {
        fd_set readset;
    
        FD_ZERO(&readset); 
        FD_SET(listening_socket, &readset);
        int maxfd = listening_socket;
    
        if (connected_socket >= 0)
        {
            FD_SET(connected_socket, &readset);
            maxfd = max(maxfd, connected_socket);
        }
    
        // NULL timeout (5-th argument) means wait until event
        select(maxfd + 1, &readset, NULL, NULL, NULL);
    
        if (FD_ISSET(listening_socket, &readset))
        {
            accept_new_connection(listening_socket);
        }
    
        if (connected_socket >= 0 && FD_ISSET(connected_socket, &readset))
        {
            if (!read_from_socket(connected_socket))
            {
                close(connected_socket);
                connected_socket = -1;
            }
        }
    }
    

    如果您有多个连接的套接字,请将它们放在一个简单的链表中并在循环中添加/检查它们。关闭时从列表中删除。

    【讨论】:

    • 好吧,我已经检查了 EAGAIN 的 errno,就像这个例子一样,在接受函数上使用 do while 语句,stackoverflow.com/questions/735249/… 我取消了 fcntl(rev, F_SETFL, O_NONBLOCK); 的注释。现在似乎一切正常,但真的是非阻塞的吗?我已经关闭了我的服务器,但它仍然崩溃。关于使用 select 来查看套接字何时可读的第二种解决方案,我该如何使用它?我在 recvtimeout 函数中的选择不足以做到这一点?谢谢。
    • 好的,最后一个问题。就我而言,我可以修改 recvtimeout 功能还是必须使用另一个选择来执行另一个功能?因为当我调用接收时两者都是高电平。
    【解决方案2】:

    在您的解决方案中,您使用select 调用连接套接字,但您没有为listen socket 使用相同的调用。

    如果您已将 listen socket 也更改为非阻塞,则必须在调用 accept 之前使用 select for listen socket

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-31
      • 2013-10-15
      • 1970-01-01
      • 2013-06-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-18
      相关资源
      最近更新 更多