当没有数据要读取时,套接字read()会挂起一段时间

时间:2010-05-24 11:45:43

标签: c linux unix sockets

嗨'我正在写一个简单的http端口转发器。我从端口80读取数据,并将数据传递到我的lighttpd服务器,在端口8080上。

只要我在端口8080上的套接字上写()数据(转发请求)没有问题,但是当我从该套接字读取数据(转发响应)时,最后一次读取()会挂起很多(大约1或2秒),然后才意识到没有更多数据并返回0。

我尝试将套接字设置为非阻塞,但是这不起作用,因为有时它会返回EWOULDBLOCKING,即使剩下一些数据(lighttpd + cgi可能很慢)。 我尝试使用select()设置超时,但是,如上所述,当实际传输一些数据时,缓慢的cgi可能会使套接字超时。


更新:已解决。毕竟这是保持活力。在我的lighttpd配置文件中禁用它后,整个过程完美无瑕。

4 个答案:

答案 0 :(得分:2)

嗯,为了完成,并根据我的评论:

HTTP服务器本身(在您的情况下为lighttpd)可能正在维护与代理的持久连接,因为您的代理中继了包含“Connection: keep-alive”的标头。当客户端想要通过同一连接发出多个请求时,此标头会有所帮助。因此,由于lighttpd收到了此标头,因此它假定它将接收更多请求并保持套接字打开,导致read在您的代理中阻塞。

在lighttpd配置中禁用keep-alive是修复它的一种方法,但您也可以在将其中继到Web服务器之前从标题中删除“Connection: keep-alive”。

答案 1 :(得分:1)

使用两个非阻塞套接字 select是正确的方法。返回EWLOULDBLOCK并不意味着整个数据流都已完成接收,这意味着,即时,现在没有什么可读的。这正是你想要的,因为这意味着read不会等待甚至半秒钟才能显示更多数据。如果数据没有立即可用,它将返回。

现在,显然,这意味着您需要多次调用read才能获得完整的数据。执行此操作的一般格式是选择循环。在伪代码中:

do
  select ( my_sockets )

  if ( select error ) 
    handle_error
  else
    for each ( socket in my_sockets ) do
      if ( socket is ready ) then
        nonblocking read from socket
        if ( no data was read ) then
          close socket
          remove socket from my_sockets
        endif
      endif
    loop
  endif
loop

我的想法是select会告诉您哪些套接字有可用于立即阅读的数据。如果您读取其中一个套接字,则可以保证获取数据或获得返回值0,表示远程端关闭了套接字。

如果您使用此方法,您将永远不会陷入无法读取数据的read通话中。阻塞操作是select调用,如果需要编写,也可以选择可写的套接字,如果需要定期执行操作,则设置超时。

答案 2 :(得分:1)

不要那样做!

Keepalives提升其他客户的性能。相反,修复您的客户端。在您的客户端发送Connection: close标头,并确保您的请求未声明HTTP/1.1合规性。 (如果没有其他原因,你可能也不会处理分块编码。)

答案 3 :(得分:0)

我想我会使用非阻塞I / O来完全扩展。而不是设置超时,我宁愿等待事件:

while(select(...)) {
    switch(...) {
    case ...: // Handle accepting new connection
    case ...: // Handle reading from socket
    ...
    }
}

Sinle-thread,阻塞转发器无论如何都会导致多个客户端出现问题。

抱歉 - 我不记得确切的电话。在某些情况下它也可能很奇怪(IIRC - 你需要处理写入),但有些库可以简化任务。