通过套接字从光纤发送和读取数据

Send and read data through socket from fiber

试图找出如何通过套接字send/read数据。在远程服务器上,我创建新的 netcat -l 4444 并从本地发送文本数据 echo "test" | netcat remote.host 4444。这总是很好用。

正在尝试重现:

require "socket"

HOST = "remote.host"
PORT = 4444

ch_request = Channel(String).new
ch_response = Channel(String).new

spawn do
  socket = TCPSocket.new(HOST, PORT)
  loop do
    select
    when request = ch_request.receive
      socket << request
      socket.flush
    end

    if response = socket.gets
      ch_response.send response
    end
  end
end

sleep 0.1

ch_request.send "hello"

loop do
  select
  when response = ch_response.receive
    pp response
  end
end

在我的梦中,我将数据发送到通道,从第一个循环读取它然后发送到套接字。同样的方法,但需要从第二个循环读取它的顺序相反。

在实践中,这不会发生。在本地连接后我得到 "test" 并且无法发回任何东西。在远程我可以发送到本地但在本地只得到一次空字符串,之后就没有了。

这种行为是什么意思,如何实现计划?

你没有展示这个,但我想你有第二个实现使用 TCPServer 作为 netcat -l 等价物。

您需要为 reading/writing 使用单独的光纤到套接字和通道。在没有看到服务器的情况下很难判断到底发生了什么,但我想你最终会陷入僵局,双方都在等待另一方或用户的输入,但无法继续实际发送或读取任何内容。换句话说,您将 sending/receiving 部分互锁,要求另一方谨慎反应和相互作用,以免锁定客户端。这显然是一种脆弱的方法。

相反,您应该确保任何光纤在一个循环中不执行超过一个操作。一个从套接字接收并将其转发到通道,第二个从通道接收并将其转发到套接字,第三个从 reader 侧通道接收并打印或执行任何你想做的数据最后一个填充发送者通道。这样,任何操作都无法阻止其他操作之一。当然,其中一根光纤应该只是主程序之一。

在服务器中,您还需要一根光纤来接受客户端连接并为每个连接生成发送方和接收方循环。

最后要注意的是select语句只有一个when分支是没有作用的,可以直接调用。 select 如果您需要在同一个光纤中同时读取或写入多个通道,则很有用,例如,如果您有多个通道提供要发送到套接字的数据,您可以使用 select不让消息被同时写入同一个套接字的两个光纤破坏。 select 的另一个用例是通过超时从通道发送或接收。

谁在寻找类似问题的答案。我想要的最终结果如下所示:

# Open socket for simulate remote server: `netcat -v -4 -l 4444`

require "socket"

HOST = "remote.host"
PORT = 4444

# JFYI: In real life this packed into class and I use class variable instead consts.
TUBE_REQUEST  = Channel(String).new
TUBE_RESPONSE = Channel(String).new

SOCKET = TCPSocket.new(HOST, PORT)

spawn do
  until SOCKET.closed?
    if request = TUBE_REQUEST.receive
      SOCKET << request
      SOCKET.flush
    end
  end
end

spawn do
  until SOCKET.closed?
    if response = SOCKET.gets
      TUBE_RESPONSE.send response
    end
  end
end

sleep 0.1

def receive_response
  TUBE_RESPONSE.receive
end

def send(message, wait_for_response = true)
  TUBE_REQUEST.send message
  receive_response if wait_for_response
end

send("command with response")
send("command with new line and response\n")
send("command without new line and response", false)

它将发送每个命令并等待来自远程的应答(最后一个除外),然后调用下一个命令。