nng协议之nng_listen

1. 函数原型:

#include<nng/nng.h>
int nng_listen(nng_socket s, const char *url, nng_listener *lp, int flags)

2. 手册说明(译)

nng_listen()函数创建一个新初始化的nng_listener对象,该对象与套接字s关联,并配置为在url指定的地址进行监听,并启动它。如果lp不为NULL,则新创建的监听器将存储在lp指定的位置。 监听器用于接受远程拨号器发起的链接。flag 暂时没有用到,但是留作将来使用。

这里先贴一下nng_listen的定义:

int nng_listen(nng_socket sid, const char *addr, nng_listener *lp, int flags)
{
    int           rv;
    nni_sock     *s;
    nni_listener *l;

    // 找到对应的 socket 对象
    if ((rv = nni_sock_find(&s, sid.id)) != 0) {
        return (rv);
    }

    // 创建一个新的监听器对象,并与 socket 绑定
    if ((rv = nni_listener_create(&l, s, addr)) != 0) {
        nni_sock_rele(s); // 释放 socket 对象
        return (rv);
    }

    // 启动监听器
    if ((rv = nni_listener_start(l, flags)) != 0) {
        nni_listener_close(l); // 启动失败,关闭监听器
        return (rv);
    }

    // 如果用户提供了 lp 参数,返回监听器的 ID
    if (lp != NULL) {
        nng_listener lid;
        lid.id = nni_listener_id(l);
        *lp    = lid;
    }

    nni_listener_rele(l); // 释放监听器对象
    return (rv);
}

下面开始展开分析:

通常我们调用函数的方式为: nng_listen(sock, "ipc:/aaa", NULL,  0)

整体来说,listen 动作是需要关联到套接字 s 上的,所以第一步是查找套接字,根据 sid.id 获取到结构体类型 nni_sock 的 s 的值。这里贴出nni_sock 的定义:

struct nni_socket {
	nni_list_node s_node;
	nni_mtx       s_mx;
	nni_cv        s_cv;
	nni_cv        s_close_cv;

	uint32_t s_id;
	uint32_t s_flags;
	unsigned s_ref;  // protected by global lock
	void    *s_data; // Protocol private
	size_t   s_size;

	nni_msgq *s_uwq; // Upper write queue
	nni_msgq *s_urq; // Upper read queue

	nni_proto_id s_self_id;
	nni_proto_id s_peer_id;

	nni_proto_pipe_ops s_pipe_ops;
	nni_proto_sock_ops s_sock_ops;
	nni_proto_ctx_ops  s_ctx_ops;

	// options
	nni_duration s_sndtimeo;  // send timeout
	nni_duration s_rcvtimeo;  // receive timeout
	nni_duration s_reconn;    // reconnect time
	nni_duration s_reconnmax; // max reconnect time
	size_t       s_rcvmaxsz;  // max receive size
	nni_list     s_options;   // opts not handled by sock/proto
	char         s_name[64];  // socket name (legacy compat)

	nni_list s_listeners; // active listeners
	nni_list s_dialers;   // active dialers
	nni_list s_pipes;     // active pipes
	nni_list s_ctxs;      // active contexts (protected by global sock_lk)

	bool s_closing; // Socket is closing
	bool s_closed;  // Socket closed, protected by global lock
	bool s_ctxwait; // Waiting for contexts to close.

	nni_mtx          s_pipe_cbs_mtx;
	nni_sock_pipe_cb s_pipe_cbs[NNG_PIPE_EV_NUM];

};

查找到前面创建的套接字结构体之后,接下来会创建 listener  调用函数nni_listener_create() 函数参数分别为

nni_listener  **l       : 二级指针,类型是& nni_listener *

nni_sock  *s           : 前面根据套接字 id 找到的套接字结构体

const char *url_str : 要监听的地址

nni_listener_create (nni_listener **lp,    nni_sock  *s,   const char *url_str) 先不考虑异常导致失败的处理,该函数的处理流程如下:

1. 为url 分配空间并解析  url_str 到局部变量  url 中, 当我传入 url_str 为 “ipc:///tmp/aaa” 的时候,解析效果如下:

