【问题标题】:Reading from a TCPSocket in Ruby从 Ruby 中的 TCPSocket 读取
【发布时间】:2016-11-23 14:24:43
【问题描述】:

此代码向 www.example.com 网站发送两个 HTTP 请求:

require 'socket'

@host = 'www.example.com'
@port = 80
@path = "/"

# Build HTTP request
def request(close=false)
  "GET #{@path} HTTP/1.1\r\nHost: #{@host}#{"\r\nConnection: close" if close}\r\n\r\n"
end

# Build socket
socket = TCPSocket.open(@host,@port)  # Connect to server

# Send request twice via socket
2.times {socket.print request}

以下是我找到的各种阅读回复的方法:

# Method 1: close_write and read
socket.close_write # Without this line, the next line hangs
response = socket.read
puts response.length

# Method 2: send another http request with 'Connection: close' header, then use 'read'
socket.print request(true) # Without this line, the next line hangs
response = socket.read
puts response.length

# Method 3: recv
# puts socket.eof?  # Method fails when I add this line
r1, r2 = socket.recv(1000000), socket.recv(1000000)
puts r1.length, r2.length

# Method 4: IO.select and read_nonblock
puts socket.eof?
# IO.select([socket])  # The code still works without this IO.select...
r1 = socket.read_nonblock(9999999)
IO.select([socket])  # ...but not without this one
r2 = socket.read_nonblock(9999999)
puts r1.length, r2.length
puts socket.eof? # Hangs for ages before returning 'true'

问题:

  1. 方法 1 中的 'socket.close_write' 行究竟是做什么的,为什么该方法必须起作用?
  2. 在方法 2 中,'Connection: close' 标头是否以某种方式实现了与方法 1 中的 'socket.close_write' 行相同的结果?如果不是,它在做什么,为什么需要让其余方法起作用?
  3. 为什么在方法 3 中添加注释行“puts socket.eof?”会导致其余代码挂起?
  4. 在方法 3 中,recv 调用如何以及为什么在 HTTP 响应结束时停止(而不是同时接收下一个响应)?
  5. 为什么方法 4 中的第二个 IO.select 是必须的,而第一个不是?
  6. IO.select 实际上做了什么?
  7. 为什么方法 4 中的最后一行“puts socket.eof?”在返回 true 之前会挂起很长时间?
  8. 是否有一种通用的方法来检查套接字当前期望的响应数量,并从套接字读取该数量的响应,而无需关闭套接字以进行写入?

最后,如果在此处无法回答,是否有一个很好的资源可以让我对以上所有内容有所了解,以及从 TCP 套接字(或一般的网络套接字)读取的一般清晰度?

谢谢。

【问题讨论】:

    标签: ruby sockets tcp


    【解决方案1】:

    socket.close_write 不是必需的。我的意思是您可以通过socket.read 获得套接字返回的内容,但您需要等待一段时间。原因是因为您试图用socket.read 读取整个流。这需要时间。您可以通过以下操作找出套接字返回的内容:

    socket.each_line do |line|
      puts line
    end
    

    顺便说一句,Nagle's algorithm 也让它变慢了。

    close_write 所做的是让客户端半关闭套接字。当服务器端注意到这一点时,它也会关闭它的一侧。然后你可以很快读完。

    或者你可以使用IO::select。正如documentation所说:

    它监视给定的 IO 对象数组,等待一个或多个 IO 对象准备好读取,准备好写入,并分别有未决的异常,并返回一个包含这些 IO 对象数组的数组。如果给定了可选的超时值并且在超时秒内没有 IO 对象准备好,它将返回 nil。

    ready = IO.select([socket], nil, nil, 10)
    if ready
      # do something
    else
      # raise timeout
    end
    

    这里我们传递第一个参数,即对象ready for reading,最后一个参数是你要设置的超时时间。这意味着如果读数在 10 秒内没有准备好,它将返回 nil 然后引发超时错误。

    【讨论】:

    • 谢谢。在我使用 socket.close_write 后,服务器如何“注意到”套接字是半关闭的?该代码实际上做了什么?它会向服务器发送一些东西吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-02-17
    • 1970-01-01
    • 1970-01-01
    • 2021-11-08
    • 1970-01-01
    • 2015-05-28
    • 1970-01-01
    相关资源
    最近更新 更多