【发布时间】:2017-01-22 12:17:19
【问题描述】:
我使用 Erlang :gen_tcp 模块的实现在 Phoenixframwork 中开发了一个 TCP 服务器。
我可以通过调用:gen_tcp.listen(port) 来启动服务器,然后它会侦听此端口上的新连接。
一个客户是药房的自动拣货系统(基本上是自动配药机器人)。
因此,作为 tcp 客户端,机器人能够打开到我的 tcp 服务器的连接。服务器通过handle_info-callback 方法侦听机器人的新消息,并且还能够在此请求中响应客户端(:gen_tcp.send)。
我面临的问题是我不知道如何在没有客户端请求的情况下使用此连接并将数据发送回机器人。
由于机器人是 tcp 客户端(机器人背后的公司表示目前无法让机器人充当服务器),因此我没有可以向其发送消息的开放端口/机器人服务器地址。所以我必须使用客户端初始化的已经建立的连接。
设置
pharmacy_ui > pharmacy_api(凤凰)> 机器人(供应商软件)
工作流程:
- 机器人通过 tcp 初始化与 api 的连接
- 机器人向api发送状态信息并得到响应
- 在某个时间点(参见更新 1),api 必须向机器人发送分配请求(通过使用在 #1 中初始化的连接)
第 1 步和第 2 步有效,第 3 部分无效。
这看起来是关于 Elixir/Phoenix 中 tcp 连接的一个相当简单的问题,但任何正确方向的提示都非常感谢 :)
到目前为止,我想出了这个实现(基于这个blog post):
defmodule MyApi.TcpServerClean do
use GenServer
defmodule State do
defstruct port: nil, lsock: nil, request_count: 0
end
def start_link(port) do
:gen_server.start_link({ :local, :my_api }, __MODULE__, port, [])
end
def start_link() do
start_link 9876 # Default Port if non provided at startup
end
def get_count() do # test call from my_frontend
:gen_server.call(:my_api, :get_count)
end
def stop() do
:gen_server.cast(:my_api, :stop)
end
def init (port) do
{ :ok, lsock } = :gen_tcp.listen(port, [{ :active, true }])
{ :ok, %State{lsock: lsock, port: port}, 0 }
end
def handle_call(:get_count, _from, state) do
{ :reply, { :ok, state.request_count }, state }
end
def handle_cast(:stop , state) do
{ :noreply, state }
end
# handles client tcp requests
def handle_info({ :tcp, socket, raw_data}, state) do
do_rpc(socket, raw_data) # raw_data = data from robot
{ :noreply, %{ state | request_count: state.request_count + 1 } } # count for testing states
end
def handle_info(:timeout, state) do
{ :ok, _sock } = :gen_tcp.accept state.lsock
{ :noreply, state }
end
def handle_info(:tcp_closed, state) do
# do something
{ :noreply, state }
end
def do_rpc(socket, raw_data) do
try do
# process data from robot and do something with it
resp = "My tcp server response ..." # test
:gen_tcp.send(socket, :io_lib.fwrite(resp, []))
catch
error -> :gen_tcp.send(socket, :io_lib.fwrite("~p~n", [error]))
end
end
end
更新 1:
在某个时刻 = 用户(例如药剂师)在 ui 前端下订单。 Frontend 触发一个 post 到 api 并且 api 处理 OrderController 中的 post。 OrderController 必须转换订单(以便机器人理解它)并将其传递给持有与机器人连接的 TcpServer。此工作流程每天会发生多次。
【问题讨论】:
-
你能举一个“在某个时候”的例子吗?谁会触发这个动作?一个简短的回答是,如果您希望
TcpServerClean能够同时处理多个客户端,您可以生成一个进程(如不同的GenServer)来存储套接字,等待触发器,然后发送响应. -
@Dogbert:我更新了我的问题,以举例说明“在某个时候”可能是什么。如果在我更新后它仍然有效,你介意并给出一个简短的例子来回答吗?感谢您的帮助!
标签: sockets tcp elixir phoenix-framework gen-tcp