`
yuky1327
  • 浏览: 125039 次
  • 性别: Icon_minigender_2
  • 来自: 广州
社区版块
存档分类
最新评论

虚拟银行 ErlyBank 与 gen_server

 
阅读更多
转载:http://apps.hi.baidu.com/share/detail/6732177
本文是介绍Erlang/OTP系列文章的第一篇。


场景:银行ErlyBank的服务器开始运行,银行要求它成为可伸缩的系统,以便管理重要的顾客帐户。听说了Elang的强大功能后,银行雇用我们建造这一系统。为了测验我们的能力,银行首先要求我们建个简单的服务器,处理银行帐户开立和注销、存款、取款这几项业务。他们只要软件原型,无须满足实际需求。


结果:我们将使用 gen_server,搭建简单的服务器和客户端,实现银行的要求。因为只是做原型,我们就把帐户资料保存在内存里;帐户只包括用户名一项,不含其他资料。当然,存款、取款时,有确认的手续。


注意:开篇之前,假定你有Erlang语法基本知识。否则,建议你去学学。


什么是 gen_server ?


gen_server 是OTP的一项行为机制,是实现“客户/服务”关系的程序模块。它拥有许多东西,给你自由使用,这点以后再讲。以后,讲到监测器和运行时错误报告时,也会同样用到这个模块。


gen_server 的行为动作包括:


● init/1 - 服务器的初始化;


● handle_call/3 - 处理对服务器的同步调用。调用服务器的客户端被阻塞,直到本函数返回。


● handle_cast/2 - 处理对服务器的异步调用。调用的执行过程中,客户端不被阻塞。


● handle_info/2 - 是起着“收容”作用的函数。服务器收到的信息,如果不是同步调用或异步调用,就交由这个函数处理。例如,如果你的服务器与其他进程相连接,那么,要求退出进程的信息,就是这个函数处理。


● terminate/2 - 关闭服务器时,调用这个函数,做些善后处理。


● code_change/3 - 服务器运行中更新时,调用这个函数。在后面的文章中,会涉及这个函数的大量细节,但你应该至少会按照基本要求使用它。


服务器的基本结构


开写有关 gen_server的文件时,我总是使用通常的结构。你可以在文字编辑器中粘贴这个基本结构:




%%%-------------------------------------------------------------------
%%% File    : eb_server.erl
%%% Author : Mitchell Hashimoto <mitchell.hashimoto@gmail.com>
%%% Description : The ErlyBank account server.
%%%
%%% Created : 5 Sep 2008 by Mitchell Hashimoto <mitchell.hashimoto@gmail.com>
%%%-------------------------------------------------------------------
-module(eb_server).


-behaviour(gen_server).


%% API
-export([start_link/0]).


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


-record(state, {}).
-define(SERVER, ?MODULE).


%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).


%%====================================================================
%% gen_server callbacks
%%====================================================================


%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%%                         {ok, State, Timeout} |
%%                         ignore               |
%%                         {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([]) ->
{ok, #state{}}.


%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%%                                      {reply, Reply, State, Timeout} |
%%                                      {noreply, State} |
%%                                      {noreply, State, Timeout} |
%%                                      {stop, Reason, Reply, State} |
%%                                      {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.


%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%%                                      {noreply, State, Timeout} |
%%                                      {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast(_Msg, State) ->
{noreply, State}.


%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%%                                       {noreply, State, Timeout} |
%%                                       {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info(_Info, State) ->
{noreply, State}.


%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
ok.


%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.


%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------


如你所见,模块名字是eb_derver,意思是“ErlyBank Server”。前面提到的服务器的响应函数,它全部都实现了,并且新增加了一个:


start_link/0。这个函数用于启动服务器:
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).

它调用gen_server的函数start_link/4,启动服务器,向以宏SERVER代表的服务器注册进程。该宏的默认值是本模块名称。其他参数是gen_server模块本身,还有些选择项。因为不必另有选择,它们是空值。gen_server的更多解释,见相关手册。


初始化ErlyBank服务器


调用函数gen_server:start_link之后,它会调用实际服务器的函数init。对实际服务器的初始化状态设置,应该集中在函数init。可以任意设置状态的形式,如原子、函数等等。服务器做出响应时,会返回这些状态。因此,我们要维护一个列表,其中内容是全部帐户及其资金余额。另外,我们要以用户姓名查找这些帐户。为此,我打算使用dict模块,在内存中存储“键-值”数据对。


注意面向对象编程思想:你可以认为服务器状态是其实例化的变量。在每个gen_server的返回对象中,你会得到有效的实例变量,也可以修改它们,等等。


这是我最终的init函数:


init(_Args) ->
{ok, dict:new()}.

它的确很简单!它和ErlyBank的预期返回值之一都是 {ok, State}, 但我只返回ok和空字典来表示服务器状态。我们并不使用init的参数,它是来自start_link的空列表,因而它有个下划线前缀。


同步调用还是异步调用的问题


在我们实现服务器主体之前,我要再次重申同步调用与异步调用的区别。


对服务器的同步调用,会阻塞客户端。这就意味着,客户端向服务器发送消息时,它要一直等待答复。你若需要答复,例如询问某帐户的资金余额是多少,就要用同步调用。


对gen_server的异步调用,属非阻塞或异步的操作方法。也就是说,客户端向服务器发送消息后,不管是否收到答复,它都继续运行。


Erlang保证向进程发送的一切消息,都会收到,所以,除非你明确表示需要服务器的答复,你应该使用异步调用。换句话说,如果你只是简单地要服务器确认收到了消息,你不必发送同步调用给它,因为Erlang保证它会收到消息。(译注:作者米歇尔这里的理解可能错了,应该是不保证收到消息)


开立银行帐户


开立新帐户,是ErlyBank必须做的第一件事。快试试:如果你编写开设银行帐户的程序,那么,你调用服务器是同步还是异步?使劲想想,需要返回什么值?你若回答是同步调用,你对了。通常你要确信帐户已经开立,而非仅仅假定如此。不过,这里我的实现是要使用异步调用,因为现在不检查错误。


首先,我要写API函数,调用模块外的函数创建帐户:


%% --------------------------------------------------------------------
%% Function: create_account(Name) -> ok
%% Description: Creates a bank account for the person with name Name
%% --------------------------------------------------------------------
create_account(Name) ->
gen_server:cast(?SERVER, {create, Name}).

向服务器发送异步调用。服务器是我们在函数start_link中注册的,并以宏?SERVER代表的。发送的请求是个元组{create, Name}。由于是异步调用,会立刻给出“ok”提示,这也是被调用函数要返回的值。


现在,我们需要编写处理异步调用的函数,即处理回复gen_server的值:


handle_cast({create, Name}, State) ->
{noreply, dict:store(Name, 0, State)};
handle_cast(_Msg, State) ->
{noreply, State}.

如你所见,我们增加了函数handle_cast,获取开户请求,接着把它存入字典,数值0表示当前帐户资金余额。函数handle_cast预设返回的值是 {noreply, State} ,其中State是服务器的新状态。这次我们返回的是增加了新帐户的新字典。


还要注意,我加了一个“收容”函数handle_info。虽然这不是个好习惯,但FP编程一般都使这一招法。你可以用这个函数,不动声色地接收消息;否则,程序运行时的意外事件可能总是纠缠着你。


存款业务


我们曾向雇主银行ErlyBank承诺,要编写API处理存款进入帐户的业务,并且还要有基本的确认手续;在存款入帐前,服务器也要核验帐户是否存在。银行不愿让顾客的钱流入黑洞。再次考考自己,同步调用还是异步调用?显然是同步调用。我们必须确信存款的操作成功,并且告知顾客。


照旧,我首先写出API函数:


%% --------------------------------------------------------------------
%% Function: deposit(Name, Amount) -> {ok, Balance} | {error, Reason}
%% Description: Deposits Amount into Name's account. Returns the
%% balance if successful, otherwise returns an error and reason.
%% --------------------------------------------------------------------
deposit(Name, Amount) ->
gen_server:call(?SERVER, {deposit, Name, Amount}).

没什么新鲜的。我们向服务器发送了消息。你该熟悉这些代码,它们与异步调用的几乎一模一样。二者的区别在服务器代码中:


handle_call({deposit, Name, Amount}, _From, State) ->
case dict:find(Name, State) of
    {ok, Value} ->
      NewBalance = Value + Amount,
      Response = {ok, NewBalance},
      NewState = dict:store(Name, NewBalance, State),
      {reply, Response, NewState};
    error ->
      {reply, {error, account_does_not_exist}, State}
end;
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.


哇!不少新鲜货!函数定义看似与handle_cast相同,只是我们没用参数From。From是发送调用的进程的标识(pid),因而,必要时我们可以发送附加消息。


我们答应过ErlyBank,会核验银行帐户的存在与否,首先是我们在何处做这件事。我们试图从状态字典中,找到相关存款帐户的数值。字典的查找函数,返回{ok, Value}或者error。


如果该帐户存在,返回的变量Value就是它的存款余额,于是,我们加上新的存款数额。然后,我们把新的存款余额保存到字典中,并给变量赋值。我还把返回的值存在变量中,这个变量可看做对存款API的注释:{ok, Balance}。接着,返回元组 {reply, Reply, State},服务器把变量Reply返回发出调用的进程,并且保存服务器的新状态。


另一方面,如果该帐户不存在,我们就不改变服务器的状态,但要返回元组 {error, account_does_not_exist}。


增加了处理存款的API后,eb_server.erl 源文件更新如下:


%%%-------------------------------------------------------------------
%%% File    : eb_server.erl
%%% Author : Mitchell Hashimoto <mitchell.hashimoto@gmail.com>
%%% Description : The ErlyBank account server.
%%%
%%% Created : 5 Sep 2008 by Mitchell Hashimoto <mitchell.hashimoto@gmail.com>
%%%-------------------------------------------------------------------
-module(eb_server).


-behaviour(gen_server).


%% API
-export([start_link/0,
        create_account/1,
        deposit/2]).


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


-define(SERVER, ?MODULE).


%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).


%%--------------------------------------------------------------------
%% Function: create_account(Name) -> ok
%% Description: Creates a bank account for the person with name Name
%%--------------------------------------------------------------------
create_account(Name) ->
gen_server:cast(?SERVER, {create, Name}).


%%--------------------------------------------------------------------
%% Function: deposit(Name, Amount) -> {ok, Balance} | {error, Reason}
%% Description: Deposits Amount into Name's account. Returns the
%% balance if successful, otherwise returns an error and reason.
%%--------------------------------------------------------------------
deposit(Name, Amount) ->
gen_server:call(?SERVER, {deposit, Name, Amount}).


%%====================================================================
%% gen_server callbacks
%%====================================================================


%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%%                         {ok, State, Timeout} |
%%                         ignore               |
%%                         {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([]) ->
{ok, dict:new()}.


%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%%                                      {reply, Reply, State, Timeout} |
%%                                      {noreply, State} |
%%                                      {noreply, State, Timeout} |
%%                                      {stop, Reason, Reply, State} |
%%                                      {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call({deposit, Name, Amount}, _From, State) ->
case dict:find(Name, State) of
    {ok, Value} ->
      NewBalance = Value + Amount,
      Response = {ok, NewBalance},
      NewState = dict:store(Name, NewBalance, State),
      {reply, Response, NewState};
    error ->
      {reply, {error, account_does_not_exist}, State}
end;
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.


%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%%                                      {noreply, State, Timeout} |
%%                                      {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast({create, Name}, State) ->
{noreply, dict:store(Name, 0, State)};
handle_cast(_Msg, State) ->
{noreply, State}.


%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%%                                       {noreply, State, Timeout} |
%%                                       {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info(_Info, State) ->
{noreply, State}.


%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
ok.


%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.


%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------


取款与帐户注销


取款与帐户注销的问题,我确实想留作练习,由读者完成该模块的API函数。你若需要dict模块的帮助,可参考它的API:


http://www.erlang.org/doc/man/dict.html。对于取款业务,请注意核验帐户是否存在,以及是否出现透支。你不必处理透支问题。


如果你完成了作业,或者放弃了(但愿没有!),你可以看以下答案。


增加了取款和帐户注销业务API后,eb_server.erl 源文件更新如下:


%%%-------------------------------------------------------------------
%%% File    : eb_server.erl
%%% Author : Mitchell Hashimoto <mitchell.hashimoto@gmail.com>
%%% Description : The ErlyBank account server.
%%%
%%% Created : 5 Sep 2008 by Mitchell Hashimoto <mitchell.hashimoto@gmail.com>
%%%-------------------------------------------------------------------
-module(eb_server).


-behaviour(gen_server).


%% API
-export([start_link/0,
        create_account/1,
        deposit/2,
        withdraw/2,
        delete_account/1]).


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


-define(SERVER, ?MODULE).


%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).


%%--------------------------------------------------------------------
%% Function: create_account(Name) -> ok
%% Description: Creates a bank account for the person with name Name
%%--------------------------------------------------------------------
create_account(Name) ->
gen_server:cast(?SERVER, {create, Name}).


%%--------------------------------------------------------------------
%% Function: deposit(Name, Amount) -> {ok, Balance} | {error, Reason}
%% Description: Deposits Amount into Name's account. Returns the
%% balance if successful, otherwise returns an error and reason.
%%--------------------------------------------------------------------
deposit(Name, Amount) ->
gen_server:call(?SERVER, {deposit, Name, Amount}).


%%--------------------------------------------------------------------
%% Function: withdraw(Name, Amount) -> {ok, Balance} | {error, Reason}
%% Description: Withdraws Amount from Name's account.
%%--------------------------------------------------------------------
withdraw(Name, Amount) ->
gen_server:call(?SERVER, {withdraw, Name, Amount}).


%%--------------------------------------------------------------------
%% Function: delete_account(Name) -> ok
%% Description: Deletes the account with the name Name.
%%--------------------------------------------------------------------
delete_account(Name) ->
gen_server:cast(?SERVER, {destroy, Name}).


%%====================================================================
%% gen_server callbacks
%%====================================================================


%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%%                         {ok, State, Timeout} |
%%                         ignore               |
%%                         {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([]) ->
{ok, dict:new()}.


%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%%                                      {reply, Reply, State, Timeout} |
%%                                      {noreply, State} |
%%                                      {noreply, State, Timeout} |
%%                                      {stop, Reason, Reply, State} |
%%                                      {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call({deposit, Name, Amount}, _From, State) ->
case dict:find(Name, State) of
    {ok, Value} ->
      NewBalance = Value + Amount,
      Response = {ok, NewBalance},
      NewState = dict:store(Name, NewBalance, State),
      {reply, Response, NewState};
    error ->
      {reply, {error, account_does_not_exist}, State}
end;
handle_call({withdraw, Name, Amount}, _From, State) ->
case dict:find(Name, State) of
    {ok, Value} when Value < Amount ->
      {reply, {error, not_enough_funds}, State};
    {ok, Value} ->
      NewBalance = Value - Amount,
      NewState = dict:store(Name, NewBalance, State),
      {reply, {ok, NewBalance}, NewState};
    error ->
      {reply, {error, account_does_not_exist}, State}
end;
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.


%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%%                                      {noreply, State, Timeout} |
%%                                      {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast({create, Name}, State) ->
{noreply, dict:store(Name, 0, State)};
handle_cast({destroy, Name}, State) ->
{noreply, dict:erase(Name, State)};
handle_cast(_Msg, State) ->
{noreply, State}.


%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%%                                       {noreply, State, Timeout} |
%%                                       {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info(_Info, State) ->
{noreply, State}.


%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
ok.


%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.


%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------

结论


我以本文向你展示了gen_server的基本用法,以及如何创建“客户-服务”关系。我没涉及gen_server的每个方面,如接收消息的超时设定和服务器的终止运行,但我解释了这一行为模块的实质性内容。


如果你要学习gen_server更多的知识,如回应函数、可能的返回值等更高级的用法,可以读相关手册:

http://www.erlang.org/doc/man/gen_server.html


还有,我没有触碰并很少提到函数code_change/3,而它的功能却是引人入胜的。别泄气,本系列文章最后几篇,有专门介绍。可以看到使用这个函数,对运行中的系统进行的实时更新。
分享到:
评论

相关推荐

    gen_server tasting 之超简单名称服务(续)

    本篇博客“gen_server tasting 之超简单名称服务(续)”主要探讨了如何使用gen_server来构建一个简单的名称服务系统,这是一个在分布式系统中常见的需求,用于存储和查找服务或资源的名称与地址的对应关系。...

    gen_server tasting 之超简单名称服务

    此外,`gen_server`通常与`gen_event`配合使用,后者提供了事件管理功能,可以用来处理日志、监控和其他系统事件。这样的组合可以让开发者构建出健壮、可扩展的Erlang应用。 在实际开发中,`gen_server`行为被广泛...

    gen_lex_hash_pc

    《gen_lex_hash_pc:MySQL交叉编译的关键工具详解》 在IT行业中,数据库管理系统是核心组件之一,而MySQL作为开源关系型数据库的代表,广泛应用于各类项目中。在特定环境下,如嵌入式设备或资源有限的PC平台,我们...

    Gen_Signature_Android2

    标题"Gen_Signature_Android2"指的是一个特定的工具,它用于生成Android应用的签名,这通常是在发布应用到Google Play或其他第三方市场之前所必需的步骤。这个工具可能是为简化开发者的工作流程而设计的,使得他们...

    基于Erlang的gen_tcp聊天室代码,功能完整

    - 处理连接请求,为每个新连接创建一个新的进程(通常是一个gen_server或gen_fsm行为)。 - 注册和登录逻辑,处理用户认证请求。 - 监听和转发消息,确保消息在正确用户间传递。 - 错误处理和异常恢复,确保系统的...

    gen_server:Erlang 的 gen_server 的(不完整的)OcamlAsync 实现

    《Erlang gen_server在OcamlAsync中的实现探索》 Erlang的gen_server是其并发模型的核心组件,它提供了一种强大的状态管理和错误处理机制。而在OCaml语言中,尽管有着自己的并发库如Async,但直接移植或模仿Erlang...

    [Android][frameworks][HIDL]使用HIDL新建虚拟HAL以实现system_server与native进程双向通信(三)——JAVA客户端

    前言 在上一篇中已经完成了服务端的集成,手机软件此时已经可以自动启动服务端,且运行无异常。 接下来我们就要实现我们... gen_java: true, } 字段定义与实现可在这里查到:/system/tools/hidl/build/hidl_interface.

    Gen_Signature_Android2.zip

    标题“Gen_Signature_Android2.zip”中的"Gen_Signature"指的是生成签名的过程,而"Android2"可能表示这是针对Android平台的第二个版本的工具或方法。这个压缩包文件包含一个名为"Gen_Signature_Android2.apk"的应用...

    Gen_Signature_Android 签名解析工具

    1. **下载与安装**:获取Gen_Signature_Android.apk文件后,将其安装到Android设备或模拟器上。由于这是一款签名解析工具,可能需要绕过安全设置(如开启未知源安装)才能完成安装。 2. **运行工具**:启动应用后,...

    srio_response_gen_srio_gen2_0_srio_gen_srio_reponse_SRIO_gen2_SR

    标题 "srio_response_gen_srio_gen2_0_srio_gen_srio_reponse_SRIO_gen2_SR" 提到的是一个与SRIO(Serial RapidIO)相关的响应生成模块,它可能是一个硬件描述语言(如Verilog或VHDL)设计的源代码文件。SRIO是一种...

    gen_tcp_server:Erlang 应用程序的通用 TCP 服务器

    通用 TCP 服务器 通用 TCP 服务器( gen_tcp_server ) 是一种 Erlang 行为,提供快速简便的方法将 TCP 服务器功能添加到您的应用程序。 它被实现为管理 TCP 连接的主管,因为它是孩子。如何使用它? 运行make来构建。...

    Gen_Signature_Android.apk

    《Android应用签名详解——以Gen_Signature_Android.apk为例》 在移动应用开发领域,尤其是Android系统中,应用的签名是确保软件安全性和完整性的关键环节。本篇文章将详细探讨Android应用签名的重要性、原理以及...

    gen_server tasting 之超简单名称服务(再续)

    在IT行业中,`gen_server` 是Erlang OTP(开放电信平台)框架中的一个核心行为模块,用于构建可靠且容错的服务。它提供了一种模式,使得开发者可以编写并发、状态管理和故障恢复的服务器进程。在"gen_server tasting...

    MKS Gen_L主板 使用手册

    ### MKS Gen_L 主板关键知识点解析 #### 一、简介 MKS Gen_L 主板是针对原有 Ramps1.4 开源主板存在的问题而设计的一款优化产品。它结合了 Arduino 2560 和 Ramps1.4 的功能,旨在提供更为稳定且易于使用的解决方案...

    gen_tags.vim, 用来轻松使用 ctags/gtags的vim和neovim的异步插件.zip

    gen_tags.vim, 用来轻松使用 ctags/gtags的vim和neovim的异步插件 gen_tags.vim 为方便用户使用 Vim/ NeoVim,简化了 ctags/ gtags的使用。它用于为你生成和维护多个平台支持的标签,在 Windows/Linux/macOS. 上测试...

    PyPI 官网下载 | gen_data_model-1.6.1.tar.gz

    gen_data_model库与“zookeeper”、“分布式”、“云原生”和“cloud native”这些标签紧密相关,意味着它可能被设计用于处理分布式系统中的数据模型。Zookeeper是一种分布式协调服务,常用于管理配置信息、命名服务...

    华硕Z87主板BIOS_updater_for_4th_Gen_Intel_Core_CPU.zip

    这个“华硕Z87主板BIOS_updater_for_4th_Gen_Intel_Core_CPU.zip”压缩包就是用于更新该主板BIOS的工具,以确保与最新硬件和软件的兼容性,解决潜在问题,提高系统的稳定性和性能。 BIOS更新通常包含以下好处: 1. ...

Global site tag (gtag.js) - Google Analytics