我需要知道客户端是否连接/断开并处理它。
这是我唯一的想法:
while(!serverStop)
{
fd_set rfds, wfdsBefore, wfdsAfter;
FD_ZERO(&rfds);
FD_SET(serverFd, &rfds);
FD_ZERO(&wfdsBefore);
fillWithClientFds(&wfdsBefore); // clients only listen for messages
wfdsAfter = wfdsBefore;
while(1)
{
select(notimportant, &rfds, &wfdsAfter, NULL, NULL);
if (FD_ISSET(serverFd, &rfds)) // new client appeared
break;
if (doSetsDiffer(&wfdsBefore, &wfdsAfter)) // some client disconnected (doesn't work)
break;
}
// inform connected clients about disconnected ones
}
不仅会出现忙等待,而且这种方法甚至不起作用(尽管客户端关闭了套接字,但wfdsAfter
不会改变)。
有什么办法吗?唯一的要求是不要使用多线程。
CCD_ 2是用CCD_ 3和CCD_。
您应该在连接后将每个客户端文件描述符放置在读取描述符(rfd)集中,并且当文件描述符随后返回为可读时,尝试从套接字读取。
首先,如果您的客户端真的没有发送任何内容(还没有断开连接),那么它的套接字将永远不会被标记为可读。这似乎可以解决你的问题,因为你说客户端实际上从未发送过任何东西:在客户端断开连接之前,它不会被标记为可读。
但是,即使客户端发送数据,只有当有数据可用或客户端已断开连接时,文件描述符才会被标记为可读。然后,您可以通过尝试读取套接字来轻松区分。返回值可以是读取的字节数(如果有数据),也可以是零(如果客户端已断开连接)。
(服务器通常会将O_NONBLOCK选项添加到套接字中,以确保当客户端有数据要发送时得到通知,但希望确保不会阻止等待来自客户端的数据。使用该选项,当客户端断开连接时,read仍然返回0。使用该选项,如果客户端仍在,但没有可用数据,则read调用将返回-1,errno设置为EAGAIN/EWOULDBLOCK.)
我没有解释的另一个细微之处是,可以在一个方向上关闭数据传递,同时允许它在另一个方向继续(如果你关心这一点,请参阅关闭(2))。
您将客户端套接字放在write描述符集中。您需要将它们放在read描述符集中。
当服务器套接字至少有一个挂起的客户端请求时,它是可读的。您可以调用accept()
来接受客户端。
当套接字的入站缓冲区中有数据,或者其连接的对等方已断开连接时,它是可读,而不是可写。您可以调用read()
进行区分。read()
在入站数据时返回>0,在正常断开连接时返回0,在出错时返回-1。
当套接字的出站缓冲区中有可用空间时,它是可写的。如果write()
失败并出现EWOULDBLOCK
错误,则出站缓冲区已满,套接字不再可写。当缓冲区清空一些空间时,套接字将再次变为可写。
此外,select()
会修改传递给它的fdset
s,因此需要在每次循环迭代中重置rfds
。为了避免这种情况,您可以使用(e)poll()
。
所以,你需要更像这样的东西:
fd_set rfds;
while (!serverStop)
{
FD_ZERO(&rfds);
FD_SET(serverFd, &rfds);
fillWithClientFds(&rfds); // clients only listen for messages
if (select(notimportant, &rfds, NULL, NULL, NULL) < 0)
break;
if (FD_ISSET(serverFd, &rfds)) // new client appeared
{
// call accept(), add client to connected list...
}
// clear disconnected list...
for (each client in connected list)
{
if (FD_ISSET(clientFd, &rfds))
{
int nBytes = read(clientFd, ...);
if (nBytes > 0)
{
// handle client data as needed ...
}
else if (nBytes == 0)
{
// add client to disconnected list
}
else
{
// handle error...
// possibly add client to disconnected list...
}
}
}
for (each client in disconnected list)
{
// remove client from connected list...
}
for (each client in disconnected list)
{
// inform connected clients
}
}