feat(notify): 重构通信内核并补齐 stream/bulk/record/transfer 能力
- 引入 LogicalConn/TransportConn 分层,ClientConn 保留兼容适配层 - 新增 Stream、Bulk、RecordStream 三条数据面能力及对应控制路径 - 完成 transfer/file 传输内核与状态快照、诊断能力 - 补齐 reconnect、inbound dispatcher、modern psk 等基础模块 - 增加大规模回归、并发与基准测试覆盖 - 更新依赖库
This commit is contained in:
+437
@@ -0,0 +1,437 @@
|
||||
package notify
|
||||
|
||||
import (
|
||||
"b612.me/starcrypto"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
type clientConnTransportDetachState struct {
|
||||
Generation uint64
|
||||
Reason string
|
||||
Err string
|
||||
At time.Time
|
||||
}
|
||||
|
||||
const (
|
||||
clientConnTransportDetachKindReadError = "read_error"
|
||||
clientConnTransportDetachKindHeartbeatTimeout = "heartbeat_timeout"
|
||||
clientConnTransportDetachKindOther = "other"
|
||||
)
|
||||
|
||||
type ClientConn struct {
|
||||
alive atomic.Value
|
||||
status Status
|
||||
logicalView atomic.Pointer[LogicalConn]
|
||||
logicalState atomic.Pointer[logicalConnState]
|
||||
runtimeState atomic.Pointer[logicalConnRuntimeState]
|
||||
transportState atomic.Pointer[clientConnTransportState]
|
||||
sessionRuntime atomic.Pointer[clientConnSessionRuntime]
|
||||
attachment atomic.Pointer[clientConnAttachmentState]
|
||||
identityBound atomic.Bool
|
||||
ClientID string
|
||||
ClientAddr net.Addr
|
||||
server Server
|
||||
}
|
||||
|
||||
type Status struct {
|
||||
Alive bool
|
||||
Reason string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (c *ClientConn) readTUMessage() {
|
||||
if logical := c.LogicalConn(); logical != nil {
|
||||
logical.readTUMessage()
|
||||
return
|
||||
}
|
||||
rt := c.clientConnSessionRuntimeSnapshot()
|
||||
if rt == nil {
|
||||
return
|
||||
}
|
||||
c.readTUMessageLoop(rt)
|
||||
}
|
||||
|
||||
func (c *ClientConn) readTUMessageLoop(rt *clientConnSessionRuntime) {
|
||||
if logical := c.LogicalConn(); logical != nil {
|
||||
logical.readTUMessageLoop(rt)
|
||||
return
|
||||
}
|
||||
if rt == nil {
|
||||
return
|
||||
}
|
||||
stopCtx := rt.transportStopCtx
|
||||
if stopCtx == nil {
|
||||
stopCtx = rt.stopCtx
|
||||
}
|
||||
if stopCtx == nil {
|
||||
return
|
||||
}
|
||||
conn := rt.tuConn
|
||||
generation := rt.transportGeneration
|
||||
defer closeClientConnSessionRuntimeTransportDone(rt)
|
||||
buf := streamReadBuffer()
|
||||
for {
|
||||
select {
|
||||
case <-sessionStopChan(stopCtx):
|
||||
if c.shouldCloseClientConnTransportOnStop(conn) {
|
||||
_ = conn.Close()
|
||||
}
|
||||
return
|
||||
default:
|
||||
}
|
||||
num, data, err := c.readFromTUTransportConnWithBuffer(conn, buf)
|
||||
if !c.handleTUTransportReadResultWithSession(stopCtx, conn, generation, num, data, err) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated: rsaDecode exists only for the legacy MSG_KEY_CHANGE flow.
|
||||
func (c *ClientConn) rsaDecode(message Message) {
|
||||
privKey, err := starcrypto.DecodeRsaPrivateKey(c.clientConnHandshakeRsaKeySnapshot(), "")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
message.Reply([]byte("failed"))
|
||||
return
|
||||
}
|
||||
data, err := starcrypto.RSADecrypt(privKey, message.Value)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
message.Reply([]byte("failed"))
|
||||
return
|
||||
}
|
||||
message.Reply([]byte("success"))
|
||||
c.setClientConnSecretKey(data)
|
||||
}
|
||||
|
||||
func (c *ClientConn) sayGoodByeForTU() error {
|
||||
if c == nil || c.server == nil {
|
||||
return errTransportDetached
|
||||
}
|
||||
_, err := c.server.SendWaitLogical(c.LogicalConn(), "bye", nil, time.Second*3)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
_, err = c.server.sendWait(c, TransferMsg{
|
||||
ID: 10010,
|
||||
Key: "bye",
|
||||
Value: nil,
|
||||
Type: MSG_SYS_WAIT,
|
||||
}, time.Second*3)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *ClientConn) GetSecretKey() []byte {
|
||||
return c.clientConnSecretKeySnapshot()
|
||||
}
|
||||
|
||||
// Deprecated: SetSecretKey injects a raw per-connection transport key directly.
|
||||
func (c *ClientConn) SetSecretKey(key []byte) {
|
||||
c.setClientConnSecretKey(key)
|
||||
}
|
||||
|
||||
func (c *ClientConn) GetMsgEn() func([]byte, []byte) []byte {
|
||||
return c.clientConnMsgEnSnapshot()
|
||||
}
|
||||
|
||||
// Deprecated: SetMsgEn overrides the per-connection transport codec directly.
|
||||
func (c *ClientConn) SetMsgEn(fn func([]byte, []byte) []byte) {
|
||||
c.setClientConnMsgEn(fn)
|
||||
}
|
||||
|
||||
func (c *ClientConn) GetMsgDe() func([]byte, []byte) []byte {
|
||||
return c.clientConnMsgDeSnapshot()
|
||||
}
|
||||
|
||||
// Deprecated: SetMsgDe overrides the per-connection transport codec directly.
|
||||
func (c *ClientConn) SetMsgDe(fn func([]byte, []byte) []byte) {
|
||||
c.setClientConnMsgDe(fn)
|
||||
}
|
||||
|
||||
func (c *ClientConn) StopMonitorChan() <-chan struct{} {
|
||||
return sessionStopChan(c.clientConnStopContextSnapshot())
|
||||
}
|
||||
|
||||
func (c *ClientConn) Status() Status {
|
||||
return c.clientConnStatusSnapshot()
|
||||
}
|
||||
|
||||
func (c *ClientConn) Server() Server {
|
||||
if c != nil {
|
||||
if logical := c.logicalView.Load(); logical != nil {
|
||||
if server := logical.Server(); server != nil {
|
||||
return server
|
||||
}
|
||||
}
|
||||
}
|
||||
return c.server
|
||||
}
|
||||
|
||||
func (c *ClientConn) GetRemoteAddr() net.Addr {
|
||||
return c.clientConnRemoteAddrSnapshot()
|
||||
}
|
||||
|
||||
func (c *ClientConn) markClientConnIdentityBound() {
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
if logical := c.logicalView.Load(); logical != nil {
|
||||
logical.markIdentityBound()
|
||||
return
|
||||
}
|
||||
state := c.ensureLogicalConnState()
|
||||
if state == nil {
|
||||
c.identityBound.Store(true)
|
||||
return
|
||||
}
|
||||
state.updatePeer(func(peer *logicalConnPeerState) {
|
||||
peer.identityBound = true
|
||||
})
|
||||
c.syncLegacyLogicalFieldsFromState(state)
|
||||
}
|
||||
|
||||
func (c *ClientConn) clientConnIdentityBoundSnapshot() bool {
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
return c.clientConnLogicalPeerStateSnapshot().identityBound
|
||||
}
|
||||
|
||||
func (c *ClientConn) markClientConnStreamTransport() {
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
if logical := c.logicalView.Load(); logical != nil {
|
||||
logical.markStreamTransport()
|
||||
return
|
||||
}
|
||||
state := c.ensureClientConnTransportState()
|
||||
if state == nil {
|
||||
return
|
||||
}
|
||||
state.streamTransport.Store(true)
|
||||
}
|
||||
|
||||
func (c *ClientConn) clientConnUsesStreamTransportSnapshot() bool {
|
||||
state := c.ensureClientConnTransportState()
|
||||
if state == nil {
|
||||
return false
|
||||
}
|
||||
return state.streamTransport.Load()
|
||||
}
|
||||
|
||||
func (c *ClientConn) shouldPreserveLogicalPeerOnTransportLoss() bool {
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
return c.clientConnIdentityBoundSnapshot() && c.clientConnUsesStreamTransportSnapshot()
|
||||
}
|
||||
|
||||
func (c *ClientConn) markClientConnTransportAttached() uint64 {
|
||||
if c == nil {
|
||||
return 0
|
||||
}
|
||||
if logical := c.logicalView.Load(); logical != nil {
|
||||
return logical.markTransportAttached()
|
||||
}
|
||||
state := c.ensureClientConnTransportState()
|
||||
if state == nil {
|
||||
return 0
|
||||
}
|
||||
gen := state.transportGen.Add(1)
|
||||
state.attachCount.Add(1)
|
||||
state.lastAttachAt.Store(time.Now().UnixNano())
|
||||
return gen
|
||||
}
|
||||
|
||||
func (c *ClientConn) clientConnTransportGenerationSnapshot() uint64 {
|
||||
state := c.ensureClientConnTransportState()
|
||||
if state == nil {
|
||||
return 0
|
||||
}
|
||||
return state.transportGen.Load()
|
||||
}
|
||||
|
||||
func (c *ClientConn) clientConnTransportAttachCountSnapshot() uint64 {
|
||||
state := c.ensureClientConnTransportState()
|
||||
if state == nil {
|
||||
return 0
|
||||
}
|
||||
return state.attachCount.Load()
|
||||
}
|
||||
|
||||
func (c *ClientConn) markClientConnTransportDetached(reason string, err error) {
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
if logical := c.logicalView.Load(); logical != nil {
|
||||
logical.markTransportDetached(reason, err)
|
||||
return
|
||||
}
|
||||
state := c.ensureClientConnTransportState()
|
||||
if state == nil {
|
||||
return
|
||||
}
|
||||
detachState := &clientConnTransportDetachState{
|
||||
Generation: c.clientConnTransportGenerationSnapshot(),
|
||||
Reason: reason,
|
||||
At: time.Now(),
|
||||
}
|
||||
if err != nil {
|
||||
detachState.Err = err.Error()
|
||||
}
|
||||
state.detachCount.Add(1)
|
||||
state.transportDetach.Store(detachState)
|
||||
}
|
||||
|
||||
func (c *ClientConn) clientConnTransportDetachCountSnapshot() uint64 {
|
||||
state := c.ensureClientConnTransportState()
|
||||
if state == nil {
|
||||
return 0
|
||||
}
|
||||
return state.detachCount.Load()
|
||||
}
|
||||
|
||||
func (c *ClientConn) clearClientConnTransportDetachState() {
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
if logical := c.logicalView.Load(); logical != nil {
|
||||
logical.clearTransportDetachState()
|
||||
return
|
||||
}
|
||||
c.setClientConnTransportDetachState(nil)
|
||||
}
|
||||
|
||||
func (c *ClientConn) clientConnTransportDetachSnapshot() *clientConnTransportDetachState {
|
||||
state := c.ensureClientConnTransportState()
|
||||
if state == nil {
|
||||
return nil
|
||||
}
|
||||
return cloneClientConnTransportDetachState(state.transportDetach.Load())
|
||||
}
|
||||
|
||||
func (c *ClientConn) clientConnLogicalTransportDetachedSnapshot() bool {
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
if !c.clientConnIdentityBoundSnapshot() || !c.clientConnUsesStreamTransportSnapshot() {
|
||||
return false
|
||||
}
|
||||
if !c.clientConnAliveSnapshot() {
|
||||
return false
|
||||
}
|
||||
return !c.clientConnTransportAttachedSnapshot()
|
||||
}
|
||||
|
||||
func (c *ClientConn) clientConnLastTransportAttachedAtSnapshot() time.Time {
|
||||
state := c.ensureClientConnTransportState()
|
||||
if state == nil {
|
||||
return time.Time{}
|
||||
}
|
||||
unixNano := state.lastAttachAt.Load()
|
||||
if unixNano == 0 {
|
||||
return time.Time{}
|
||||
}
|
||||
return time.Unix(0, unixNano)
|
||||
}
|
||||
|
||||
func classifyClientConnTransportDetachReason(reason string) string {
|
||||
switch reason {
|
||||
case "":
|
||||
return ""
|
||||
case "read error":
|
||||
return clientConnTransportDetachKindReadError
|
||||
case "heartbeat timeout":
|
||||
return clientConnTransportDetachKindHeartbeatTimeout
|
||||
default:
|
||||
return clientConnTransportDetachKindOther
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClientConn) clientConnTransportDetachKindSnapshot() string {
|
||||
if c == nil {
|
||||
return ""
|
||||
}
|
||||
detach := c.clientConnTransportDetachSnapshot()
|
||||
if detach == nil {
|
||||
return ""
|
||||
}
|
||||
return classifyClientConnTransportDetachReason(detach.Reason)
|
||||
}
|
||||
|
||||
func (c *ClientConn) clientConnTransportDetachGenerationSnapshot() uint64 {
|
||||
if c == nil {
|
||||
return 0
|
||||
}
|
||||
detach := c.clientConnTransportDetachSnapshot()
|
||||
if detach == nil {
|
||||
return 0
|
||||
}
|
||||
if detach.Generation == 0 {
|
||||
return c.clientConnTransportGenerationSnapshot()
|
||||
}
|
||||
return detach.Generation
|
||||
}
|
||||
|
||||
func (c *ClientConn) clientConnTransportDetachExpirySnapshot() (time.Time, bool) {
|
||||
if c == nil {
|
||||
return time.Time{}, false
|
||||
}
|
||||
detach := c.clientConnTransportDetachSnapshot()
|
||||
if detach == nil || detach.At.IsZero() {
|
||||
return time.Time{}, false
|
||||
}
|
||||
if c.server == nil {
|
||||
return time.Time{}, false
|
||||
}
|
||||
keepSec := c.server.DetachedClientKeepSec()
|
||||
if keepSec <= 0 {
|
||||
return time.Time{}, false
|
||||
}
|
||||
return detach.At.Add(time.Duration(keepSec) * time.Second), true
|
||||
}
|
||||
|
||||
func (c *ClientConn) clientConnTransportDetachExpiredSnapshot(now time.Time) bool {
|
||||
if c == nil || !c.clientConnLogicalTransportDetachedSnapshot() {
|
||||
return false
|
||||
}
|
||||
expiry, ok := c.clientConnTransportDetachExpirySnapshot()
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return !now.Before(expiry)
|
||||
}
|
||||
|
||||
func (c *ClientConn) clientConnTransportDetachRemainingSnapshot(now time.Time) time.Duration {
|
||||
if c == nil || !c.clientConnLogicalTransportDetachedSnapshot() {
|
||||
return 0
|
||||
}
|
||||
expiry, ok := c.clientConnTransportDetachExpirySnapshot()
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
if !now.Before(expiry) {
|
||||
return 0
|
||||
}
|
||||
return expiry.Sub(now)
|
||||
}
|
||||
|
||||
func (c *ClientConn) clientConnReattachEligibleSnapshot(now time.Time) bool {
|
||||
if c == nil || !c.clientConnLogicalTransportDetachedSnapshot() {
|
||||
return false
|
||||
}
|
||||
if !c.clientConnAliveSnapshot() {
|
||||
return false
|
||||
}
|
||||
if c.clientConnTransportAttachedSnapshot() {
|
||||
return false
|
||||
}
|
||||
if c.clientConnTransportDetachExpiredSnapshot(now) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user