我们正在编写一个客户端和一个服务器来执行(我认为是)非常简单的网络通信。多个客户端连接到服务器,然后服务器应该将数据发送回所有其他客户端。
服务器只是处于阻塞select
循环中等待流量,当流量到来时,将数据发送给其他客户端。这似乎工作得很好。
问题是客户端。为了响应读取,它有时会想要进行写入。
但是,我发现如果我使用:
rv = select(fdmax + 1, &master_list, NULL, NULL, NULL);
我的代码将阻塞,直到有新数据要读取。但有时(从另一个线程异步)我会有新数据要写入网络通信线程。所以,我希望我的选择定期唤醒并让我检查是否有数据要写入,例如:
if (select(....) != -1)
{
if (FD_SET(sockfd, &master_list))
// handle data or disconnect
else
// look for data to write and write() / send() those.
}
我尝试将选择设置为轮询模式(或非常短的超时):
// master list contains the sockfd from the getaddrinfo/socket/connect seq
struct timeval t;
memset(&t, 0, sizeof t);
rv = select(fdmax + 1, &master_list, NULL, NULL, &t);
但发现然后客户端永远不会收到任何传入数据。
我还尝试将套接字 fd 设置为非阻塞,例如:
fcntl(sockfd, F_SETFL, O_NONBLOCK);
但这并不能解决问题:
- 如果我的客户
select()
没有struct timeval
,则读取数据有效,但它永远不会解除阻止让我查找可写数据。 - 如果我的客户
select()
有一个timeval
让它轮询,那么它永远不会发出有要读取的传入数据的信号,并且我的应用程序冻结认为没有建立网络连接(尽管所有其他函数调用都已成功)
关于我可能做错了什么的任何指示?是否不可能在同一个套接字上进行读写(我不敢相信这是真的)。
(编辑:正确的答案,以及我在服务器上而不是在客户端上记得的事情,是有第二个 fd_set,并在每次调用 select() 之前复制 master_list:
// declare and FD_ZERO read_fds:
// put sockfd in master_list
while (1)
{
read_fds = master_list;
select(...);
if (FD_ISSET(read_fds))
....
else
// sleep or otherwise don't hog cpu resources
}
)