使用select读取和写入相同的套接字(TCP)

Mar*_*Wan 11 c sockets select read-write blocking

我们正在写一个客户端和服务器(我认为是)非常简单的网络通信.多个客户端连接到服务器,然后服务器将数据发送回所有其他客户端.

服务器只是位于阻塞select循环中等待流量,当它到来时,将数据发送到其他客户端.这似乎工作得很好.

问题是客户.在阅读时,它有时会想要写一次.

但是,我发现如果我使用:

 rv = select(fdmax + 1, &master_list, NULL, NULL, NULL);
Run Code Online (Sandbox Code Playgroud)

我的代码将阻塞,直到有新数据要读取.但有时(异步,来自另一个线程)我将在网络通信线程上写入新数据.所以,我希望我的select定期唤醒,让我检查是否有要写的数据,如:

if (select(....) != -1)
{
  if (FD_SET(sockfd, &master_list))
     // handle data or disconnect
  else
     // look for data to write and write() / send() those.
}
Run Code Online (Sandbox Code Playgroud)

我尝试将选择设置为轮询模式(或荒谬的短暂超时):

// 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);
Run Code Online (Sandbox Code Playgroud)

但是发现那时客户端永远不会得到任何传入的数据.

我也尝试将socket fd设置为非阻塞,如:

fcntl(sockfd, F_SETFL, O_NONBLOCK);
Run Code Online (Sandbox Code Playgroud)

但这并没有解决问题:

  1. 如果我的客户端select()没有struct timeval,读取数据是有效的,但它永远不会解除阻止让我查找可写数据.
  2. 如果我的客户端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
}
Run Code Online (Sandbox Code Playgroud)

)

lau*_*ura 12

一切都很好,除了你做的那条线if (FD_SET(sockfd, &master_list)).我有一个非常相似的代码结构,我用过FD_ISSET.您应该测试列表是否已设置,而不是再次设置.除此之外,我什么也看不见.

编辑.另外,我有以下超时:

timeval listening_timeout;
listening_timeout.tv_sec = timeout_in_seconds;
listening_timeout.tv_usec = 0;
Run Code Online (Sandbox Code Playgroud)

也许如果你把它设置为0就会出现问题(你似乎正在做什么?)

EDIT2.我记得当我在退出选择之后并且在我再次输入之前没有清除读取集时,我遇到了一个奇怪的问题.我不得不这样做:

FD_ZERO(&sockfd);
FD_SET(sockfd, &rd);
Run Code Online (Sandbox Code Playgroud)

在我进入之前select.我不记得为什么.


Mic*_*son 7

我似乎记得在网络线程和添加到select调用中的描述符的主线程之间创建和共享读/写文件描述符的技巧.这个fd有一个字节由主线程写入它有什么要发送的时候.写入从select调用中唤醒网络线程,然后网络线程从共享缓冲区中获取数据并将其写入网络,然后在select中返回休眠状态.

对不起,如果这有点模糊和缺乏代码......我的记忆可能不正确..所以其他人可能需要进一步指导你.