【问题标题】:Communicating result of a method invocation over TCP通过 TCP 传递方法调用的结果
【发布时间】:2015-03-29 12:53:07
【问题描述】:

我想知道如何实现一种“远程方法调用”(以非常简单的形式)。我正在以面向对象的方式编写程序。我有对象存在于树莓派上,而对象存在于我的计算机上。所以有时我想从我的计算机上的一个对象向树莓派上的一个对象发送消息。到目前为止没有什么太难的。但是当我希望该消息有结果时,我被卡住了

例如,如何实现我计算机上的一个对象正在向树莓派发送带有参数 3 和 4 的消息“add”并期待结果 (7)?

我制定了一个协议,通过 TCP 向对象发送消息和参数。但是,如果预期结果,我的 Raspberry Pi 上的对象将不得不通过 TCP 发送新消息来回答。但是我怎么能得到这个答案?

我可以使用/进行阻塞操作,将消息发送到 Raspberry Pi 并等待返回结果。但在模拟程序中,我猜这不是你想要的。

所以我在想如何我可以在不进行阻塞操作的情况下实现它。我想出了这个:

我可以扩展负责通信的对象(通过 TCP)使用“消息表”。在该表中,我可以为每条消息存储 ID、消息本身、目标对象(在树莓派上)以及结果。

所以现在如果我想发送消息“getNumber”(它随机返回一个数字),我将使用消息和目的地调用通信对象。它将用唯一的 ID、消息和目的地填充表。 目标对象会在某个时刻完成计算并返回结果为此,它将通过 TCP 将其答案发送到将结果填充到表中的通信对象。 从那时起,只需传递 ID 即可请求消息的结果。通信对象所要做的就是从他的表中读取结果。

如果不通过 TCP/IP 进行通信,它看起来像:

(destination-object 'get-number) ; Results in a number, with TCP this could not have a result because the result itself also has to be send over TCP.

通过如上所述的通信:

(define id ((communication-object 'send-message) "get-number" "destination-object"))
(define result ((communication-object 'get-result) id))

因为我从未编写过通过 TCP/IP 进行通信的程序,所以我想知道这是否是处理消息及其结果的好方法,或者是否有更好/更简单的方法。

知道我正在用 Racket 编写程序,也许使用带有当前延续的调用是一种更简单的方法来实现这个(如果可能的话)?通过保留“未来”(仍然必须做的事情)直到知道结果。

【问题讨论】:

  • 你到底在问什么?如何进行非阻塞 I/O? (答案:char-ready?byte-ready?)或者您是在问设计客户端/服务器程序的最佳方法是什么,其中远程计算机代表本地计算机进行计算?
  • 我想设计一个非阻塞 I/O。这就是为什么我保留一个消息“表”,所以当我收到答案时(使用字节准备好?知道是否有一些输入)我也把它放在表中。但无论如何,当我向树莓派发送消息时,我将不得不阻止该程序,等待答案。否则我将不得不以异步方式重新设计我的程序。

标签: tcp racket communication


【解决方案1】:

如果您想利用该领域的其他工作,您可以使用 REST API。

这基本上使用了 HTTP 协议的机制,该协议运行在 TCP 之上,是浏览器用来连接网站的主要协议,为主机上的服务提供类似 Web 的 API。返回结果是此架构的标准部分。

它不像 TCP 之上的自定义协议那样轻量级,但另一方面,您将能够利用所有内置的错误和边缘情况处理以及大型用户社区。​​p>

有很多指南可用于 Raspberry 上的 REST:

【讨论】:

  • 谢谢!我会问老师我是否可以使用 REST,但我认为他不会同意(因为使用 TCP 是项目的一部分,也是最困难的部分)。
【解决方案2】:

一种可能的方法是对 Raspberry Pi 的每个请求建立一个连接,并在两端每个连接一个线程。因此,在 PC 上,您的发送消息将被定义为:

(define (send-message message-name . args)
     (let-values ((in-port out-port) (tcp-connect rasp-pi-addr port))
           ; Assuming you're sending Lisp values across the network
           (write (cons message-name args) out-port)
           (let ((result (read in-port)))
               (close-input-port in-port)
               (close-output-port out-port)
               result)))

...然后当您想向 Raspberry Pi 发送请求时,您可以在线程中进行:

(thread (λ () (async-channel-put result-channel
                 ((communication-object 'send-message) "get-number" "destination-object"))))

然后,您的程序的其余部分将在此操作发生时继续运行。你也可以在线程中对结果做任何你想做的事情,而不是让主线程等待结果:

(thread (λ ()
          (update-opponent-position
              ((communication-object 'send-message) "opponent" "make-move"))))

根据您的程序的功能,这可能使您可以最大限度地减少所需的重新设计范围。

需要注意的是,Racket 的线程不是真正的 CPU 线程,因此您不会通过使用处理器的多核来受益。但它们非常适合并行化 I/O 密集型任务。

【讨论】:

  • 谢谢!我可以这样做,但我真的不明白如何在等待输入的同时做其他事情。在发出请求(使用 call/cc)之前,我正在考虑捕捉未来(所以仍然要发生什么)。这样我就可以继续做其他事情,然后当数据可用时调用延续。但这一切变得有点复杂。
  • 您可以在等待输入时执行其他操作,因为输入(以及该输入的“未来”尽可能多地)将在与程序其余部分不同的线程中处理。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多