如何使用select()函数处理服务器中的多个客户端?

时间:2015-03-23 21:19:17

标签: c linux sockets network-programming

是否可以使用select()功能从一个客户端接收数据并同时将数据发送到其他客户端?如何实现超时?如何同时从多个客户端发送和接收数据?我可以理解如何使用fork()来完成,但select()让我感到困惑。

P.S。如果这个问题达不到Stack Overflow的水平,请原谅我,但我没有在select()找到任何好的教程,而且我是socket编程世界的新手。请帮帮我。

2 个答案:

答案 0 :(得分:1)

要在字面上同时执行两个操作,您需要在至少具有两个CPU内核的计算机上运行两个线程(可能但不一定属于不同的进程)。 (在单独的进程中,您不需要显式管理线程;您可以只依赖进程的默认线程。)这不是select()的用途。实际上,select()主要用于避免

select()函数帮助单个进程的[单个线程]通过允许它在任何给定时间识别哪些通道已准备好进行读取和/或写入来有效地为多个通道提供I / O服务。使用select()的程序将待处理的I / O请求排队,或以其他方式知道需要什么I / O,并循环,仅在select()通知目标I / O设备时执行每个操作准备。这样既可以避免在其他人等待服务的情况下阻止I / O到未准备好的设备,也可以通过不断轮询设备来确定它们是否准备就绪来避免浪费CPU。

答案 1 :(得分:1)

  

是否可以从一个客户端接收数据并将数据发送给某些客户端   其他客户端同时使用select()函数?

是(请注意,这与您在send()调用和recv()调用中同时阻止的程序不同 - 这对于单个线程是不可能的,但幸运的是,它没有必要请参阅我对John Bollinger上面的回答的评论

  

如何实施超时?

第一个问题是,为什么要实现超时?高效的服务器不需要依赖超时。

  

如何从同一个客户端发送和接收数据   时间?

将所有套接字设置为非阻塞模式,这样send()和recv()都不会阻塞;而是每个人都会立即回归。这样,与客户端A的网络连接不良将导致服务器长时间阻塞(在recv()或send()调用内)并停止响应客户端B。

完成后,您的程序阻止/等待的唯一位置是在select()调用中。设置你的fd_set参数,以便当你的任何套接字准备好读取时select()返回,并且每当你有数据准备发送到的任何套接字时它都会返回准备就绪的写入。

一旦select()返回,在循环中使用FD_ISSET()来找出哪些套接字有数据可供你recv(),哪些套接字已准备好接受你的一些数据(通过send())。

在每个准备好读取的套接字上调用recv()以从它们获取一些字节的数据。请注意,recv()可能会向您提供小于或等于您要求它的数字的任意数量的字节,甚至可能返回EWOULDBLOCK(如果由于某种原因它没有任何字节可以提供给您)。

在每个可写入套接字上调用send()以向它们发送更多字节。同样,请注意send()可能接受的字节数少于传递给它的字节数(其返回值将告诉您实际从缓冲区复制到内核缓冲区的字节数),并且它也可能返回EWOULDBLOCK(如果由于某种原因)它现在无法接受你的任何字节。)

然后在循环中重复上述所有操作,这就是您的单线程/多客户端服务器的事件循环。请注意,此设计要求您为每个连接保留发送和接收缓冲区以及显式状态变量,以跟踪当前正在发送/接收的消息当前发送/接收的字节数,因此它比替代方案更多的工作(阻塞)模型,阻塞调用允许您使用线程的执行位置来帮助保持该状态;但好处是你可以让一个线程同时为多个连接提供服务而不会相互干扰。