连接流程详解
进程启动时,先使用main_cmdline
解析命令行参数,比如使用-c
就会使用指定路径的配置文件,使用-d
就会启用后台进程。
之后使用fd_core_initialize
初始化核心库。具体会先使用fd_conf_init
初始化配置,比如设置各项的默认值,初始化字典,初始化主事件队列等。之后使用fd_dict_base_protocol
,定义了一些基础的 AVP,例如 Vendor-Id、Host-IP-Address、Supported-Vendor-Id
等,他们都作为字典对象存放在字典中,以供查询。之后是初始化三个队列,分别是fd_g_local
、fd_g_outgoing、fd_g_incoming
,fd_g_local
用于存储需要在本地处理的消息,fd_g_outgoing
用于管理所有待发送到远程对等体的消息,fd_g_incoming
用于存储所有已经接收到但尚未进行进一步处理的消息。之后还有fd_hooks_init、fd_sess_start、fd_p_expi_init。
接着是fd_core_parseconf
解析配置文件,解析的规则由fdd.l
和fdd.y
生成,其会更新fd_g_config
并建立对应的数据结构,其中的ConnectPeer
模块会创建peer
并调用fd_psm_begin
进行启动。
如上图,fd_psm_begin
启动后会创建线程p_psm_th
,线程p_psm_th
会创建线程connect_thr
,connect_thr
会像对端发送sctp
连接,与对端进行sctp
四次握手,成功后将连接cnx
构建成事件放到peer
的事件队列中,并且创建一个线程rcvthr_notls_sctp
来接收消息构建事件并放到连接cnx
的事件队列中,线程rcvthr_notls_sctp
在成功接收一个普通消息后便结束(即接收到的是sctp
的普通消息而非通知,等待的是CEA)。
之后p_psm_th
就能从peer事件队列中取到连接事件并进行处理。具体就是向对端发送CER
,然后将连接的事件队列与peer的事件队列进行同步,收到的CEA
也就能同步到peer的事件队列中,p_psm_th
就能取出CEA
事件进行处理。
p_psm_th
取到CEA
事件后会创建一个rcvthr_notls_sctp
线程来循环接收消息并放到连接的事件队列(peer事件队列)中,之后p_psm_th
就可以从peer中一直取消息事件进行处理。
之后使用fd_rtdisp_init
初始化运行时分发模块,创建三个线程分别负责dispatch_thr
、routing_out_thr
和routing_in_thr
,dispatch_thr
调用 process_thr
函数,并将 msg_dispatch
作为处理函数,fd_g_local
作为消息队列;routing_in_thr
调用 process_thr
函数,使用 msg_rt_in
作为处理函数,fd_g_incoming
作为消息队列;routing_out_thr
调用 process_thr
函数,使用 msg_rt_out
作为处理函数,fd_g_outgoing
作为消息队列。
msg_rt_in
函数负责处理进入的Diameter消息,包括解析消息头、检查路由信息、决定消息是本地处理还是转发,如果是本地处理就将消息放到fd_g_local
中,转发就放到fd_g_outgoing
。它确保请求消息根据路由规则和本地配置被正确地分发,并且响应消息被送回正确的请求者。如果消息不符合协议规范或无法处理,该函数还会处理错误响应。
msg_dispatch
是消息分发处理函数。它解析消息,检查是否有注册的回调函数来处理该消息,并根据回调函数的返回值或消息类型决定如何进一步处理消息。在test_app
中,服务端注册了 ta_tr_cb
函数来处理Test_Request。在处理并构建完应答后,会将应答消息或报错消息发布到fd_g_outgoing
中等待发送。
msg_rt_out
处理Diameter消息路由输出的函数。根据路由规则和对等体状态确定如何转发消息。
接着是调用fd_core_start
启动服务器,fd_core_start
主要调用了fd_servers_start
,fd_psm_start
,core_runner_thread
,core_state_set
。
fd_servers_start
函数的作用是初始化并启动 freeDiameter 守护进程的服务器监听功能。其内部会调用new_serv
新建一个服务实例,还会启用一个serv_th
线程负责监听。该服务会创建一个队列存放客户端的连接请求,并创建一定量的线程运行client_worker
,监听线程会将监听到的连接请求放到服务实例的队列中,client_worker
会负责处理这些连接对象。如图
先创建一个线程rcvthr_notls_sctp
接收一个消息(CER),将其构建成事件放到连接对象的事件队列中,接着client_worker
会去取事件,将事件解析成消息后,判断如果是CER
会交给fd_peer_handle_newCER
处理。
fd_peer_handle_newCER
会先在peer队列中找到该CER
对应的peer,然后将其构建成事件添加到peer的事件队列中,让peer的状态机去处理。
在peer的状态机中,线程p_psm_th
会从peer的事件队列中取出CER
事件处理,成功后会向对端发送CEA
,并且会创建线程rcvthr_notls_sctp
来循环的接收消息,并放到连接的事件队列中(peer事件队列)。
pthread_create(&core_runner, NULL, core_runner_thread, NULL)
创建核心运行器线程,这是一个主要的事件处理线程,负责处理守护进程的主要事件循环,直到接收到关闭信号。在test_app
的例子中,ta_cli_init
注册了ta_cli_test_message
回调函数,当指定的信号被触发时,负责处理信号的线程会向主事件队列中发送事件,在由core_runner_thread
调用对应的回调函数处理。
core_state_set(CORE_RUNNING)
更新内部状态标志,表明核心组件已经启动并运行。用于同步。
之后主线程还会创建负责捕捉处理信号的线程catch_signals
,其会循环捕捉信号,处理后将事件发送到主事件队列中。
从上面的流程可知,freeDiameter的节点是不分客户端和服务端的,每一个节点都具有发起连接和接收连接的功能。假设我们先开启了peer1
,后开启peer2
,从抓到的包中可以看到,CER
是由peer2
发给peer1
相反,如果先开启peer2
,后开启peer1
,CER
则是由peer1
发给peer2