url->u_rawurl  [ipc:///tmp/aaa]  /*原始参数*/
url->u_scheme  [ipc]
url->u_userinfo[(null)]
url->u_host    [(null)]
url->u_hostname[(null)]
url->u_port    [(null)]
url->u_path    [/tmp/aaa]
url->u_query   [(null)]
url->u_fragment[(null)]
url->u_requri  [(null)]

2. 根据解析的 url 的 url->u_scheme 从全局变量 sp_tran_list 中查找传输层的信息,其中  sp_tran_list  在init 函数初始化。这里以ipc 模式为例,将会找到传输层的信息为:nni_sp_tran_sys_init();  函数初始化的信息,也就是     nni_sp_ipc_register();  函数进行的初始化。 这里查找的结果是  ipc_tran

static nni_sp_dialer_ops ipc_dialer_ops = {
	.d_init    = ipc_ep_init_dialer,
	.d_fini    = ipc_ep_fini,
	.d_connect = ipc_ep_connect,
	.d_close   = ipc_ep_close,
	.d_getopt  = ipc_dialer_get,
	.d_setopt  = ipc_dialer_set,
};

static nni_sp_listener_ops ipc_listener_ops = {
	.l_init   = ipc_ep_init_listener,
	.l_fini   = ipc_ep_fini,
	.l_bind   = ipc_ep_bind,
	.l_accept = ipc_ep_accept,
	.l_close  = ipc_ep_close,
	.l_getopt = ipc_listener_get,
	.l_setopt = ipc_listener_set,
};

static nni_sp_pipe_ops ipc_tran_pipe_ops = {
	.p_init   = ipc_pipe_init,
	.p_fini   = ipc_pipe_fini,
	.p_stop   = ipc_pipe_stop,
	.p_send   = ipc_pipe_send,
	.p_recv   = ipc_pipe_recv,
	.p_close  = ipc_pipe_close,
	.p_peer   = ipc_pipe_peer,
	.p_getopt = ipc_pipe_get,
};
static nni_sp_tran ipc_tran = {
	.tran_scheme   = "ipc",
	.tran_dialer   = &ipc_dialer_ops,
	.tran_listener = &ipc_listener_ops,
	.tran_pipe     = &ipc_tran_pipe_ops,
	.tran_init     = ipc_tran_init,
	.tran_fini     = ipc_tran_fini,
};

3. 为 nni_listener  结构体分配空间,并进行初始化:

4. 初始化异步操作I/O对象,对  &l->l_acc_aio 和  &l->l_tmo_aio 分别进行初始化,除了简单的赋值,还调用了函数 nni_task_init 对 &l->l_acc_aio->task 进行初始化。初始化后的结果是:

//初始化监听器结构
l->l_url    = url;
l->l_closed = false;
l->l_data   = NULL;
l->l_ref    = 1;
l->l_sock   = s;
l->l_tran   = tran;

l->l_ops = *tran->tran_listener; 这个赋值操作等价于:

l->l_ops = &ipc_tran->tran_listener;

l->l_acc_aio->a_expire = NNI_TIME_NEVER;
l->l_acc_aio->l_timeout = NNG_DURATION_INFINITE;
l->l_acc_aio->a_expire_q = nni_aio_expire_q_list[随机一个];

&l->l_acc_aio->task->task_prep = false;
&l->l_acc_aio->task->task_busy = 0;
&l->l_acc_aio->task->task_cb   = listener_accept_cb;
&l->l_acc_aio->task->arg       = l;
&l->l_acc_aio->task->task_tq   = nni_taskq_systq;


l->l_tmo_aio->a_expire = NNI_TIME_NEVER;
l->l_tmo_aio->l_timeout = NNG_DURATION_INFINITE;
l->l_acc_aio->a_expire_q = nni_aio_expire_q_list[随机一个];

&l->l_tmo_aio->task->task_prep = false;
&l->l_tmo_aio->task->task_busy = 0;
&l->l_tmo_aio->task->task_cb   = listener_accept_cb;
&l->l_tmo_aio->task->arg       = l;
&l->l_tmo_aio->task->task_tq   = nni_taskq_systq;

5. 分配监听器ID,调用函数  nni_id_alloc32(&listeners, &l->id, l )

6. 初始化监听器,添加监听器到套接字

(待续。。。)

7.启动监听器

8.释放监听器对象。

9. 返回 ;

相关推荐

  1. nng协议nng_listen

    2024-07-20 06:44:02       20 阅读
  2. nng协议分析互斥锁pthread_mutexattr_settype函数

    2024-07-20 06:44:02       18 阅读
  3. nng协议nni_posix_resolv_sysinit()系统初始化

    2024-07-20 06:44:02       19 阅读
  4. PyTorch nn.Parameter

    2024-07-20 06:44:02       47 阅读
  5. 【物联网·协议·ZigBee】

    2024-07-20 06:44:02       46 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-20 06:44:02       52 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-20 06:44:02       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-20 06:44:02       45 阅读
  4. Python语言-面向对象

    2024-07-20 06:44:02       55 阅读

热门阅读

  1. 03-Spring AOP中的设计模式

    2024-07-20 06:44:02       16 阅读
  2. Unity如何使摄像机视锥体外的物体不被剔除

    2024-07-20 06:44:02       16 阅读
  3. 微信小程序开发入门指南

    2024-07-20 06:44:02       18 阅读
  4. Azure MySQL资源优化策略

    2024-07-20 06:44:02       15 阅读
  5. IP地址与mac地址的绑定、解绑

    2024-07-20 06:44:02       20 阅读
  6. Go的数据结构与实现【LinkedList】

    2024-07-20 06:44:02       19 阅读
  7. postgres 的WAL日志膨胀的几种原因

    2024-07-20 06:44:02       19 阅读
  8. 桥接模式(Bridge Pattern)

    2024-07-20 06:44:02       19 阅读
  9. Metalog 源码解读

    2024-07-20 06:44:02       13 阅读
  10. 452. 用最少数量的箭引爆气球

    2024-07-20 06:44:02       15 阅读
  11. Python爬虫——1爬虫基础(一步一步慢慢来)

    2024-07-20 06:44:02       11 阅读
  12. 2024.7.19 Ai大模型问答 - 底特律和长春

    2024-07-20 06:44:02       17 阅读