使用TCPServer的Ruby中的简单HTTP服务器

Aus*_*yde 8 ruby sockets http tcpserver

对于学校作业,我试图使用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
}
Run Code Online (Sandbox Code Playgroud)

这按预期工作.但是,当我让服务器告诉我谁刚刚连接

puts "Client: #{client.addr[2]}"
Run Code Online (Sandbox Code Playgroud)

并使用Chromium(浏览器)连接localhost:2000/(只需一次),我得到:

Client: 127.0.0.1
Client: 127.0.0.1
Client: 127.0.0.1
Client: 127.0.0.1
Run Code Online (Sandbox Code Playgroud)

我假设这是Chromium请求辅助文件,favicon.ico而不是我的脚本做一些奇怪的事情,所以我想调查传入的请求.我换了resp = "Hello?"一行

resp = client.read()
Run Code Online (Sandbox Code Playgroud)

并重新启动服务器.我对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>'
Run Code Online (Sandbox Code Playgroud)

显然,我做错了,因为预期的行为是将响应请求作为响应发回.

我错过了什么?

And*_*dev 20

我真的不知道chrome和四个连接,但我会尝试回答你关于如何正确阅读请求的问题.

首先,IO#read在这种情况下不起作用.根据文档,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 ...
}
Run Code Online (Sandbox Code Playgroud)

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
}
Run Code Online (Sandbox Code Playgroud)

对于损坏的管道,发生该错误是因为浏览器在read尝试访问数据时强行断开连接.