我正在尝试使用HTTP1.1协议在Linux下用C编写一个web服务器。我已经为多个请求使用了select,我想实现持久连接,但到目前为止还不起作用,因为我无法正确设置超时。我该怎么做?我认为setsockopt函数:
setsockopt(connsd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv))
其中tv是一个结构timeval。这也不起作用。
有什么建议吗?
SO_RCVTIMEO
只有在实际读取数据时才能工作。select()
不会接受它。select()
在其最后一个参数中使用超时参数。如果您有一个定时器数据结构来组织哪些连接应该按什么顺序超时,那么您可以将最快超时时间传递给select()
。如果返回值为0,则表示发生了超时,您应该终止所有超时的连接。处理完实时连接(并在计时器数据结构中重新设置其空闲超时)后,在再次调用select()
之前,应再次检查是否有任何连接超时。
您可以使用各种数据结构,但流行的数据结构包括定时轮和定时器堆。
定时轮基本上是一个组织为圆形缓冲器的阵列,其中每个缓冲器位置代表一个时间单位。如果轮子的单位是秒,则可以构造一个300元素的数组来表示5分钟的时间。有一个粘性索引,它表示任何计时器最后一次过期的时间,当前位置将是当前时间乘以数组大小的模。要添加超时,请计算它需要超时的绝对时间,将其乘以数组的大小,然后将其添加到该数组位置的列表中。上一个索引和当前位置之间的所有存储桶(其超时时间已达到)都需要过期。条目过期后,最后一个索引将更新到当前位置。为了计算到下一次到期的时间,从当前位置开始扫描存储桶,以找到一个条目将到期的存储桶。
计时器堆基本上是一个优先级队列,其中较早过期的条目比较晚过期的条目具有更高的优先级。非空堆的顶部决定了下一次到期的时间。
如果您的应用程序一直在插入大量计时器,然后一直取消它们,那么轮子可能更合适,因为插入轮子并从轮子中移除比从优先级队列中插入和移除更有效。
最简单的解决方案可能是保留每个连接收到的最后一次请求,然后定期检查该时间,如果时间太长,则关闭连接。