zmq源码分析之session

文章目录

      • [一、Session 的本质](#一、Session 的本质)
      • [二、Session 的核心结构](#二、Session 的核心结构)
      • [三、Session 的生命周期](#三、Session 的生命周期)
        • [**1. 创建阶段**](#1. 创建阶段)
        • [**2. 启动阶段**](#2. 启动阶段)
        • [**3. 连接建立阶段**](#3. 连接建立阶段)
        • [**4. 消息转发阶段**](#4. 消息转发阶段)
        • [**5. 错误处理与重连**](#5. 错误处理与重连)
        • [**6. 终止阶段**](#6. 终止阶段)
      • [四、Session 与 Socket 的对比](#四、Session 与 Socket 的对比)
      • [五、Session 的状态机](#五、Session 的状态机)
      • [六、特殊 Session 类型](#六、特殊 Session 类型)
        • [**1. req_session_t**](#1. req_session_t)
        • [**2. hello_msg_session_t**](#2. hello_msg_session_t)
      • [七、ZAP 认证机制](#七、ZAP 认证机制)
      • 八、总结

一、Session 的本质

Session 是 ZeroMQ 中管理单个网络连接的生命周期对象,位于 I/O 线程中,负责:

  • 网络连接的建立和维护
  • 协议握手和认证
  • 自动重连
  • 与 socket 之间的消息中转

二、Session 的核心结构

cpp 复制代码
class session_base_t : public own_t,            // 对象生命周期管理
                       public io_object_t,      // I/O 事件处理
                       public i_pipe_events     // 管道事件监听
{
    // === 核心成员变量 ===
    
    const bool _active;           // 是否主动连接(connect 创建 vs bind 创建)
    pipe_t *_pipe;                // 与 socket 通信的管道
    pipe_t *_zap_pipe;            // ZAP 认证管道(可选)
    i_engine *_engine;            // 协议引擎(TCP/IPC/WS 等)
    socket_base_t *_socket;       // 所属的 socket
    io_thread_t *_io_thread;      // 所属的 I/O 线程
    address_t *_addr;             // 连接地址信息
    
    bool _pending;                // 是否等待消息发送完成
    bool _incomplete_in;          // 是否有未完成的多部分消息
    bool _has_linger_timer;       // linger 定时器是否运行
};

三、Session 的生命周期

1. 创建阶段
cpp 复制代码
// socket_base.cpp: connect_internal (TCP 模式)
// 第 1104 行
session_base_t *session = session_base_t::create (
    io_thread,    // I/O 线程
    true,         // active=true 表示主动连接
    this,         // socket 指针
    options,      // 配置选项
    paddr         // 地址信息
);

// session_base.cpp: create() 第 28-87 行
session_base_t *s = NULL;
switch (options_.type) {
    case ZMQ_REQ:
        s = new req_session_t (...);  // 特殊 socket 类型有特殊 session
        break;
    // ... 其他类型
    default:
        s = new session_base_t (...);  // 普通 session
}
2. 启动阶段
cpp 复制代码
// 第 1 步:socket 发送 plug 命令给 session
send_plug (session);  // 通过命令队列

// 第 2 步:session 收到 plug 命令
// session_base.cpp: process_plug() 第 822-826 行
void zmq::session_base_t::process_plug ()
{
    if (_active)
        start_connecting (false);  // 开始连接
}

// 第 3 步:创建 connecter
// session_base.cpp: start_connecting() 第 585-760 行
void zmq::session_base_t::start_connecting (bool wait_)
{
    io_thread_t *io_thread = choose_io_thread (options.affinity);
    
    // 根据协议类型创建不同的 connecter
    if (_addr->protocol == protocol_name::tcp) {
        connecter = new tcp_connecter_t (io_thread, this, options, _addr, wait_);
    }
    // ... IPC, WS, TIPC 等
    
    launch_child (connecter);  // 启动 connecter
}
3. 连接建立阶段
复制代码
TCP 连接建立流程:

Connecter                         Session                      Engine
  |                                 |                            |
  |-- TCP connect() --------------->|                            |
  |                                 |                            |
  |-- 创建 engine ----------------->|                            |
  |                                 |                            |
  |                                 |-- process_attach(engine) ->|
  |                                 |                            |
  |                                 |                            |-- engine->plug()
  |                                 |                            |   注册到轮询器
  |                                 |                            |
  |                                 |-- engine_ready() --------->|
  |                                 |                            |
  |                                 |-- 创建 pipe -------------->|
  |                                 |                            |
  |                                 |-- send_bind(socket) ------->|
  |                                 |                            |
  v                                 v                            v

关键代码

cpp 复制代码
// session_base.cpp: process_attach() 第 382-393 行
void zmq::session_base_t::process_attach (i_engine *engine_)
{
    zmq_assert (engine_ != NULL);
    zmq_assert (!_engine);
    _engine = engine_;
    
    if (!engine_->has_handshake_stage ())
        engine_ready ();  // 无需握手,直接就绪
    
    // 将 engine 插入到 I/O 线程的轮询器
    _engine->plug (_io_thread, this);
}

// session_base.cpp: engine_ready() 第 395-425 行
void zmq::session_base_t::engine_ready ()
{
    // 创建与 socket 通信的管道
    if (!_pipe && !is_terminating ()) {
        object_t *parents[2] = {this, _socket};
        pipe_t *pipes[2] = {NULL, NULL};
        
        int hwms[2] = {options.rcvhwm, options.sndhwm};
        bool conflates[2] = {conflate, conflate};
        
        const int rc = pipepair (parents, pipes, hwms, conflates);
        
        // 设置事件接收器
        pipes[0]->set_event_sink (this);
        _pipe = pipes[0];
        
        // 设置端点对
        pipes[0]->set_endpoint_pair (_engine->get_endpoint ());
        pipes[1]->set_endpoint_pair (_engine->get_endpoint ());
        
        // 让 socket 附加到管道的远端
        send_bind (_socket, pipes[1]);
    }
}
4. 消息转发阶段

从 Socket 到网络(发送)

cpp 复制代码
// 1. Socket 写入消息到 pipe
socket->send(msg)
  ↓
pipe_t::write(msg)  // 写入无锁队列
  ↓
pipe_t::flush()
  ↓
send_activate_read(peer)  // 发送命令到 session

// 2. Session 收到 activate_read 命令
// session_base.cpp: read_activated() 第 276-296 行
void zmq::session_base_t::read_activated (pipe_t *pipe_)
{
    if (likely (pipe_ == _pipe))
        _engine->restart_output ();  // 通知 engine 可以发送
}

// 3. Engine 从 pipe 读取并发送到网络
// stream_engine_base.cpp: restart_output()
engine->restart_output()
  ↓
in_event() / out_event()  // 轮询器触发
  ↓
_decoder->decode()  // 解码消息
  ↓
socket->send()  // 实际网络发送

从网络到 Socket(接收)

cpp 复制代码
// 1. 网络数据到达,轮询器触发
engine->in_event()
  ↓
_decoder->decode()  // 解码数据
  ↓
session->push_msg(msg)  // 推送到 pipe

// 2. Session 推送到 pipe
// session_base.cpp: push_msg() 第 157-171 行
int zmq::session_base_t::push_msg (msg_t *msg_)
{
    if (_pipe && _pipe->write (msg_)) {
        msg_->init ();  // 重置消息
        return 0;
    }
    errno = EAGAIN;
    return -1;
}

// 3. Pipe 通知 socket
pipe_t::write_activated()
  ↓
socket->read_activated()
  ↓
socket->recv()  // 用户接收
5. 错误处理与重连
cpp 复制代码
// session_base.cpp: engine_error() 第 427-482 行
void zmq::session_base_t::engine_error (bool handshaked_,
                                        error_reason_t reason_)
{
    _engine = NULL;  // 清除 engine
    
    // 清理管道
    if (_pipe) {
        clean_pipes ();
        
        // 发送断开消息(如果配置)
        if (options.can_recv_disconnect_msg)
            _pipe->send_disconnect_msg ();
    }
    
    switch (reason_) {
        case connection_error:
        case timeout_error:
            if (_active) {
                reconnect ();  // 主动连接才重连
                break;
            }
            // FALLTHROUGH
            
        case protocol_error:
            if (_pending)
                _pipe->terminate (false);
            else
                terminate ();  // 终止 session
            break;
    }
}

// session_base.cpp: reconnect() 第 542-583 行
void zmq::session_base_t::reconnect ()
{
    // 处理 hiccup(颠簸)
    if (_pipe && options.immediate == 1) {
        _pipe->hiccup ();  // 通知 socket 连接中断
        _pipe->terminate (false);
        _pipe = NULL;
    }
    
    // 重新启动连接
    if (options.reconnect_ivl > 0)
        start_connecting (true);  // 重新开始连接流程
    else
        send_term_endpoint (_socket, ep);  // 通知 socket 端点终止
}
6. 终止阶段
cpp 复制代码
// session_base.cpp: process_term() 第 484-521 行
void zmq::session_base_t::process_term (int linger_)
{
    // 如果没有管道,立即终止
    if (!_pipe && !_zap_pipe) {
        own_t::process_term (0);
        return;
    }
    
    _pending = true;
    
    if (_pipe != NULL) {
        // 设置 linger 定时器
        if (linger_ > 0) {
            add_timer (linger_, linger_timer_id);
            _has_linger_timer = true;
        }
        
        // 终止管道(等待消息发送完成)
        _pipe->terminate (linger_ != 0);
    }
    
    if (_zap_pipe != NULL)
        _zap_pipe->terminate (false);
}

// session_base.cpp: timer_event() 第 523-533 行
void zmq::session_base_t::timer_event (int id_)
{
    zmq_assert (id_ == linger_timer_id);
    _has_linger_timer = false;
    
    // Linger 超时,强制终止
    zmq_assert (_pipe);
    _pipe->terminate (false);
}

// session_base.cpp: pipe_terminated() 第 240-274 行
void zmq::session_base_t::pipe_terminated (pipe_t *pipe_)
{
    if (pipe_ == _pipe) {
        _pipe = NULL;
        // 取消定时器
        if (_has_linger_timer) {
            cancel_timer (linger_timer_id);
            _has_linger_timer = false;
        }
    }
    
    // 如果所有管道都终止了,继续销毁流程
    if (_pending && !_pipe && !_zap_pipe) {
        _pending = false;
        own_t::process_term (0);
    }
}

四、Session 与 Socket 的对比

特性 Socket Session
数量 1 个(用户创建) N 个(每个连接 1 个)
所属线程 应用线程 I/O 线程
可见性 用户可见 用户不可见
职责 消息路由、多连接管理 单个连接管理
重连 ❌ 不处理 ✅ 自动重连
协议处理 ❌ 不处理 ✅ 通过 engine
ZAP 认证 ❌ 不处理 ✅ 处理
生命周期 用户控制 系统自动控制

五、Session 的状态机

复制代码
┌─────────────────────────────────────────────────────────────────┐
│                       Session 状态机                             │
└─────────────────────────────────────────────────────────────────┘

创建
  ↓
┌──────────────────┐
│  初始状态         │
│  - _engine = NULL │
│  - _pipe = NULL   │
└────────┬─────────┘
         │
         │ process_plug()
         ↓
┌──────────────────┐
│  连接中           │
│  - 创建 connecter  │
│  - TCP connect()  │
└────────┬─────────┘
         │
         │ 连接成功
         ↓
┌──────────────────┐
│  握手阶段         │
│  - 创建 engine    │
│  - 协议握手      │
│  - ZAP 认证        │
└────────┬─────────┘
         │
         │ 握手成功
         ↓
┌──────────────────┐
│  就绪状态         │
│  - engine_ready()│
│  - 创建 pipe      │
│  - 消息转发      │
└────────┬─────────┘
         │
         │ 错误/断开
         ↓
┌──────────────────┐
│  错误处理         │
│  - engine_error()│
│  - 清理管道      │
└────────┬─────────┘
         │
         │ 需要重连?
         ├───────────────┐
         │ 是            │ 否
         ↓               ↓
┌──────────────────┐ ┌──────────────┐
│  重连            │ │  终止        │
│  - reconnect()   │ │  - terminate()│
│  - start_connecting() │ │  - 销毁      │
└──────────────────┘ └──────────────┘

六、特殊 Session 类型

1. req_session_t

用于 REQ socket,处理严格的请求 - 响应模式:

cpp 复制代码
class req_session_t : public session_base_t {
    enum {
        bottom,      // 底部(可以发送请求)
        body,        // 消息体中
        request_id   // 请求 ID
    } _state;
    
    // 确保严格的请求 - 响应顺序
    int push_msg (msg_t *msg_);
};
2. hello_msg_session_t

支持发送 Hello 消息的 session:

cpp 复制代码
class hello_msg_session_t : public session_base_t {
    bool _new_pipe;
    
    // 连接建立后自动发送 hello 消息
    int pull_msg (msg_t *msg_) {
        if (_new_pipe) {
            msg_->init_buffer (options.hello_msg);
            _new_pipe = false;
            return 0;
        }
        return session_base_t::pull_msg (msg_);
    }
};

七、ZAP 认证机制

cpp 复制代码
// session_base.cpp: zap_connect() 第 334-375 行
int zmq::session_base_t::zap_connect ()
{
    // 查找 ZAP socket
    endpoint_t peer = find_endpoint ("inproc://zeromq.zap.01");
    
    // 创建与 ZAP socket 的管道
    object_t *parents[2] = {this, peer.socket};
    pipepair (parents, new_pipes, ...);
    
    _zap_pipe = new_pipes[0];
    _zap_pipe->set_event_sink (this);
    
    send_bind (peer.socket, new_pipes[1]);
}

// 认证流程:
// 1. Engine 收到连接请求
// 2. Session 发送认证请求到 ZAP socket
// 3. ZAP socket 返回认证结果
// 4. Session 通知 engine 认证结果

八、总结

Session 是 ZeroMQ 架构中的关键组件

  1. 承上启下:连接 Socket(应用层)和 Engine(协议层)
  2. 隔离保护:将复杂的网络操作隔离在 I/O 线程
  3. 自动恢复:处理重连、错误恢复等复杂逻辑
  4. 协议抽象:通过 engine 接口支持多种协议
  5. 认证支持:集成 ZAP 认证机制
  6. 生命周期管理:自动管理连接的创建和销毁

设计哲学

  • 单一职责:一个 session 管理一个连接
  • 异步非阻塞:所有操作在 I/O 线程异步完成
  • 命令模式:通过命令队列实现跨线程通信
  • 状态机:清晰的状态转换逻辑
  • 可扩展性:通过继承支持特殊 socket 类型

这种设计使得 ZeroMQ 能够高效、可靠地管理大量并发连接,同时保持简洁的用户 API。

相关推荐
奇妙之二进制1 小时前
zmq源码分析之PUSH/PULL 模式的负载均衡分析
运维·网络·负载均衡
IP老炮不瞎唠2 小时前
什么是Grok?以及如何解决卡顿、风控问题
服务器·网络
淼淼爱喝水2 小时前
eNSP 防火墙 NAT 策略配置(Easy IP/No-PAT/NAPT/ 黑洞路由)
服务器·网络·tcp/ip·ensp·防火墙·nat
小柯博客2 小时前
STM32MP2 RIF资源隔离框架详解:从架构到实践
网络·stm32·单片机·嵌入式硬件·架构·嵌入式·yocto
JackSparrow4142 小时前
彻底理解Java NIO(一)C语言实现 单进程+多进程+多线程 阻塞式I/O 服务器详解
java·linux·c语言·网络·后端·tcp/ip·nio
奇妙之二进制2 小时前
zmq源码分析之signaler_t
linux·服务器·网络
聊点儿技术2 小时前
海外玩家伪装来源? 怎么用IP归属地识别
网络·tcp/ip·游戏·游戏安全·ip地址查询·查ip归属地·海外玩家
汤愈韬2 小时前
防火墙双机热备之VRRP
网络·网络协议·security
阿Y加油吧2 小时前
小林计算机网络・传输篇TCP/UDP|三次握手|四次挥手|可靠传输
网络