草庐IT

tcp - Erlang TCP 接受模式

coder 2023-09-19 原文

考虑以下(基于 LYSE 的 sockserv)

%%% The supervisor in charge of all the socket acceptors.
-module(tcpsocket_sup).
-behaviour(supervisor).

-export([start_link/0, start_socket/0]).
-export([init/1]).

start_link() ->
  supervisor:start_link({local, ?MODULE}, ?MODULE, []).

init([]) ->
  {ok, Port} = application:get_env(my_app,tcpPort),
  {ok, ListenSocket} = gen_tcp:listen(
    Port,
    [binary, {packet, 0}, {reuseaddr, true}, {active, true} ]),
  lager:info(io_lib:format("Listening for TCP on port ~p", [Port])),
  spawn_link(fun empty_listeners/0),
  {ok, {{simple_one_for_one, 60, 3600},
    [{socket,
      {tcpserver, start_link, [ListenSocket]},
      temporary, 1000, worker, [tcpserver]}
    ]}}.

start_socket() ->
  supervisor:start_child(?MODULE, []).%,

empty_listeners() ->
  [start_socket() || _ <- lists:seq(1,20)],
  ok.

%%%-------------------------------------------------------------------
%%% @author mylesmcdonnell
%%% @copyright (C) 2015, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 06. Feb 2015 07:49
%%%-------------------------------------------------------------------
-module(tcpserver).
-author("mylesmcdonnell").

-behaviour(gen_server).

-record(state, {
    next,
    socket}).

-export([start_link/1]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, code_change/3, terminate/2]).

-define(SOCK(Msg), {tcp, _Port, Msg}).
-define(TIME, 800).
-define(EXP, 50).

start_link(Socket) ->
  gen_server:start_link(?MODULE, Socket, []).

