简体   繁体   English

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

[英]Simple HTTP server in Ruby using TCPServer

For a school assignment, I am trying to create a simple HTTP server using Ruby and the sockets library. 对于学校的作业,我试图使用Ruby和套接字库创建一个简单的HTTP服务器。

Right now, I can get it to respond to any connection with a simple hello: 现在,我可以用一个简单的问候来响应任何连接:

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
}

This works as expected. 这按预期工作。 However, when I have the server tell me who just connected with 但是,当我让服务器告诉我谁刚刚与

puts "Client: #{client.addr[2]}"

and use Chromium (browser) to connect to localhost:2000/ (just once), I get: 并使用Chromium(浏览器)连接到localhost:2000/ (仅一次),我得到:

Client: 127.0.0.1
Client: 127.0.0.1
Client: 127.0.0.1
Client: 127.0.0.1

I assume this is Chromium requesting auxiliary files, like favicon.ico , and not my script doing something weird, so I wanted to investigate the incoming request. 我假设这是Chromium请求辅助文件,例如favicon.ico ,而不是我的脚本做得很奇怪,所以我想调查传入的请求。 I replaced the resp = "Hello?" 我替换了resp = "Hello?" line with 符合

resp = client.read()

And restarted the server. 并重新启动服务器。 I resent the request in Chromium, and instead of it coming back right away, it just hung. 我对Chromium中的请求感到不满,它没有立即返回,而是挂了。 Meanwhile, I got the output Client: 127.0.0.1 in my server output. 同时,我在服务器输出中得到输出Client: 127.0.0.1 I hit the "stop" button in Chromium, and then the server crashed with 我点击了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>'

Obviously, I'm doing something wrong, as the expected behavior was sending the incoming request back as the response. 显然,我做错了,因为预期的行为是将传入的请求作为响应发送回去。

What am I missing? 我想念什么?

I don't really know about chrome and the four connections, but I'll try to answer your questions on how to read the request properly. 我不太了解chrome和这四个连接,但是我将尝试回答您有关如何正确读取请求的问题。

First of all, IO#read won't work in this case. 首先, IO#read在这种情况下不起作用。 According to the documentation , read without any parameters reads until it encounters EOF, but nothing like that happens. 根据该文件read不带任何参数读取直到遇到EOF,但没有这样的情况发生。 A socket is an endless stream, you won't be able to use that method in order to read in the entire message, since there is no "entire" message for the socket. 套接字是一个无休止的流,您将无法使用该方法读取整个消息,因为该套接字没有“整个”消息。 You could use read with an integer, like read(100) or something, but that will block at some point anyway. 可以将read与一个整数一起使用,例如read(100)东西,但是无论如何都会阻塞。

Basically, reading a socket is very different from reading a file. 基本上,读取套接字与读取文件有很大不同。 A socket is updated asynchronously, completely independent of the time you try to read it. 套接字是异步更新的,完全独立于您尝试读取它的时间。 If you request 10 bytes, it's possible that, at this point in the code, only 5 bytes are available. 如果您请求10个字节,则代码中的这一点可能只有5个字节可用。 With blocking IO, the read(10) call will then hang and wait until 5 more bytes are available, or until the connection is closed. 如果阻塞了 IO,则read(10)调用将挂起,并等待直到另外5个字节可用,或者直到连接关闭。 This means that, if you try repeatedly reading packets of 10 bytes, at some point, it will still hang. 这意味着,如果您尝试重复读取10个字节的数据包,则在某些时候它仍将挂起。 Another way to read a socket is using non-blocking IO, but that's not very important in your case, and it's a long topic by itself. 读取套接字的另一种方法是使用非阻塞IO,但这在您的情况下不是很重要,它本身就是一个漫长的话题。

So here's an example of how you might access the data by using blocking IO: 因此,这里有一个示例,说明如何使用阻塞IO来访问数据:

loop {
  client = server.accept

  while line = client.gets
    puts line.chomp
    break if line =~ /^\s*$/
  end

  # rest of loop ...
}

The gets method tries to read from the socket until it encounters a newline. gets方法尝试从套接字读取,直到遇到换行符为止。 This will happen at some point for an HTTP request, so even if the entire message is transferred piece by piece, gets should return a single line from the output. 对于HTTP请求,此操作有时发生,因此,即使整个消息是逐段传输的, gets应从输出返回一行。 The line.chomp call will cut off the final newlines if they're present. 如果存在,最后的换行符将被line.chomp调用切断。 If the line read is empty, that means the HTTP headers have been transferred and we can safely break the loop (you can put that in the while condition, of course). 如果读取的行为空,则表示HTTP头已传输,我们可以安全地中断循环(当然,您可以将其置于while条件中)。 The request will be dumped to the console that the server has been started on. 该请求将转储到已启动服务器的控制台。 If you really want to send it back to the browser, the idea's the same, you just need to handle the lines differently: 如果您确实希望将其发送回浏览器,则想法是相同的,您只需要以不同的方式处理行即可:

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
}

As for the broken pipe, that error occurs because the browser forcefully breaks the connection off while read is trying to access data. 至于断开的管道,则会发生此错误,因为在read尝试访问数据时浏览器会强制断开连接。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM