【问题标题】:Select return 0 but data can be read选择返回0但可以读取数据
【发布时间】:2020-03-27 01:00:19
【问题描述】:

我正在尝试理解 Linux 上的 select() 系统调用。为此,我编写了一个将打开服务器和客户端套接字的小程序。客户端套接字将在新创建的线程中创建。客户端将向服务器发送大约 90 字节的数据(只是一个测试字符串)。在从服务器套接字读取之前,我执行了一个 select(),超时为 60 秒。

我现在的问题是:每次选择都会超时。我检查了我的选择调用是否正确(我在调用之前设置了 FD_Set),我确保服务器 fd 已设置并且我选择了足够大的超时,以便正常的调度不会干扰它。然而,由于某种原因,我无法正常工作。

我的代码如下(用于调试,无论 select() 返回什么,我都会阅读):

struct timeval tv = {
    .tv_sec = 60,
    .tv_usec = 0,
};    
printf("[+] Creating server socket!\n");
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) 
{ 
    // set error and exit
} 
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) 
{ 
    // set error and exit
} 
if (bind(server_fd, (struct sockaddr *)&address_block, sizeof(address_block))<0) 
{ 
    // set error and exit
} 

//Create client thread here
pthread_create(&p2, NULL, client_function, NULL);

if (listen(server_fd, 3) < 0) 
{ 
    // set error and exit
} 

if ((new_socket = accept(server_fd, (struct sockaddr *)&address,  (socklen_t*)&addrlen))<0) 
{ 
    // set error and exit
} 

printf("\t[select] Non-Blocking Server fd is: %d\n", server_fd);

//create and set fds_non_blocking set    
FD_ZERO(&fds_non_blocking);
FD_SET(server_fd, &fds_non_blocking);


int select_return = select(server_fd + 1, &fds_non_blocking, NULL, NULL, &tv);

valread = read( new_socket , buffer, 1024); 

(注意:我知道我应该检查 select return 是否 >0 以及设置了哪个 FD)。函数“client_function()”做了类似的事情,然后发送一个字符串。它将休眠 3 秒,并且总是在 select() 启动后发送数据。

执行此程序时,select总是超时,select之后的read总是会收到客户端发送的数据。

任何人都可以在这里看到我的错误吗?我已经将我的实现与其他实现进行了比较(最常见的错误是第一个选择参数未设置为“Server fd + 1”)。任何帮助,将不胜感激!

【问题讨论】:

  • 顺便说一句,在 Linux 上,select() 修改超时以反映未睡觉的时间量。您需要在每次调用 select() 之前重置电视
  • 感谢您的提示!直到现在才知道这一点。关于我的问题的原因有什么想法吗?
  • 你需要将 new_socket 添加到 fds_non_blocking 以便选择检测新套接字上的读取

标签: c linux sockets system-calls


【解决方案1】:

你必须选择new_socket,而不是server_fd

FD_ZERO(&fds_non_blocking);
FD_SET(new_socket, &fds_non_blocking);

int select_return = select(new_socket + 1, &fds_non_blocking, NULL, NULL, &tv);

valread = read( new_socket , buffer, 1024); 

如果你想同时检查两个文件描述符,你可以使用

FD_ZERO(&fds_non_blocking);
FD_SET(new_socket, &fds_non_blocking);
FD_SET(server_fd, &fds_non_blocking);

int maxfd = (server_fd > new_socket)?server_fd+1:new_socket+1;   
int select_return = select(maxfd, &fds_non_blocking, NULL, NULL, &tv);

if (FD_ISSET(new_socket, &fds_non_blocking))
    valread = read( new_socket , buffer, 1024); 
else if (FD_ISSET(server_fd, &fds_non_blocking))
    ; // Accept a new client

否则,您将只寻找要接受的新客户端连接,而不是寻找已接受套接字上的待处理数据。

此外,您必须在每次调用 select() 后重置 timeval 结构。那里的值由内核更新以反映未花费等待的剩余时间。

【讨论】:

  • 谢谢!我显然在这里做了一个复制和粘贴错误。但这解决了它!
猜你喜欢
  • 2014-04-22
  • 2016-04-16
  • 1970-01-01
  • 2018-02-09
  • 2021-10-26
  • 2020-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多