嗨'我正在写一个简单的http端口转发器。我从端口80读取数据,并将数据传递到我的lighttpd服务器,在端口8080上。
只要我在端口8080上的套接字上写()数据(转发请求)没有问题,但是当我从该套接字读取数据(转发响应)时,最后一次读取()会挂起很多(大约1或2秒),然后才意识到没有更多数据并返回0。
我尝试将套接字设置为非阻塞,但是这不起作用,因为有时它会返回EWOULDBLOCKING,即使剩下一些数据(lighttpd + cgi可能很慢)。 我尝试使用select()设置超时,但是,如上所述,当实际传输一些数据时,缓慢的cgi可能会使套接字超时。
更新:已解决。毕竟这是保持活力。在我的lighttpd配置文件中禁用它后,整个过程完美无瑕。
答案 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 - 你需要处理写入),但有些库可以简化任务。