gpt4 book ai didi

C 套接字 : FD_ISSET return true with no data waiting

转载 作者:太空宇宙 更新时间:2023-11-04 04:23:15 24 4
gpt4 key购买 nike

作为一个学校项目,我正在实现一个 IRC 服务器,我今天一直被这个问题所困扰。我的服务器使用 select 来选择哪个客户端正在发送数据,然后它从该用户读取一个命令(命令是\r\n 分隔的),解析它并在传递给下一个用户之前执行它。用户可以像这样一次发送多个命令:

"command1\r\ncommand2\r\n"

如果发生这种情况,我希望第一个命令被执行,第二个命令保留在流中,以便在下一次 select() 调用时可以读取它。 (这样,每个用户每个“回合”只执行一个命令)。为此,我为每个客户端设置了一个 FILE *stream,只要连接就保持打开状态。

这非常有效(如果我发送上面指定的双命令,这两个命令会一个接一个地执行)。

问题是在那之后,select() 继续告诉我套接字中有东西要读取(fd 返回 1 的 FD_ISSET),所以我的接收函数被调用为那个 fd 和 getline()它失败了(没有设置 errno)并且它永远这样循环。

我不明白为什么 select 仍然认为套接字中有东西要读取以及为什么 getline 失败而不是阻塞。

有什么想法吗?

编辑:我不允许在这个项目中使用非阻塞套接字或 fork()

“主”循环:

  while (g_run_server)                                                                
{
fd_max = g_socket_fd;
FD_ZERO(fds);
FD_SET(g_socket_fd, fds);
tmp = users;
while (tmp)
{
FD_SET(tmp->fd, fds);
fd_max = (tmp->fd > fd_max ? tmp->fd : fd_max);
tmp = tmp->next;
}
if (select(fd_max + 1, &fds, NULL, NULL, NULL) < 0)
break;
if (FD_ISSET(g_socket_fd, &fds))
accept_new_user(&hdl, &users);
handle_clients(&hdl, &fds);
}

处理和读取客户端输入的函数:

static bool recv_and_execute(t_handle *hdl)
{
char *raw;
size_t len;

len = 0;
raw = NULL;
if (!hdl->sender->stream &&
(hdl->sender->stream = fdopen(dup(hdl->sender->fd), "r")) == NULL)
return (false);
if (getline(&raw, &len, hdl->sender->stream) != -1)
{
printf("Received \"%s\"\n", raw);
parse_cmd(hdl, raw);
exec_cmd(hdl);
}
else
printf("Getline failed %s\n", strerror(errno));
free(raw);
return (true);
}

int handle_clients(t_handle *hdl, fd_set *fds)
{
t_user *tmp;

tmp = *hdl->users;
while (tmp)
{
if (FD_ISSET(tmp->fd, fds))
{
printf("fd %d is ready to be read\n", tmp->fd);
hdl->sender = tmp;
recv_and_execute(hdl);
FD_CLR(tmp->fd, fds);
tmp = tmp->next;
if (hdl->sender->status == DEAD)
del_user(hdl->users, hdl->sender);
}
else
tmp = tmp->next;
}
return (0);
}

这是当我连接一个客户端并发送“USER foo\r\nUSER no bo dy :wa\r\n”时的输出:

fd 4 is ready to be read
Received "NICK foo
"
[DEBUG] Executing "NICK" with params "foo" "(null)" "(null)" "(null)"
[INFO] Nickname successfully changed.
fd 4 is ready to be read
Received "USER no bo dy :wa
"
[DEBUG] Executing "USER" with params "no" "bo" "dy" ":wa"
[INFO] User registered.
fd 4 is ready to be read
Getline failed Success
fd 4 is ready to be read
Getline failed Success
fd 4 is ready to be read
Getline failed Success
fd 4 is ready to be read
Getline failed Success
fd 4 is ready to be read
Getline failed Success
continue like this....

编辑:我根据 alk 的评论编辑了我的接收函数:

static bool     recv_and_execute(t_handle *hdl)                                               
{
char *raw;
size_t len;
ssize_t nread;

len = 0;
raw = NULL;
if (!hdl->sender->stream &&
(hdl->sender->stream = fdopen(dup(hdl->sender->fd), "r")) == NULL)
return (false);
errno = 0;
if ((nread = getline(&raw, &len, hdl->sender->stream)) > 0)
{
printf("Received \"%s\"\n", raw);
parse_cmd(hdl, raw);
exec_cmd(hdl);
}
else if (errno != 0)
printf("getline failed %s\n", strerror(errno));
else {
printf("EOF reached\n");
fclose(hdl->sender->stream);
hdl->sender->stream = NULL;
}
printf("nread = %zd\n", nread);
free(raw);
return (true);
}

所以这一次,当到达 EOF(getline 返回 -1)时,我关闭流并将其设置为 NULL,以便下次在套接字 fd 上选择查找数据时重新打开。但即使当我关闭流时,选择仍然检测到有可用数据并且无限循环继续:/

fd 4 is ready to be read                                                                       
Received "NICK foo^M
"
nread = 10
fd 4 is ready to be read
Received "USER no bo dy :wa^M
"
nread = 19
fd 4 is ready to be read
EOF reached
nread = -1
fd 4 is ready to be read
EOF reached
nread = -1
fd 4 is ready to be read
EOF reached
nread = -1
and so on...

最佳答案

我很确定您使用的 select 有误。我向您展示了一个关于如何使用它的简单代码示例(我不处理很多错误),您可以根据需要对其进行编辑。

/*
* If you read at man select bugs you can see
* that select could return that someone is
* ready but it isn't true
*/
int fd_c;
fd_set rdset;
fd_set set; /*That's the mask you'll use when new requests arrive*/
FD_ZERO(&set); /*Clears the mask*/
FD_SET(g_socket_fd,&set); /*Set the listening socket as ready*/
while(g_run_server){
/*
* YOU MUST INITIALIZATE IT EVERY LOOP
* read @ man select
*/
rdset = set;
if(select((fd_num_max+1),(&rdset),NULL,NULL,NULL) < 0){
perror("Failed on select\n");
exit(EXIT_FAILURE);
}
/*You go through the ready clients in the rdset*/
for(fd=0;fd<=fd_num_max;fd++) {
if(FD_ISSET(fd,&rdset)) { /*If the bit is set*/
if(fd == fd_skt) { /*If it's a new client*/
/*You can handle the new client here or delegete it to someone*/
fd_c=accept(fd_skt,NULL,0); /*File descriptor of new client*/
FD_SET(fd_c,&set);
if(fd_c > fd_num_max) fd_num_max = fd_c;
}else { /*If it's a request from an existing client*/
/*You can handle the new request here or delegete it to someone*/
FD_SET(fd,&set);
}
}
}
}

您还应该这样修改 static bool recv_and_execute(t_handle *hdl):

errno = 0;
if ((nread = getline(&raw, &len, hdl->sender->stream)) != -1){
printf("Received \"%s\"\n", raw);
parse_cmd(hdl, raw);
exec_cmd(hdl);

}else{
if( errno == 0){
printf("EOF reached\n");
fclose(hdl->sender->stream);
hdl->sender->stream = NULL;
}else{
printf("getline failed %s\n", strerror(errno));
exit(EXIT_FAILURE); /*You must handle it in some way, exiting or doing something*/
}
}

关于C 套接字 : FD_ISSET return true with no data waiting,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44245597/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com