嗨,我正在写一个简单的http端口转发器。 我从端口80读取数据,并将数据传送到我的lighttpd服务器,端口8080上。
只要我在端口8080(转发请求)上写入()数据在套接字上就没有问题,但是当我从该套接字读取()数据(转发响应)时,最后一个read()会挂起很多(大约1或2秒),然后才意识到没有更多的数据并返回0。
我尝试将套接字设置为非阻塞,但是这不起作用,因为即使有一些数据(lighttpd + cgi可能非常慢),它有时也会返回EWOULDBLOCKING。 我尝试用select()设置超时,但是,如上所述,当实际上有一些数据要传输时,慢速cgi可能会超时套接字。
更新:已解决。 毕竟这是保持活力。 在我的lighttpdconfiguration文件中禁用它之后,整个事情运行完美。
那么,为了完成,并根据我的评论:
HTTP服务器本身(你的情况下是lighttpd)可能会维持一个到你的代理的持久连接,因为你的代理中继了一个包含“ Connection: keep-alive
”的头文件。 当客户端想要通过同一个连接发出多个请求时,这个头信息会起作用。 所以,因为lighttpd收到了这个头文件,它认为它会接收到更多的请求,并保持套接字打开,导致read
在你的代理中被阻塞。
在您的lighttpd配置中禁用保持活动是解决这个问题的一种方法,但是也可以在将标题转发到Web服务器之前从标题中Connection: keep-alive
“ Connection: keep-alive
”。
同时使用非阻塞套接字和 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
调用,如果需要写入,还可以选择超出可写入的套接字,如果需要定期执行操作,则可以设置超时。
不要这样做!
Keepalives提高其他客户的表现。 相反,修复你的客户端。 发送Connection: close
在您的客户端Connection: close
标头,并确保您的请求不要求HTTP/1.1
合规性。 (如果没有其他原因,你可能也不处理分块编码)。
我想我会使用非阻塞I / O来完全扩展。 而不是设置超时,我宁愿等待事件:
while(select(...)) { switch(...) { case ...: // Handle accepting new connection case ...: // Handle reading from socket ... } }
Sinle-thread,阻止转发器会给多个客户端造成问题。
对不起 – 我不记得确切的电话。 另外在某些情况下(IIRC – 你需要处理写入)可能会很奇怪,但是有一些库可以简化任务。