|
|
|
@ -1,377 +1,570 @@
|
|
|
|
|
package notify
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"b612.me/starcrypto"
|
|
|
|
|
"b612.me/stario"
|
|
|
|
|
"b612.me/starnet"
|
|
|
|
|
"context"
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"math/rand"
|
|
|
|
|
"net"
|
|
|
|
|
"strings"
|
|
|
|
|
"os"
|
|
|
|
|
"sync"
|
|
|
|
|
"sync/atomic"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"b612.me/starcrypto"
|
|
|
|
|
"b612.me/starnet"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// StarNotifyC 为Client端
|
|
|
|
|
type StarNotifyC struct {
|
|
|
|
|
Connc net.Conn
|
|
|
|
|
dialTimeout time.Duration
|
|
|
|
|
clientSign map[string]chan string
|
|
|
|
|
mu sync.Mutex
|
|
|
|
|
// FuncLists 当不使用channel时,使用此记录调用函数
|
|
|
|
|
FuncLists map[string]func(CMsg)
|
|
|
|
|
stopSign context.Context
|
|
|
|
|
cancel context.CancelFunc
|
|
|
|
|
defaultFunc func(CMsg)
|
|
|
|
|
// UseChannel 是否使用channel作为信息传递
|
|
|
|
|
UseChannel bool
|
|
|
|
|
isUDP bool
|
|
|
|
|
Sync bool
|
|
|
|
|
// Queue 是用来处理收发信息的简单消息队列
|
|
|
|
|
Queue *starnet.StarQueue
|
|
|
|
|
// Online 当前链接是否处于活跃状态
|
|
|
|
|
Online bool
|
|
|
|
|
lockPool map[string]CMsg
|
|
|
|
|
aesKey []byte
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CMsg 指明当前客户端被通知的关键字
|
|
|
|
|
type CMsg struct {
|
|
|
|
|
Key string
|
|
|
|
|
Value string
|
|
|
|
|
mode string
|
|
|
|
|
wait chan int
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (star *StarNotifyC) starinitc() {
|
|
|
|
|
builder := starnet.NewQueue()
|
|
|
|
|
builder.EncodeFunc = encodeFunc
|
|
|
|
|
builder.DecodeFunc = decodeFunc
|
|
|
|
|
builder.Encode = true
|
|
|
|
|
star.stopSign, star.cancel = context.WithCancel(context.Background())
|
|
|
|
|
star.Queue = builder
|
|
|
|
|
star.FuncLists = make(map[string]func(CMsg))
|
|
|
|
|
star.UseChannel = false
|
|
|
|
|
star.clientSign = make(map[string]chan string)
|
|
|
|
|
star.Online = false
|
|
|
|
|
star.lockPool = make(map[string]CMsg)
|
|
|
|
|
star.Queue.RestoreDuration(time.Millisecond * 50)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (star *StarNotifyC) SetAesKey(key []byte) {
|
|
|
|
|
star.aesKey = key
|
|
|
|
|
star.Queue.EncodeFunc = func(data []byte) []byte {
|
|
|
|
|
return starcrypto.AesEncryptCFB(data, key)
|
|
|
|
|
}
|
|
|
|
|
star.Queue.DecodeFunc = func(data []byte) []byte {
|
|
|
|
|
return starcrypto.AesDecryptCFB(data, key)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (star *StarNotifyC) GetAesKey() []byte {
|
|
|
|
|
if len(star.aesKey) == 0 {
|
|
|
|
|
return aesKey
|
|
|
|
|
}
|
|
|
|
|
return star.aesKey
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Notify 用于获取一个通知
|
|
|
|
|
func (star *StarNotifyC) Notify(key string) chan string {
|
|
|
|
|
if _, ok := star.clientSign[key]; !ok {
|
|
|
|
|
ch := make(chan string, 20)
|
|
|
|
|
star.mu.Lock()
|
|
|
|
|
star.clientSign[key] = ch
|
|
|
|
|
star.mu.Unlock()
|
|
|
|
|
}
|
|
|
|
|
return star.clientSign[key]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (star *StarNotifyC) store(key, value string) {
|
|
|
|
|
if _, ok := star.clientSign[key]; !ok {
|
|
|
|
|
ch := make(chan string, 20)
|
|
|
|
|
ch <- value
|
|
|
|
|
star.mu.Lock()
|
|
|
|
|
star.clientSign[key] = ch
|
|
|
|
|
star.mu.Unlock()
|
|
|
|
|
return
|
|
|
|
|
//var nowd int64
|
|
|
|
|
type ClientCommon struct {
|
|
|
|
|
alive atomic.Value
|
|
|
|
|
status Status
|
|
|
|
|
byeFromServer bool
|
|
|
|
|
conn net.Conn
|
|
|
|
|
mu sync.Mutex
|
|
|
|
|
msgID uint64
|
|
|
|
|
queue *starnet.StarQueue
|
|
|
|
|
stopFn context.CancelFunc
|
|
|
|
|
stopCtx context.Context
|
|
|
|
|
parallelNum int
|
|
|
|
|
maxReadTimeout time.Duration
|
|
|
|
|
maxWriteTimeout time.Duration
|
|
|
|
|
keyExchangeFn func(c Client) error
|
|
|
|
|
linkFns map[string]func(message *Message)
|
|
|
|
|
defaultFns func(message *Message)
|
|
|
|
|
msgEn func([]byte, []byte) []byte
|
|
|
|
|
msgDe func([]byte, []byte) []byte
|
|
|
|
|
noFinSyncMsgPool sync.Map
|
|
|
|
|
handshakeRsaPubKey []byte
|
|
|
|
|
SecretKey []byte
|
|
|
|
|
noFinSyncMsgMaxKeepSeconds int
|
|
|
|
|
lastHeartbeat int64
|
|
|
|
|
heartbeatPeriod time.Duration
|
|
|
|
|
wg stario.WaitGroup
|
|
|
|
|
netType NetType
|
|
|
|
|
skipKeyExchange bool
|
|
|
|
|
useHeartBeat bool
|
|
|
|
|
sequenceDe func([]byte) (interface{}, error)
|
|
|
|
|
sequenceEn func(interface{}) ([]byte, error)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ClientCommon) Connect(network string, addr string) error {
|
|
|
|
|
if c.alive.Load().(bool) {
|
|
|
|
|
return errors.New("client already run")
|
|
|
|
|
}
|
|
|
|
|
c.stopCtx, c.stopFn = context.WithCancel(context.Background())
|
|
|
|
|
c.queue = starnet.NewQueueCtx(c.stopCtx, 4)
|
|
|
|
|
conn, err := net.Dial(network, addr)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
c.alive.Store(true)
|
|
|
|
|
c.status.Alive = true
|
|
|
|
|
c.conn = conn
|
|
|
|
|
if c.useHeartBeat {
|
|
|
|
|
go c.Heartbeat()
|
|
|
|
|
}
|
|
|
|
|
star.clientSign[key] <- value
|
|
|
|
|
return c.clientPostInit()
|
|
|
|
|
}
|
|
|
|
|
func NewNotifyCWithTimeOut(netype, value string, timeout time.Duration) (*StarNotifyC, error) {
|
|
|
|
|
var err error
|
|
|
|
|
var star StarNotifyC
|
|
|
|
|
star.starinitc()
|
|
|
|
|
star.isUDP = false
|
|
|
|
|
if strings.Index(netype, "udp") >= 0 {
|
|
|
|
|
star.isUDP = true
|
|
|
|
|
|
|
|
|
|
func (c *ClientCommon) ConnectTimeout(network string, addr string, timeout time.Duration) error {
|
|
|
|
|
if c.alive.Load().(bool) {
|
|
|
|
|
return errors.New("client already run")
|
|
|
|
|
}
|
|
|
|
|
star.Connc, err = net.DialTimeout(netype, value, timeout)
|
|
|
|
|
c.stopCtx, c.stopFn = context.WithCancel(context.Background())
|
|
|
|
|
c.queue = starnet.NewQueueCtx(c.stopCtx, 4)
|
|
|
|
|
conn, err := net.DialTimeout(network, addr, timeout)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
star.dialTimeout = timeout
|
|
|
|
|
go star.cnotify()
|
|
|
|
|
go func() {
|
|
|
|
|
<-star.stopSign.Done()
|
|
|
|
|
star.Connc.Close()
|
|
|
|
|
star.Online = false
|
|
|
|
|
return
|
|
|
|
|
}()
|
|
|
|
|
go func() {
|
|
|
|
|
for {
|
|
|
|
|
buf := make([]byte, 8192)
|
|
|
|
|
n, err := star.Connc.Read(buf)
|
|
|
|
|
if n != 0 {
|
|
|
|
|
star.Queue.ParseMessage(buf[0:n], star.Connc)
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
star.Connc.Close()
|
|
|
|
|
star.ClientStop()
|
|
|
|
|
//star, _ = NewNotifyC(netype, value)
|
|
|
|
|
star.Online = false
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
c.alive.Store(true)
|
|
|
|
|
c.status.Alive = true
|
|
|
|
|
c.conn = conn
|
|
|
|
|
if c.useHeartBeat {
|
|
|
|
|
go c.Heartbeat()
|
|
|
|
|
}
|
|
|
|
|
return c.clientPostInit()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ClientCommon) monitorPool() {
|
|
|
|
|
for {
|
|
|
|
|
select {
|
|
|
|
|
case <-c.stopCtx.Done():
|
|
|
|
|
c.noFinSyncMsgPool.Range(func(k, v interface{}) bool {
|
|
|
|
|
data := v.(WaitMsg)
|
|
|
|
|
close(data.Reply)
|
|
|
|
|
c.noFinSyncMsgPool.Delete(k)
|
|
|
|
|
return true
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
case <-time.After(time.Second * 30):
|
|
|
|
|
}
|
|
|
|
|
now := time.Now()
|
|
|
|
|
if c.noFinSyncMsgMaxKeepSeconds > 0 {
|
|
|
|
|
c.noFinSyncMsgPool.Range(func(k, v interface{}) bool {
|
|
|
|
|
data := v.(WaitMsg)
|
|
|
|
|
if data.Time.Add(time.Duration(c.noFinSyncMsgMaxKeepSeconds) * time.Second).Before(now) {
|
|
|
|
|
close(data.Reply)
|
|
|
|
|
c.noFinSyncMsgPool.Delete(k)
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
star.Online = true
|
|
|
|
|
return &star, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewNotifyC 用于新建一个Client端进程
|
|
|
|
|
func NewNotifyC(netype, value string) (*StarNotifyC, error) {
|
|
|
|
|
var err error
|
|
|
|
|
var star StarNotifyC
|
|
|
|
|
star.starinitc()
|
|
|
|
|
star.isUDP = false
|
|
|
|
|
if strings.Index(netype, "udp") >= 0 {
|
|
|
|
|
star.isUDP = true
|
|
|
|
|
func (c *ClientCommon) SkipExchangeKey() bool {
|
|
|
|
|
return c.skipKeyExchange
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ClientCommon) SetSkipExchangeKey(val bool) {
|
|
|
|
|
c.skipKeyExchange = val
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ClientCommon) clientPostInit() error {
|
|
|
|
|
go c.readMessage()
|
|
|
|
|
go c.loadMessage()
|
|
|
|
|
if !c.skipKeyExchange {
|
|
|
|
|
err := c.keyExchangeFn(c)
|
|
|
|
|
if err != nil {
|
|
|
|
|
c.alive.Store(false)
|
|
|
|
|
c.mu.Lock()
|
|
|
|
|
c.status = Status{
|
|
|
|
|
Alive: false,
|
|
|
|
|
Reason: "key exchange failed",
|
|
|
|
|
Err: err,
|
|
|
|
|
}
|
|
|
|
|
c.mu.Unlock()
|
|
|
|
|
c.stopFn()
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
star.Connc, err = net.Dial(netype, value)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
func NewClient() Client {
|
|
|
|
|
var client = ClientCommon{
|
|
|
|
|
maxReadTimeout: 0,
|
|
|
|
|
maxWriteTimeout: 0,
|
|
|
|
|
sequenceEn: encode,
|
|
|
|
|
sequenceDe: Decode,
|
|
|
|
|
keyExchangeFn: aesRsaHello,
|
|
|
|
|
SecretKey: defaultAesKey,
|
|
|
|
|
handshakeRsaPubKey: defaultRsaPubKey,
|
|
|
|
|
msgEn: defaultMsgEn,
|
|
|
|
|
msgDe: defaultMsgDe,
|
|
|
|
|
}
|
|
|
|
|
go star.cnotify()
|
|
|
|
|
go func() {
|
|
|
|
|
<-star.stopSign.Done()
|
|
|
|
|
star.Connc.Close()
|
|
|
|
|
star.Online = false
|
|
|
|
|
client.alive.Store(false)
|
|
|
|
|
//heartbeat should not controlable for user
|
|
|
|
|
client.useHeartBeat = true
|
|
|
|
|
client.heartbeatPeriod = time.Second * 20
|
|
|
|
|
client.linkFns = make(map[string]func(*Message))
|
|
|
|
|
client.defaultFns = func(message *Message) {
|
|
|
|
|
return
|
|
|
|
|
}()
|
|
|
|
|
go func() {
|
|
|
|
|
for {
|
|
|
|
|
buf := make([]byte, 8192)
|
|
|
|
|
n, err := star.Connc.Read(buf)
|
|
|
|
|
if n != 0 {
|
|
|
|
|
star.Queue.ParseMessage(buf[0:n], star.Connc)
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
star.Connc.Close()
|
|
|
|
|
star.ClientStop()
|
|
|
|
|
//star, _ = NewNotifyC(netype, value)
|
|
|
|
|
star.Online = false
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
client.wg = stario.NewWaitGroup(0)
|
|
|
|
|
client.stopCtx, client.stopFn = context.WithCancel(context.Background())
|
|
|
|
|
return &client
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ClientCommon) Heartbeat() {
|
|
|
|
|
failedCount := 0
|
|
|
|
|
for {
|
|
|
|
|
select {
|
|
|
|
|
case <-c.stopCtx.Done():
|
|
|
|
|
return
|
|
|
|
|
case <-time.After(c.heartbeatPeriod):
|
|
|
|
|
}
|
|
|
|
|
_, err := c.sendWait(TransferMsg{
|
|
|
|
|
ID: 10000,
|
|
|
|
|
Key: "heartbeat",
|
|
|
|
|
Value: nil,
|
|
|
|
|
Type: MSG_SYS_WAIT,
|
|
|
|
|
}, time.Second*5)
|
|
|
|
|
if err == nil {
|
|
|
|
|
c.lastHeartbeat = time.Now().Unix()
|
|
|
|
|
failedCount = 0
|
|
|
|
|
}
|
|
|
|
|
failedCount++
|
|
|
|
|
if failedCount >= 3 {
|
|
|
|
|
//fmt.Println("heatbeat failed,stop client")
|
|
|
|
|
c.alive.Store(false)
|
|
|
|
|
c.mu.Lock()
|
|
|
|
|
c.status = Status{
|
|
|
|
|
Alive: false,
|
|
|
|
|
Reason: "heartbeat failed more than 3 times",
|
|
|
|
|
Err: errors.New("heartbeat failed more than 3 times"),
|
|
|
|
|
}
|
|
|
|
|
c.mu.Unlock()
|
|
|
|
|
c.stopFn()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
star.Online = true
|
|
|
|
|
return &star, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Send 用于向Server端发送数据
|
|
|
|
|
func (star *StarNotifyC) Send(name string) error {
|
|
|
|
|
return star.SendValue(name, "")
|
|
|
|
|
func (c *ClientCommon) readMessage() {
|
|
|
|
|
for {
|
|
|
|
|
select {
|
|
|
|
|
case <-c.stopCtx.Done():
|
|
|
|
|
c.conn.Close()
|
|
|
|
|
return
|
|
|
|
|
default:
|
|
|
|
|
}
|
|
|
|
|
data := make([]byte, 8192)
|
|
|
|
|
if c.maxReadTimeout.Seconds() != 0 {
|
|
|
|
|
c.conn.SetReadDeadline(time.Now().Add(c.maxReadTimeout))
|
|
|
|
|
}
|
|
|
|
|
readNum, err := c.conn.Read(data)
|
|
|
|
|
if err == os.ErrDeadlineExceeded {
|
|
|
|
|
if readNum != 0 {
|
|
|
|
|
c.queue.ParseMessage(data[:readNum], "b612")
|
|
|
|
|
}
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Println("client read error", err)
|
|
|
|
|
c.alive.Store(false)
|
|
|
|
|
c.mu.Lock()
|
|
|
|
|
c.status = Status{
|
|
|
|
|
Alive: false,
|
|
|
|
|
Reason: "client read error",
|
|
|
|
|
Err: err,
|
|
|
|
|
}
|
|
|
|
|
c.mu.Unlock()
|
|
|
|
|
c.stopFn()
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
c.queue.ParseMessage(data[:readNum], "b612")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (star *StarNotifyC) Stoped() <-chan struct{} {
|
|
|
|
|
return star.stopSign.Done()
|
|
|
|
|
func (c *ClientCommon) sayGoodBye() error {
|
|
|
|
|
_, err := c.sendWait(TransferMsg{
|
|
|
|
|
ID: 10010,
|
|
|
|
|
Key: "bye",
|
|
|
|
|
Value: nil,
|
|
|
|
|
Type: MSG_SYS_WAIT,
|
|
|
|
|
}, time.Second*3)
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (star *StarNotifyC) SendValueRaw(key string, msg interface{}) error {
|
|
|
|
|
encodeData, err := encode(msg)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
func (c *ClientCommon) loadMessage() {
|
|
|
|
|
for {
|
|
|
|
|
select {
|
|
|
|
|
case <-c.stopCtx.Done():
|
|
|
|
|
//say goodbye
|
|
|
|
|
if !c.byeFromServer {
|
|
|
|
|
c.sayGoodBye()
|
|
|
|
|
}
|
|
|
|
|
c.conn.Close()
|
|
|
|
|
return
|
|
|
|
|
case data, ok := <-c.queue.RestoreChan():
|
|
|
|
|
if !ok {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
c.wg.Add(1)
|
|
|
|
|
go func(data starnet.MsgQueue) {
|
|
|
|
|
defer c.wg.Done()
|
|
|
|
|
//fmt.Println("c received:", float64(time.Now().UnixNano()-nowd)/1000000)
|
|
|
|
|
now := time.Now()
|
|
|
|
|
//transfer to Msg
|
|
|
|
|
msg, err := c.sequenceDe(c.msgDe(c.SecretKey, data.Msg))
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Println("client decode data error", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
message := Message{
|
|
|
|
|
ServerConn: c,
|
|
|
|
|
TransferMsg: msg.(TransferMsg),
|
|
|
|
|
NetType: NET_CLIENT,
|
|
|
|
|
}
|
|
|
|
|
message.Time = now
|
|
|
|
|
c.dispatchMsg(message)
|
|
|
|
|
}(data)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return star.SendValue(key, string(encodeData))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SendValue 用于向Server端发送key-value类型数据
|
|
|
|
|
func (star *StarNotifyC) SendValue(name, value string) error {
|
|
|
|
|
var err error
|
|
|
|
|
var key []byte
|
|
|
|
|
for _, v := range []byte(name) {
|
|
|
|
|
if v == byte(124) || v == byte(92) {
|
|
|
|
|
key = append(key, byte(92))
|
|
|
|
|
func (c *ClientCommon) dispatchMsg(message Message) {
|
|
|
|
|
switch message.TransferMsg.Type {
|
|
|
|
|
case MSG_SYS_WAIT:
|
|
|
|
|
fallthrough
|
|
|
|
|
case MSG_SYS:
|
|
|
|
|
c.sysMsg(message)
|
|
|
|
|
return
|
|
|
|
|
case MSG_KEY_CHANGE:
|
|
|
|
|
fallthrough
|
|
|
|
|
case MSG_SYS_REPLY:
|
|
|
|
|
fallthrough
|
|
|
|
|
case MSG_SYNC_REPLY:
|
|
|
|
|
data, ok := c.noFinSyncMsgPool.Load(message.ID)
|
|
|
|
|
if ok {
|
|
|
|
|
wait := data.(WaitMsg)
|
|
|
|
|
wait.Reply <- message
|
|
|
|
|
c.noFinSyncMsgPool.Delete(message.ID)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
key = append(key, v)
|
|
|
|
|
return
|
|
|
|
|
//fallthrough
|
|
|
|
|
default:
|
|
|
|
|
}
|
|
|
|
|
callFn := func(fn func(*Message)) {
|
|
|
|
|
fn(&message)
|
|
|
|
|
}
|
|
|
|
|
fn, ok := c.linkFns[message.Key]
|
|
|
|
|
if ok {
|
|
|
|
|
callFn(fn)
|
|
|
|
|
}
|
|
|
|
|
if c.defaultFns != nil {
|
|
|
|
|
callFn(c.defaultFns)
|
|
|
|
|
}
|
|
|
|
|
_, err = star.Connc.Write(star.Queue.BuildMessage([]byte("pa" + "||" + string(key) + "||" + value)))
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (star *StarNotifyC) trim(name string) string {
|
|
|
|
|
var slash bool = false
|
|
|
|
|
var key []byte
|
|
|
|
|
for _, v := range []byte(name) {
|
|
|
|
|
if v == byte(92) && !slash {
|
|
|
|
|
slash = true
|
|
|
|
|
continue
|
|
|
|
|
func (c *ClientCommon) sysMsg(message Message) {
|
|
|
|
|
switch message.Key {
|
|
|
|
|
case "bye":
|
|
|
|
|
if message.TransferMsg.Type == MSG_SYS_WAIT {
|
|
|
|
|
//fmt.Println("recv stop signal from server")
|
|
|
|
|
c.byeFromServer = true
|
|
|
|
|
message.Reply(nil)
|
|
|
|
|
}
|
|
|
|
|
slash = false
|
|
|
|
|
key = append(key, v)
|
|
|
|
|
c.alive.Store(false)
|
|
|
|
|
c.mu.Lock()
|
|
|
|
|
c.status = Status{
|
|
|
|
|
Alive: false,
|
|
|
|
|
Reason: "recv stop signal from server",
|
|
|
|
|
Err: nil,
|
|
|
|
|
}
|
|
|
|
|
c.mu.Unlock()
|
|
|
|
|
c.stopFn()
|
|
|
|
|
}
|
|
|
|
|
return string(key)
|
|
|
|
|
}
|
|
|
|
|
func (star *StarNotifyC) SendValueWaitRaw(key string, msg interface{}, tmout time.Duration) (CMsg, error) {
|
|
|
|
|
encodeData, err := encode(msg)
|
|
|
|
|
|
|
|
|
|
func (c *ClientCommon) SetDefaultLink(fn func(message *Message)) {
|
|
|
|
|
c.defaultFns = fn
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ClientCommon) SetLink(key string, fn func(*Message)) {
|
|
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
c.linkFns[key] = fn
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ClientCommon) send(msg TransferMsg) (WaitMsg, error) {
|
|
|
|
|
var wait WaitMsg
|
|
|
|
|
if msg.Type != MSG_SYNC_REPLY && msg.Type != MSG_KEY_CHANGE && msg.Type != MSG_SYS_REPLY || msg.ID == 0 {
|
|
|
|
|
msg.ID = atomic.AddUint64(&c.msgID, 1)
|
|
|
|
|
}
|
|
|
|
|
data, err := c.sequenceEn(msg)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return CMsg{}, err
|
|
|
|
|
return WaitMsg{}, err
|
|
|
|
|
}
|
|
|
|
|
return star.SendValueWait(key, string(encodeData), tmout)
|
|
|
|
|
data = c.msgEn(c.SecretKey, data)
|
|
|
|
|
data = c.queue.BuildMessage(data)
|
|
|
|
|
if c.maxWriteTimeout.Seconds() != 0 {
|
|
|
|
|
c.conn.SetWriteDeadline(time.Now().Add(c.maxWriteTimeout))
|
|
|
|
|
}
|
|
|
|
|
_, err = c.conn.Write(data)
|
|
|
|
|
if err == nil && (msg.Type == MSG_SYNC_ASK || msg.Type == MSG_KEY_CHANGE || msg.Type == MSG_SYS_WAIT) {
|
|
|
|
|
wait.Time = time.Now()
|
|
|
|
|
wait.TransferMsg = msg
|
|
|
|
|
wait.Reply = make(chan Message, 1)
|
|
|
|
|
c.noFinSyncMsgPool.Store(msg.ID, wait)
|
|
|
|
|
}
|
|
|
|
|
return wait, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ClientCommon) Send(key string, value MsgVal) error {
|
|
|
|
|
_, err := c.send(TransferMsg{
|
|
|
|
|
Key: key,
|
|
|
|
|
Value: value,
|
|
|
|
|
Type: MSG_ASYNC,
|
|
|
|
|
})
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SendValueWait 用于向Server端发送key-value类型数据并等待结果返回,此结果不会通过标准返回流程处理
|
|
|
|
|
func (star *StarNotifyC) SendValueWait(name, value string, tmout time.Duration) (CMsg, error) {
|
|
|
|
|
var err error
|
|
|
|
|
var tmceed <-chan time.Time
|
|
|
|
|
if star.UseChannel {
|
|
|
|
|
return CMsg{}, errors.New("Do Not Use UseChannel Mode!")
|
|
|
|
|
func (c *ClientCommon) sendWait(msg TransferMsg, timeout time.Duration) (Message, error) {
|
|
|
|
|
data, err := c.send(msg)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return Message{}, err
|
|
|
|
|
}
|
|
|
|
|
rand.Seed(time.Now().UnixNano())
|
|
|
|
|
mode := "cr" + fmt.Sprintf("%d%06d", time.Now().UnixNano(), rand.Intn(999999))
|
|
|
|
|
var key []byte
|
|
|
|
|
for _, v := range []byte(name) {
|
|
|
|
|
if v == byte(124) || v == byte(92) {
|
|
|
|
|
key = append(key, byte(92))
|
|
|
|
|
if timeout.Seconds() == 0 {
|
|
|
|
|
msg, ok := <-data.Reply
|
|
|
|
|
if !ok {
|
|
|
|
|
return msg, os.ErrInvalid
|
|
|
|
|
}
|
|
|
|
|
key = append(key, v)
|
|
|
|
|
return msg, nil
|
|
|
|
|
}
|
|
|
|
|
_, err = star.Connc.Write(star.Queue.BuildMessage([]byte(mode + "||" + string(key) + "||" + value)))
|
|
|
|
|
select {
|
|
|
|
|
case <-time.After(timeout):
|
|
|
|
|
close(data.Reply)
|
|
|
|
|
c.noFinSyncMsgPool.Delete(data.TransferMsg.ID)
|
|
|
|
|
return Message{}, os.ErrDeadlineExceeded
|
|
|
|
|
case msg, ok := <-data.Reply:
|
|
|
|
|
if !ok {
|
|
|
|
|
return msg, os.ErrInvalid
|
|
|
|
|
}
|
|
|
|
|
return msg, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ClientCommon) sendCtx(msg TransferMsg, ctx context.Context) (Message, error) {
|
|
|
|
|
data, err := c.send(msg)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return CMsg{}, err
|
|
|
|
|
return Message{}, err
|
|
|
|
|
}
|
|
|
|
|
if int64(tmout) > 0 {
|
|
|
|
|
tmceed = time.After(tmout)
|
|
|
|
|
if ctx == nil {
|
|
|
|
|
ctx = context.Background()
|
|
|
|
|
}
|
|
|
|
|
var source CMsg
|
|
|
|
|
source.wait = make(chan int, 2)
|
|
|
|
|
star.mu.Lock()
|
|
|
|
|
star.lockPool[mode] = source
|
|
|
|
|
star.mu.Unlock()
|
|
|
|
|
select {
|
|
|
|
|
case <-source.wait:
|
|
|
|
|
res := star.lockPool[mode]
|
|
|
|
|
star.mu.Lock()
|
|
|
|
|
delete(star.lockPool, mode)
|
|
|
|
|
star.mu.Unlock()
|
|
|
|
|
return res, nil
|
|
|
|
|
case <-tmceed:
|
|
|
|
|
return CMsg{}, errors.New("Time Exceed")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ReplyMsg 用于向Server端Reply信息
|
|
|
|
|
func (star *StarNotifyC) ReplyMsg(data CMsg, name, value string) error {
|
|
|
|
|
var err error
|
|
|
|
|
var key []byte
|
|
|
|
|
for _, v := range []byte(name) {
|
|
|
|
|
if v == byte(124) || v == byte(92) {
|
|
|
|
|
key = append(key, byte(92))
|
|
|
|
|
case <-ctx.Done():
|
|
|
|
|
close(data.Reply)
|
|
|
|
|
c.noFinSyncMsgPool.Delete(data.TransferMsg.ID)
|
|
|
|
|
return Message{}, os.ErrDeadlineExceeded
|
|
|
|
|
case msg, ok := <-data.Reply:
|
|
|
|
|
if !ok {
|
|
|
|
|
return msg, os.ErrInvalid
|
|
|
|
|
}
|
|
|
|
|
key = append(key, v)
|
|
|
|
|
return msg, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ClientCommon) SendObjCtx(ctx context.Context, key string, val interface{}) (Message, error) {
|
|
|
|
|
data, err := c.sequenceEn(val)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return Message{}, err
|
|
|
|
|
}
|
|
|
|
|
return c.sendCtx(TransferMsg{
|
|
|
|
|
Key: key,
|
|
|
|
|
Value: data,
|
|
|
|
|
Type: MSG_SYNC_ASK,
|
|
|
|
|
}, ctx)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ClientCommon) SendObj(key string, val interface{}) error {
|
|
|
|
|
data, err := encode(val)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
_, err = star.Connc.Write(star.Queue.BuildMessage([]byte(data.mode + "||" + string(key) + "||" + value)))
|
|
|
|
|
_, err = c.send(TransferMsg{
|
|
|
|
|
Key: key,
|
|
|
|
|
Value: data,
|
|
|
|
|
Type: MSG_ASYNC,
|
|
|
|
|
})
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (star *StarNotifyC) cnotify() {
|
|
|
|
|
for {
|
|
|
|
|
select {
|
|
|
|
|
case <-star.stopSign.Done():
|
|
|
|
|
return
|
|
|
|
|
default:
|
|
|
|
|
}
|
|
|
|
|
data, err := star.Queue.RestoreOne()
|
|
|
|
|
if err != nil {
|
|
|
|
|
time.Sleep(time.Millisecond * 500)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if string(data.Msg) == "b612ryzstop" {
|
|
|
|
|
star.ClientStop()
|
|
|
|
|
star.Online = false
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
strs := strings.SplitN(string(data.Msg), "||", 3)
|
|
|
|
|
if len(strs) < 3 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
strs[1] = star.trim(strs[1])
|
|
|
|
|
if star.UseChannel {
|
|
|
|
|
go star.store(strs[1], strs[2])
|
|
|
|
|
} else {
|
|
|
|
|
mode, key, value := strs[0], strs[1], strs[2]
|
|
|
|
|
if mode[0:2] != "cr" {
|
|
|
|
|
if msg, ok := star.FuncLists[key]; ok {
|
|
|
|
|
if star.Sync {
|
|
|
|
|
msg(CMsg{key, value, mode, nil})
|
|
|
|
|
} else {
|
|
|
|
|
go msg(CMsg{key, value, mode, nil})
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if star.defaultFunc != nil {
|
|
|
|
|
if star.Sync {
|
|
|
|
|
star.defaultFunc(CMsg{key, value, mode, nil})
|
|
|
|
|
} else {
|
|
|
|
|
go star.defaultFunc(CMsg{key, value, mode, nil})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if sa, ok := star.lockPool[mode]; ok {
|
|
|
|
|
sa.Key = key
|
|
|
|
|
sa.Value = value
|
|
|
|
|
sa.mode = mode
|
|
|
|
|
star.mu.Lock()
|
|
|
|
|
star.lockPool[mode] = sa
|
|
|
|
|
star.mu.Unlock()
|
|
|
|
|
sa.wait <- 1
|
|
|
|
|
} else {
|
|
|
|
|
if msg, ok := star.FuncLists[key]; ok {
|
|
|
|
|
if star.Sync {
|
|
|
|
|
msg(CMsg{key, value, mode, nil})
|
|
|
|
|
} else {
|
|
|
|
|
go msg(CMsg{key, value, mode, nil})
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if star.defaultFunc != nil {
|
|
|
|
|
if star.Sync {
|
|
|
|
|
star.defaultFunc(CMsg{key, value, mode, nil})
|
|
|
|
|
} else {
|
|
|
|
|
go star.defaultFunc(CMsg{key, value, mode, nil})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
func (c *ClientCommon) SendCtx(ctx context.Context, key string, value MsgVal) (Message, error) {
|
|
|
|
|
return c.sendCtx(TransferMsg{
|
|
|
|
|
Key: key,
|
|
|
|
|
Value: value,
|
|
|
|
|
Type: MSG_SYNC_ASK,
|
|
|
|
|
}, ctx)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ClientCommon) SendWait(key string, value MsgVal, timeout time.Duration) (Message, error) {
|
|
|
|
|
return c.sendWait(TransferMsg{
|
|
|
|
|
Key: key,
|
|
|
|
|
Value: value,
|
|
|
|
|
Type: MSG_SYNC_ASK,
|
|
|
|
|
}, timeout)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ClientCommon) Reply(m Message, value MsgVal) error {
|
|
|
|
|
return m.Reply(value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ClientCommon) ExchangeKey(newKey []byte) error {
|
|
|
|
|
newSendKey, err := starcrypto.RSAEncrypt(newKey, c.handshakeRsaPubKey)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
data, err := c.sendWait(TransferMsg{
|
|
|
|
|
ID: 19961127,
|
|
|
|
|
Key: "sirius",
|
|
|
|
|
Value: newSendKey,
|
|
|
|
|
Type: MSG_KEY_CHANGE,
|
|
|
|
|
}, time.Second*10)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if string(data.Value) != "success" {
|
|
|
|
|
return errors.New("cannot exchange new aes-key")
|
|
|
|
|
}
|
|
|
|
|
c.SecretKey = newKey
|
|
|
|
|
time.Sleep(time.Millisecond * 100)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ClientStop 终止client端运行
|
|
|
|
|
func (star *StarNotifyC) ClientStop() {
|
|
|
|
|
if star.isUDP {
|
|
|
|
|
star.Send("b612ryzstop")
|
|
|
|
|
func aesRsaHello(c Client) error {
|
|
|
|
|
newAesKey := []byte(fmt.Sprintf("%d%d%d%s", time.Now().UnixNano(), rand.Int63(), rand.Int63(), "b612.me"))
|
|
|
|
|
newAesKey = []byte(starcrypto.Md5Str(newAesKey))
|
|
|
|
|
return c.ExchangeKey(newAesKey)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ClientCommon) GetMsgEn() func([]byte, []byte) []byte {
|
|
|
|
|
return c.msgEn
|
|
|
|
|
}
|
|
|
|
|
func (c *ClientCommon) SetMsgEn(fn func([]byte, []byte) []byte) {
|
|
|
|
|
c.msgEn = fn
|
|
|
|
|
}
|
|
|
|
|
func (c *ClientCommon) GetMsgDe() func([]byte, []byte) []byte {
|
|
|
|
|
return c.msgDe
|
|
|
|
|
}
|
|
|
|
|
func (c *ClientCommon) SetMsgDe(fn func([]byte, []byte) []byte) {
|
|
|
|
|
c.msgDe = fn
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ClientCommon) HeartbeatPeroid() time.Duration {
|
|
|
|
|
return c.heartbeatPeriod
|
|
|
|
|
}
|
|
|
|
|
func (c *ClientCommon) SetHeartbeatPeroid(duration time.Duration) {
|
|
|
|
|
c.heartbeatPeriod = duration
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ClientCommon) GetSecretKey() []byte {
|
|
|
|
|
return c.SecretKey
|
|
|
|
|
}
|
|
|
|
|
func (c *ClientCommon) SetSecretKey(key []byte) {
|
|
|
|
|
c.SecretKey = key
|
|
|
|
|
}
|
|
|
|
|
func (c *ClientCommon) RsaPubKey() []byte {
|
|
|
|
|
return c.handshakeRsaPubKey
|
|
|
|
|
}
|
|
|
|
|
func (c *ClientCommon) SetRsaPubKey(key []byte) {
|
|
|
|
|
c.handshakeRsaPubKey = key
|
|
|
|
|
}
|
|
|
|
|
func (c *ClientCommon) Stop() error {
|
|
|
|
|
if !c.alive.Load().(bool) {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
c.alive.Store(false)
|
|
|
|
|
c.mu.Lock()
|
|
|
|
|
c.status = Status{
|
|
|
|
|
Alive: false,
|
|
|
|
|
Reason: "recv stop signal from user",
|
|
|
|
|
Err: nil,
|
|
|
|
|
}
|
|
|
|
|
star.cancel()
|
|
|
|
|
c.mu.Unlock()
|
|
|
|
|
c.stopFn()
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
func (c *ClientCommon) StopMonitorChan() <-chan struct{} {
|
|
|
|
|
return c.stopCtx.Done()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetNotify 用于设置关键词的调用函数
|
|
|
|
|
func (star *StarNotifyC) SetNotify(name string, data func(CMsg)) {
|
|
|
|
|
star.FuncLists[name] = data
|
|
|
|
|
func (c *ClientCommon) Status() Status {
|
|
|
|
|
return c.status
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetDefaultNotify 用于设置默认关键词的调用函数
|
|
|
|
|
func (star *StarNotifyC) SetDefaultNotify(data func(CMsg)) {
|
|
|
|
|
star.defaultFunc = data
|
|
|
|
|
func (c *ClientCommon) GetSequenceEn() func(interface{}) ([]byte, error) {
|
|
|
|
|
return c.sequenceEn
|
|
|
|
|
}
|
|
|
|
|
func (c *ClientCommon) SetSequenceEn(fn func(interface{}) ([]byte, error)) {
|
|
|
|
|
c.sequenceEn = fn
|
|
|
|
|
}
|
|
|
|
|
func (c *ClientCommon) GetSequenceDe() func([]byte) (interface{}, error) {
|
|
|
|
|
return c.sequenceDe
|
|
|
|
|
}
|
|
|
|
|
func (c *ClientCommon) SetSequenceDe(fn func([]byte) (interface{}, error)) {
|
|
|
|
|
c.sequenceDe = fn
|
|
|
|
|
}
|
|
|
|
|