pub struct ClientConn {Show 15 fields
pub conn_id: u64,
pub session_generation: u64,
pub state: ConnState,
pub aes_key: [u8; 16],
pub aes_encrypt_enabled: bool,
pub proto_fmt_type: ProtoFmtType,
pub last_keepalive: Instant,
pub recv_notify: bool,
pub ai_type: i32,
pub keepalive_count: AtomicU32,
pub tx: Sender<FutuFrame>,
pub key_id: Option<String>,
pub scopes: HashSet<Scope>,
pub allowed_markets: Option<Arc<HashSet<String>>>,
pub allowed_acc_ids: Option<Arc<HashSet<u64>>>,
}Fields§
§conn_id: u64随机连接 ID(对应 C++ GetRand_MilliTimeAndU22)
session_generation: u64Monotonic physical-socket generation allocated at accept time. Direct async push owners must match this value inside the final connection map lookup; a retired request may never recreate it.
state: ConnState连接状态:InitConnect 前 / 后 / 已断开
aes_key: [u8; 16]随机 AES-128 key(InitConnect 响应里下发给客户端)
aes_encrypt_enabled: boolAES 加解密已启用(InitConnect 完成且配置了 RSA 时为 true)
proto_fmt_type: ProtoFmtType该连接协商的 proto 格式(Protobuf / JSON)
last_keepalive: Instant上次收到 KeepAlive 的时间,用于超时检查
recv_notify: boolInitConnect.C2S.recvNotify:此连接是否接收市场状态 / 交易解锁等通知。
C++ 在 APIServer_InitConnect.cpp 里把该字段写入 ConnInfo;
RegQotPush / Qot_Sub(isRegOrUnRegPush) 不会修改这个开关。
ai_type: i32InitConnect.C2S.aiType:AI 调用类型。C++ 10.7
APIServer_InitConnect.cpp 缺省为 0,并写入连接状态。
keepalive_count: AtomicU32已收到的 KeepAlive 计数(监控用)
tx: Sender<FutuFrame>发送帧到此连接
key_id: Option<String>该连接绑定的 API key id;WS 握手时填,未配 keys.json 时为 None
scopes: HashSet<Scope>该连接持有的 scope 集合;空集 = legacy 模式 / TCP 直连,scope 检查放行
allowed_markets: Option<Arc<HashSet<String>>>v1.4.105 D3 (Phase 4) T-B2: 该连接 caller key 的 allowed_markets 硬
限额 (大写字符串 set, e.g. {“HK”,“US”}). None = 无限制 (legacy 模式
/ TCP 直连默认全开 / 未配 allowed_markets); Some(set) 非空 → push 端
应过滤 trd_market 不在 set 中的 trade event.
触发: WS handshake 时从 KeyRecord.allowed_markets 拷贝过来.
PushDispatcher::push_trd_acc 端 Layer 3 filter 检查. 与
caller_allowed_acc_ids (Layer 1, per-call snapshot in IncomingRequest)
区别: 本字段是 per-conn snapshot (handshake 时一次性), 不随 per-call
重读 — KeyRecord SIGHUP reload 后仅新建连接生效, 老连接保持 snapshot
(与 scopes / caller_allowed_acc_ids 的 snapshot 语义一致).
allowed_acc_ids: Option<Arc<HashSet<u64>>>codex round 1 F4 (P2) v1.4.105: 该连接 caller key 的 allowed_acc_ids
硬限额 (per-conn snapshot, handshake 时一次性). None / Some(empty) =
无限制 (legacy 模式 / TCP 直连默认全开 / 未配 allowed_acc_ids);
Some(non-empty set) →
PushDispatcher::push_trd_acc 端 push-time 硬过滤 acc_id 不在 set 中
的 trade event (Layer 1, 与 allowed_markets 的 Layer 3 互补).
Deny-all 使用 sentinel {0},不使用空集合。
触发: codex F4 指出 raw TCP push 端只查 acc:read scope +
allowed_markets, 不查 allowed_acc_ids. 即使 request-time
SubAccPushHandler 已阻止越权订阅, stale subscription / KeyRecord
reload 后窄化的 acc 范围 / 历史 bug 留下的 conn→acc 关系 仍可能让 push
漏 leak. 本字段提供第二层 push-time 兜底.
与 caller_allowed_acc_ids (IncomingRequest, per-call) 区别: 本字段
在 push-time 用 (无 IncomingRequest), per-conn snapshot 与 scopes /
allowed_markets 的 snapshot 语义一致.
Implementations§
Source§impl ClientConn
impl ClientConn
Sourcepub fn generate_conn_id() -> u64
pub fn generate_conn_id() -> u64
生成随机连接 ID(与 C++ 的 GetRand_MilliTimeAndU22 对应)
pub fn generate_session_generation() -> u64
Sourcepub fn generate_aes_key() -> [u8; 16]
pub fn generate_aes_key() -> [u8; 16]
生成随机 AES key(16 字节 hex 字符串的 ASCII 字节)
Sourcepub fn make_frame(
&self,
proto_id: u32,
serial_no: u32,
body: Bytes,
) -> FutuFrame
pub fn make_frame( &self, proto_id: u32, serial_no: u32, body: Bytes, ) -> FutuFrame
创建发送帧,自动处理 AES 加密
当 aes_encrypt_enabled 为 true 时:
- SHA1 基于明文计算
- body 使用 AES-128 ECB 加密
- header.body_len 更新为密文长度
对应 C++ APIServerCS_Conn::OnSendPacketData 的加密逻辑
Sourcepub fn make_frame_with_sha1(
&self,
proto_id: u32,
serial_no: u32,
body: Bytes,
body_sha1: [u8; 20],
) -> FutuFrame
pub fn make_frame_with_sha1( &self, proto_id: u32, serial_no: u32, body: Bytes, body_sha1: [u8; 20], ) -> FutuFrame
创建发送帧,复用调用方已计算的明文 body SHA1。
Sourcepub fn decrypt_body(&self, body: &[u8]) -> Result<Vec<u8>, FutuError>
pub fn decrypt_body(&self, body: &[u8]) -> Result<Vec<u8>, FutuError>
解密请求 body(如果启用了 AES 加密)
对应 C++ APIServerCS_Conn::OnRecvPacket 的解密逻辑
Sourcepub fn handle_init_connect(
&mut self,
body: &[u8],
server_ver: i32,
login_user_id: u64,
keepalive_interval: i32,
rsa_private_key: Option<&str>,
) -> Result<Vec<u8>, FutuError>
pub fn handle_init_connect( &mut self, body: &[u8], server_ver: i32, login_user_id: u64, keepalive_interval: i32, rsa_private_key: Option<&str>, ) -> Result<Vec<u8>, FutuError>
处理 InitConnect 请求,返回 InitConnect 响应 body
当配置了 RSA 私钥时:
- C2S 请求 body 使用 RSA 公钥加密(需要用私钥解密)
- S2C 响应 body 使用 RSA 公钥加密(客户端用私钥解密)
对应 C++ APIServer::OnRecvInitConnect
pub fn handle_init_connect_with_identity( &mut self, body: &[u8], server_ver: i32, login_user_id: u64, user_attribution: Option<i32>, keepalive_interval: i32, rsa_private_key: Option<&str>, ) -> Result<Vec<u8>, FutuError>
Sourcepub fn handle_keepalive(&self, body: &[u8]) -> Result<Vec<u8>, FutuError>
pub fn handle_keepalive(&self, body: &[u8]) -> Result<Vec<u8>, FutuError>
处理 KeepAlive 请求。
This compatibility helper falls back to the local daemon clock. The
TCP / WebSocket dispatch paths must inject the backend-adjusted server
time through Self::handle_keepalive_at to match C++ OpenD.
Auto Trait Implementations§
impl !Freeze for ClientConn
impl RefUnwindSafe for ClientConn
impl Send for ClientConn
impl Sync for ClientConn
impl Unpin for ClientConn
impl UnsafeUnpin for ClientConn
impl UnwindSafe for ClientConn
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more