init(Socket) ->
  gen_server:cast(self(), accept),
  {ok, #state{socket=Socket}}.

handle_call(_E, _From, State) ->
  {noreply, State}.

handle_cast(accept, S = #state{socket=ListenSocket}) ->
  {ok, AcceptSocket} = gen_tcp:accept(ListenSocket),
  kvstore_tcpsocket_sup:start_socket(),
  receive
    {tcp, Socket, <<"store",Value/binary>>} ->
      Uid = kvstore:store(Value),
      send(Socket,Uid);
    {tcp, Socket, <<"retrieve",Key/binary>>} ->
      case kvstore:retrieve(binary_to_list(Key)) of
        [{_, Value}|_] ->
          send(Socket,Value);
        _ ->
          send(Socket,<<>>)
      end;
    {tcp, Socket, _} ->
      send(Socket, "INVALID_MSG")
  end,
  {noreply, S#state{socket=AcceptSocket, next=name}}.

handle_info(_, S) ->
  {noreply, S}.

code_change(_OldVsn, State, _Extra) ->
  {ok, State}.

terminate(normal, _State) ->
  ok;
terminate(_Reason, _State) ->
  lager:info("terminate reason: ~p~n", [_Reason]).

send(Socket, Bin) ->
  ok = gen_tcp:send(Socket, Bin),
  ok = gen_tcp:close(Socket),
  ok.

我不清楚每个 tcpserver 进程是如何终止的?这是泄漏过程吗?

最佳答案

我没有看到任何地方终止拥有进程。

我想你要找的是四种情况:

  • 客户端终止连接(您收到tcp_closed)
  • 连接不稳定(您收到 tcp_error)
  • 服务器收到一条系统消息要终止(当然,这可能只是主管杀死它,或者一条终止消息)
  • 客户端发送一条消息告诉服务器它已完成,您想要做一些清理工作,而不仅仅是对 tcp_closed 使用react。

最常见的情况通常是客户端只是关闭连接,为此你需要这样的东西:

handle_info({tcp_closed, _}, State) ->
    {stop, normal, State};

连接变得奇怪总是有可能的。我想不出任何时候我想要拥有拥有进程或套接字,所以:

%% You might want to log something here.
handle_info({tcp_error, _}, State) ->
    {stop, normal, State};

在任何情况下,如果客户端告诉服务器它已完成并且您需要根据客户端已成功完成某些操作进行清理(也许您打开了应该首先写入的资源,或者打开了待处理的数据库事务,或者其他任何情况) 您会希望从客户端收到一条成功消息,该消息以您的 send/2 方式关闭连接,并返回 {stop, normal, State} 以停止过程。

这里的关键是确保你确定你想要结束连接的情况,要么杀死服务器进程,要么(更好)返回 {stop, Reason, State}

如上所述,如果您希望 send/2 成为一个单一的响应和一个干净的退出(或者实际上,每个 accept 转换都应该导致一个单一的 send/2 然后终止),那么你想要:

handle_cast(accept, S = #state{socket=ListenSocket}) ->
  {ok, AcceptSocket} = gen_tcp:accept(ListenSocket),
  kvstore_tcpsocket_sup:start_socket(),
  receive
    %% stuff that results in a call to send/2 in any case.
  end,
  {stop, normal, S}.

LYSE 演示的案例是连接持续存在并且客户端和服务器之间持续来回的案例。在上面的例子中,你正在处理一个请求,产生一个新的监听器来重新填充监听器池,并且应该退出,因为你没有计划让这个 gen_server 做任何进一步的工作。

关于tcp - Erlang TCP 接受模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28497453/

有关tcp - Erlang TCP 接受模式的更多相关文章

  1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  4. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  5. ruby-on-rails - environment.rb 中设置的常量在开发模式中消失 - 2

    了解Rails缓存如何工作的人可以真正帮助我。这是嵌套在Rails::Initializer.runblock中的代码:config.after_initializedoSomeClass.const_set'SOME_CONST','SOME_VAL'end现在,如果我运行script/server并发出请求,一切都很好。然而,在我的Rails应用程序的第二个请求中,一切都因单元化常量错误而变得糟糕。在生产模式下,我可以成功发出第二个请求,这意味着常量仍然存在。我已通过将以上内容更改为以下内容来解决问题:config.after_initializedorequire'some_cl

  6. ruby - 在好的 Ruby 代码中没有注释是否被认为是可以接受的? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭5年前。Improvethisquestion我审查了一些用Ruby编写的专业代码,没有发现任何评论。代码读起来相当清晰,但没有self记录。我应该期望专业编写的Ruby代码有注释吗?或者,是否有一些Ruby原则认为注释不是必需的?

  7. ruby-on-rails - Ruby rand() 不能接受变量? - 2

    我对此有点困惑。我在RoR项目中的最终目标是从我的数据库中获取单个随机配置文件。我想它应该是这样的:@profile=Profile.find_by_user_id(rand(User.count))它一直抛出错误,因为user_id0不存在,所以我把它的一部分拿出来检查发生了什么:@r=rand(User.count)每次都返回0。发生什么了?我注册了5个假用户和5个相关配置文件来测试这个。如果我将Profile.find_by_user_id(rand(User.count))重写为Profile.find_by_user_id(3)它工作得很好。User.count也在工作。所以

  8. Ruby:标准递归模式 - 2

    我经常迷上ruby​​的一件事是递归模式。例如,假设我有一个数组,它可能包含无限深度的数组作为元素。所以,例如:my_array=[1,[2,3,[4,5,[6,7]]]]我想创建一个方法,可以将数组展平为[1,2,3,4,5,6,7]。我知道.flatten可以完成这项工作,但这个问题是作为我经常遇到的递归问题的一个例子-因此我试图找到一个更可重用的解决方案。简而言之-我猜这种事情有一个标准模式,但我想不出任何特别优雅的东西。任何想法表示赞赏 最佳答案 递归是一种方法,它不依赖于语言。您在编写算法时要考虑两种情况:再次调用函数的情

  9. ruby - 在 Ruby 中查找多个正则表达式匹配的模式和位置 - 2

    这应该是一个简单的问题,但我找不到任何相关信息。给定一个Ruby中的正则表达式,对于每个匹配项,我需要检索匹配的模式$1、$2,但我还需要匹配位置。我知道=~运算符为我提供了第一个匹配项的位置,而string.scan(/regex/)为我提供了所有匹配模式。如果可能,我需要在同一步骤中获得两个结果。 最佳答案 MatchDatastring.scan(regex)do$1#Patternatfirstposition$2#Patternatsecondposition$~.offset(1)#Startingandendingpo

  10. ruby - sinatra 框架的 MVC 模式 - 2

    我想开始使用“Sinatra”框架进行编码,但我找不到该框架的“MVC”模式。是“MVC-Sinatra”模式或框架吗? 最佳答案 您可能想查看Padrino这是一个围绕Sinatra构建的框架,可为您的项目提供更“类似Rails”的感觉,但没有那么多隐藏的魔法。这是使用Sinatra可以做什么的一个很好的例子。虽然如果您需要开始使用这很好,但我个人建议您将它用作学习工具,以对您来说最有意义的方式使用Sinatra构建您自己的应用程序。写一些测试/期望,写一些代码,通过测试-重复:)至于ORM,你还应该结帐Sequel其中(imho

随机推荐