对于学校作业,我正在尝试使用Ruby和套接字库创建一个简单的HTTP服务器。
现在,我可以用简单的问候来回应任何连接:
require 'socket'
server = TCPServer.open 2000
puts "Listening on port 2000"
loop {
client = server.accept()
resp = "Hello?"
headers = ["HTTP/1.1 200 OK",
"Date: Tue, 14 Dec 2010 10:48:45 GMT",
"Server: Ruby",
"Content-Type: text/html; charset=iso-8859-1",
"Content-Length: #{resp.length}\r\n\r\n"].join("\r\n")
client.puts headers
client.puts resp
client.close
}
这可以按预期工作。但是,当我让服务器告诉我谁与
连接时puts "Client: #{client.addr[2]}"
并使用Chromium(浏览器)连接到localhost:2000/
(只需一次),我得到:
Client: 127.0.0.1
Client: 127.0.0.1
Client: 127.0.0.1
Client: 127.0.0.1
我认为这是Chromium请求辅助文件,如favicon.ico
,而不是我的脚本做一些奇怪的事情,所以我想调查传入的请求。我用
resp = "Hello?"
行
resp = client.read()
然后重新启动服务器。我对Chromium的请求表示不满,而不是立即回来,它只是挂了。同时,我在服务器输出中得到了输出Client: 127.0.0.1
。我点击了Chromium中的“停止”按钮,然后服务器崩溃了
server.rb:16:in `write': Broken pipe (Errno::EPIPE)
from server.rb:16:in `puts'
from server.rb:16:in `block in <main>'
from server.rb:6:in `loop'
from server.rb:6:in `<main>'
显然,我做错了,因为预期的行为是将传入的请求作为响应发回。
我错过了什么?
答案 0 :(得分:20)
我真的不知道chrome和四个连接,但我会尝试回答你关于如何正确阅读请求的问题。
首先,IO#read
在这种情况下不起作用。根据{{3}},read
没有任何参数读取,直到它遇到EOF,但没有发生这种情况。套接字是一个循环流,您将无法使用该方法读取整个消息,因为套接字没有“完整”消息。你可以使用带整数的读取,比如read(100)
或者其他东西,但 会在某个时候阻塞
基本上,读取套接字与读取文件非常不同。套接字是异步更新的,完全独立于您尝试读取它的时间。如果您请求10个字节,那么在代码中此时,可能只有5个字节可用。使用阻止 IO,read(10)
调用将挂起并等待5个字节可用,或直到连接关闭。这意味着,如果您尝试重复读取10个字节的数据包,在某些时候,它仍会挂起。另一种读取套接字的方法是使用非阻塞IO,但这在你的情况下并不是很重要,而且它本身就是一个很长的话题。
以下是使用阻塞IO访问数据的示例:
loop {
client = server.accept
while line = client.gets
puts line.chomp
break if line =~ /^\s*$/
end
# rest of loop ...
}
gets
方法尝试从套接字读取,直到遇到换行符。这个将在某个时刻发生HTTP请求,因此即使整个消息是逐个传输的,gets
也应该从输出返回一行。 line.chomp
电话会在最终换行符出现时切断。如果读取的行是空的,这意味着已经传输了HTTP标头,我们可以安全地中断循环(当然,您可以将其置于while
条件下)。请求将被转储到已启动服务器的控制台。如果你真的想把它发回浏览器,想法是一样的,你只需要以不同的方式处理这些行:
loop {
client = server.accept
lines = []
while line = client.gets and line !~ /^\s*$/
lines << line.chomp
end
resp = lines.join("<br />")
headers = ["http/1.1 200 ok",
"date: tue, 14 dec 2010 10:48:45 gmt",
"server: ruby",
"content-type: text/html; charset=iso-8859-1",
"content-length: #{resp.length}\r\n\r\n"].join("\r\n")
client.puts headers # send the time to the client
client.puts resp
client.close
}
对于损坏的管道,发生该错误是因为浏览器在read
尝试访问数据时强行断开连接。