v2 version release
parent
bdcfcd05db
commit
555bc3653e
@ -0,0 +1,47 @@
|
|||||||
|
package notify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client interface {
|
||||||
|
SetDefaultLink(func(message *Message))
|
||||||
|
SetLink(string, func(*Message))
|
||||||
|
send(msg TransferMsg) (WaitMsg, error)
|
||||||
|
sendWait(msg TransferMsg, timeout time.Duration) (Message, error)
|
||||||
|
Send(key string, value MsgVal) error
|
||||||
|
SendWait(key string, value MsgVal, timeout time.Duration) (Message, error)
|
||||||
|
SendCtx(ctx context.Context, key string, value MsgVal) (Message, error)
|
||||||
|
Reply(m Message, value MsgVal) error
|
||||||
|
ExchangeKey(newKey []byte) error
|
||||||
|
Connect(network string, addr string) error
|
||||||
|
ConnectTimeout(network string, addr string, timeout time.Duration) error
|
||||||
|
SkipExchangeKey() bool
|
||||||
|
SetSkipExchangeKey(bool)
|
||||||
|
|
||||||
|
GetMsgEn() func([]byte, []byte) []byte
|
||||||
|
SetMsgEn(func([]byte, []byte) []byte)
|
||||||
|
GetMsgDe() func([]byte, []byte) []byte
|
||||||
|
SetMsgDe(func([]byte, []byte) []byte)
|
||||||
|
|
||||||
|
Heartbeat()
|
||||||
|
HeartbeatPeroid() time.Duration
|
||||||
|
SetHeartbeatPeroid(duration time.Duration)
|
||||||
|
|
||||||
|
GetSecretKey() []byte
|
||||||
|
SetSecretKey(key []byte)
|
||||||
|
RsaPubKey() []byte
|
||||||
|
SetRsaPubKey([]byte)
|
||||||
|
|
||||||
|
Stop() error
|
||||||
|
StopMonitorChan() <-chan struct{}
|
||||||
|
Status() Status
|
||||||
|
|
||||||
|
GetSequenceEn() func(interface{}) ([]byte, error)
|
||||||
|
SetSequenceEn(func(interface{}) ([]byte, error))
|
||||||
|
GetSequenceDe() func([]byte) (interface{}, error)
|
||||||
|
SetSequenceDe(func([]byte) (interface{}, error))
|
||||||
|
SendObjCtx(ctx context.Context, key string, val interface{}) (Message, error)
|
||||||
|
SendObj(key string, val interface{}) error
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
package notify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"b612.me/starcrypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
var defaultRsaKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
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
|
||||||
|
-----END RSA PRIVATE KEY-----`)
|
||||||
|
|
||||||
|
var defaultRsaPubKey = []byte(`-----BEGIN PUBLIC KEY-----
|
||||||
|
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxmeMqr9yfJFKZn26oe/H
|
||||||
|
vC7bZXNLC9Nk55AuTkb4XuIoqXDbAJD2Y/p167oJLKIqL3edcj7h+oTfn6s79vxT
|
||||||
|
0ZCEf37ILU0G+scRzVwYHiLMwOUCbS2o4Xor3zqUi9f1piJBvoBNh8RKKtsmJW6V
|
||||||
|
QZdiUGJHbgX4MdOdtf/6TvxZMwSXU+PRSCAjy04A31Zi7DEWUWJPyqmHeu++PxXU
|
||||||
|
5lvoMdCGDqpcF2j2uO7oJJUww01M3F5FtTElMrK4/P9gD4kP7NiPhOfVPEfBsYT/
|
||||||
|
DSSjvqNZJZuWnxu+cDxE7J/sBvdpeNRLhqzdmMYagZFuUmVrz8QmsD6jKHgydW+r
|
||||||
|
7irllvb8WJPK/RIMif+4Rg7rDKFbj8+ZQ3HZ/gKELoRSyb3zL6RC2qlGLjC1tdeN
|
||||||
|
7TNTinCv092y39T8jIARJ7tpfePhNBxsBdxfXbCAzHYZIHufI9Zlsc+felQwanlD
|
||||||
|
hq+q8YLcnKHvNKYVyCf/upExpAiArr88y/KbeKes0KorKkwMBnGUMTothWM25wHo
|
||||||
|
zcurixNvP4UMWX7LWD7vOZZuNDQNutZYeTwdsniI3mTO9vlPWEK8JTfxBU7x9SeP
|
||||||
|
UMJNDyjfDUJM8C2DOlyhGNPkgazOGdliH87tHkEy/7jJnGclgKmciiVPgwHfFx9G
|
||||||
|
GoBHEfvmAoGGrk4qNbjm7JECAwEAAQ==
|
||||||
|
-----END PUBLIC KEY-----`)
|
||||||
|
|
||||||
|
var defaultAesKey = []byte{0x19, 0x96, 0x11, 0x27, 228, 187, 187, 231, 142, 137, 230, 179, 189, 229, 184, 133}
|
||||||
|
|
||||||
|
func defaultMsgEn(key []byte, d []byte) []byte {
|
||||||
|
return starcrypto.AesEncryptCFB(d, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultMsgDe(key []byte, d []byte) []byte {
|
||||||
|
return starcrypto.AesDecryptCFB(d, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Register(TransferMsg{})
|
||||||
|
}
|
@ -0,0 +1,472 @@
|
|||||||
|
package notify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"b612.me/starcrypto"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MSG_SYS MessageType = iota
|
||||||
|
MSG_SYS_WAIT
|
||||||
|
MSG_SYS_REPLY
|
||||||
|
MSG_KEY_CHANGE
|
||||||
|
MSG_ASYNC
|
||||||
|
MSG_SYNC_ASK
|
||||||
|
MSG_SYNC_REPLY
|
||||||
|
)
|
||||||
|
|
||||||
|
type MessageType uint8
|
||||||
|
|
||||||
|
type NetType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
NET_SERVER NetType = iota
|
||||||
|
NET_CLIENT
|
||||||
|
)
|
||||||
|
|
||||||
|
type MsgVal []byte
|
||||||
|
type TransferMsg struct {
|
||||||
|
ID uint64
|
||||||
|
Key string
|
||||||
|
Value MsgVal
|
||||||
|
Type MessageType
|
||||||
|
}
|
||||||
|
|
||||||
|
type Message struct {
|
||||||
|
NetType
|
||||||
|
ClientConn *ClientConn
|
||||||
|
ServerConn Client
|
||||||
|
TransferMsg
|
||||||
|
Time time.Time
|
||||||
|
sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
type WaitMsg struct {
|
||||||
|
TransferMsg
|
||||||
|
Time time.Time
|
||||||
|
Reply chan Message
|
||||||
|
//Ctx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Message) Reply(value MsgVal) (err error) {
|
||||||
|
reply := TransferMsg{
|
||||||
|
ID: m.ID,
|
||||||
|
Key: m.Key,
|
||||||
|
Value: value,
|
||||||
|
Type: m.Type,
|
||||||
|
}
|
||||||
|
if reply.Type == MSG_SYNC_ASK {
|
||||||
|
reply.Type = MSG_SYNC_REPLY
|
||||||
|
}
|
||||||
|
if reply.Type == MSG_SYS_WAIT {
|
||||||
|
reply.Type = MSG_SYS_REPLY
|
||||||
|
}
|
||||||
|
if m.NetType == NET_SERVER {
|
||||||
|
_, err = m.ClientConn.server.send(m.ClientConn, reply)
|
||||||
|
}
|
||||||
|
if m.NetType == NET_CLIENT {
|
||||||
|
_, err = m.ServerConn.send(reply)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Message) ReplyObj(value interface{}) (err error) {
|
||||||
|
data, err := encode(value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return m.Reply(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientConn struct {
|
||||||
|
alive atomic.Value
|
||||||
|
status Status
|
||||||
|
ClientID string
|
||||||
|
ClientAddr net.Addr
|
||||||
|
tuConn net.Conn
|
||||||
|
server Server
|
||||||
|
stopFn context.CancelFunc
|
||||||
|
stopCtx context.Context
|
||||||
|
maxReadTimeout time.Duration
|
||||||
|
maxWriteTimeout time.Duration
|
||||||
|
msgEn func([]byte, []byte) []byte
|
||||||
|
msgDe func([]byte, []byte) []byte
|
||||||
|
handshakeRsaKey []byte
|
||||||
|
SecretKey []byte
|
||||||
|
lastHeartBeat int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type Status struct {
|
||||||
|
Alive bool
|
||||||
|
Reason string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientConn) readTUMessage() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-c.stopCtx.Done():
|
||||||
|
c.tuConn.Close()
|
||||||
|
c.server.removeClient(c)
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
if c.maxReadTimeout.Seconds() > 0 {
|
||||||
|
c.tuConn.SetReadDeadline(time.Now().Add(c.maxReadTimeout))
|
||||||
|
}
|
||||||
|
data := make([]byte, 8192)
|
||||||
|
num, err := c.tuConn.Read(data)
|
||||||
|
if err == os.ErrDeadlineExceeded {
|
||||||
|
if num != 0 {
|
||||||
|
c.server.pushMessage(data[:num], c.ClientID)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
//conn is broke
|
||||||
|
c.alive.Store(false)
|
||||||
|
c.status = Status{
|
||||||
|
Alive: false,
|
||||||
|
Reason: "read error",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
c.stopFn()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
c.server.pushMessage(data[:num], c.ClientID)
|
||||||
|
//fmt.Println("finished:", float64(time.Now().UnixNano()-nowd)/1000000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientConn) rsaDecode(message Message) {
|
||||||
|
unknownKey := message.Value
|
||||||
|
data, err := starcrypto.RSADecrypt(unknownKey, c.handshakeRsaKey, "")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
message.Reply([]byte("failed"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//fmt.Println("aes-key changed to", string(data))
|
||||||
|
message.Reply([]byte("success"))
|
||||||
|
c.SecretKey = data
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientConn) sayGoodByeForTU() error {
|
||||||
|
_, 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.SecretKey
|
||||||
|
}
|
||||||
|
func (c *ClientConn) SetSecretKey(key []byte) {
|
||||||
|
c.SecretKey = key
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientConn) GetMsgEn() func([]byte, []byte) []byte {
|
||||||
|
return c.msgEn
|
||||||
|
}
|
||||||
|
func (c *ClientConn) SetMsgEn(fn func([]byte, []byte) []byte) {
|
||||||
|
c.msgEn = fn
|
||||||
|
}
|
||||||
|
func (c *ClientConn) GetMsgDe() func([]byte, []byte) []byte {
|
||||||
|
return c.msgDe
|
||||||
|
}
|
||||||
|
func (c *ClientConn) SetMsgDe(fn func([]byte, []byte) []byte) {
|
||||||
|
c.msgDe = fn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientConn) StopMonitorChan() <-chan struct{} {
|
||||||
|
return c.stopCtx.Done()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientConn) Status() Status {
|
||||||
|
return c.status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientConn) Server() Server {
|
||||||
|
return c.server
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientConn) GetRemoteAddr() net.Addr {
|
||||||
|
return c.ClientAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MsgVal) ToClearString() string {
|
||||||
|
return string(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MsgVal) ToInterface() (interface{}, error) {
|
||||||
|
return Decode(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MsgVal) MustToInterface() interface{} {
|
||||||
|
inf, err := m.ToInterface()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return inf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MsgVal) ToString() (string, error) {
|
||||||
|
inf, err := m.ToInterface()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if data, ok := inf.(string); !ok {
|
||||||
|
return "", errors.New("source data not match target type")
|
||||||
|
} else {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MsgVal) MustToString() string {
|
||||||
|
inf, err := m.ToString()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return inf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MsgVal) ToInt32() (int32, error) {
|
||||||
|
inf, err := m.ToInterface()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if data, ok := inf.(int32); !ok {
|
||||||
|
return 0, errors.New("source data not match target type")
|
||||||
|
} else {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MsgVal) MustToInt32() int32 {
|
||||||
|
inf, err := m.ToInt32()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return inf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MsgVal) ToInt() (int, error) {
|
||||||
|
inf, err := m.ToInterface()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if data, ok := inf.(int); !ok {
|
||||||
|
return 0, errors.New("source data not match target type")
|
||||||
|
} else {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MsgVal) MustToInt() int {
|
||||||
|
inf, err := m.ToInt()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return inf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MsgVal) ToUint64() (uint64, error) {
|
||||||
|
inf, err := m.ToInterface()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if data, ok := inf.(uint64); !ok {
|
||||||
|
return 0, errors.New("source data not match target type")
|
||||||
|
} else {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MsgVal) MustToUint64() uint64 {
|
||||||
|
inf, err := m.ToUint64()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return inf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MsgVal) ToUint32() (uint32, error) {
|
||||||
|
inf, err := m.ToInterface()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if data, ok := inf.(uint32); !ok {
|
||||||
|
return 0, errors.New("source data not match target type")
|
||||||
|
} else {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MsgVal) MustToUint32() uint32 {
|
||||||
|
inf, err := m.ToUint32()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return inf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MsgVal) ToUint() (uint, error) {
|
||||||
|
inf, err := m.ToInterface()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if data, ok := inf.(uint); !ok {
|
||||||
|
return 0, errors.New("source data not match target type")
|
||||||
|
} else {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MsgVal) MustToUint() uint {
|
||||||
|
inf, err := m.ToUint()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return inf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MsgVal) ToBool() (bool, error) {
|
||||||
|
inf, err := m.ToInterface()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if data, ok := inf.(bool); !ok {
|
||||||
|
return false, errors.New("source data not match target type")
|
||||||
|
} else {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MsgVal) MustToBool() bool {
|
||||||
|
inf, err := m.ToBool()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return inf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MsgVal) ToFloat64() (float64, error) {
|
||||||
|
inf, err := m.ToInterface()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if data, ok := inf.(float64); !ok {
|
||||||
|
return 0, errors.New("source data not match target type")
|
||||||
|
} else {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MsgVal) MustToFloat64() float64 {
|
||||||
|
inf, err := m.ToFloat64()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return inf
|
||||||
|
}
|
||||||
|
func (m MsgVal) ToFloat32() (float32, error) {
|
||||||
|
inf, err := m.ToInterface()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if data, ok := inf.(float32); !ok {
|
||||||
|
return 0, errors.New("source data not match target type")
|
||||||
|
} else {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MsgVal) MustToFloat32() float32 {
|
||||||
|
inf, err := m.ToFloat32()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return inf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MsgVal) ToSliceString() ([]string, error) {
|
||||||
|
inf, err := m.ToInterface()
|
||||||
|
if err != nil {
|
||||||
|
return []string{}, err
|
||||||
|
}
|
||||||
|
if data, ok := inf.([]string); !ok {
|
||||||
|
return []string{}, errors.New("source data not match target type")
|
||||||
|
} else {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MsgVal) MustToSliceString() []string {
|
||||||
|
inf, err := m.ToSliceString()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return inf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MsgVal) ToSliceInt64() ([]int64, error) {
|
||||||
|
inf, err := m.ToInterface()
|
||||||
|
if err != nil {
|
||||||
|
return []int64{}, err
|
||||||
|
}
|
||||||
|
if data, ok := inf.([]int64); !ok {
|
||||||
|
return []int64{}, errors.New("source data not match target type")
|
||||||
|
} else {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MsgVal) MustToSliceInt64() []int64 {
|
||||||
|
inf, err := m.ToSliceInt64()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return inf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MsgVal) ToSliceFloat64() ([]float64, error) {
|
||||||
|
inf, err := m.ToInterface()
|
||||||
|
if err != nil {
|
||||||
|
return []float64{}, err
|
||||||
|
}
|
||||||
|
if data, ok := inf.([]float64); !ok {
|
||||||
|
return []float64{}, errors.New("source data not match target type")
|
||||||
|
} else {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MsgVal) MustToSliceFloat64() []float64 {
|
||||||
|
inf, err := m.ToSliceFloat64()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return inf
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToMsgVal(val interface{}) (MsgVal, error) {
|
||||||
|
return Encode(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MustToMsgVal(val interface{}) MsgVal {
|
||||||
|
d, err := ToMsgVal(val)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
package notify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server interface {
|
||||||
|
SetDefaultCommEncode(func([]byte, []byte) []byte)
|
||||||
|
SetDefaultCommDecode(func([]byte, []byte) []byte)
|
||||||
|
SetDefaultLink(func(message *Message))
|
||||||
|
SetLink(string, func(*Message))
|
||||||
|
send(c *ClientConn, msg TransferMsg) (WaitMsg, error)
|
||||||
|
sendWait(c *ClientConn, msg TransferMsg, timeout time.Duration) (Message, error)
|
||||||
|
SendObjCtx(ctx context.Context, c *ClientConn, key string, val interface{}) (Message, error)
|
||||||
|
SendObj(c *ClientConn, key string, val interface{}) error
|
||||||
|
Send(c *ClientConn, key string, value MsgVal) error
|
||||||
|
SendWait(c *ClientConn, key string, value MsgVal, timeout time.Duration) (Message, error)
|
||||||
|
SendCtx(ctx context.Context, c *ClientConn, key string, value MsgVal) (Message, error)
|
||||||
|
Reply(m Message, value MsgVal) error
|
||||||
|
pushMessage([]byte, string)
|
||||||
|
removeClient(client *ClientConn)
|
||||||
|
Listen(network string, addr string) error
|
||||||
|
Stop() error
|
||||||
|
StopMonitorChan() <-chan struct{}
|
||||||
|
Status() Status
|
||||||
|
|
||||||
|
GetSecretKey() []byte
|
||||||
|
SetSecretKey(key []byte)
|
||||||
|
RsaPrivKey() []byte
|
||||||
|
SetRsaPrivKey([]byte)
|
||||||
|
|
||||||
|
GetClient(id string) *ClientConn
|
||||||
|
GetClientLists() []*ClientConn
|
||||||
|
GetClientAddrs() []net.Addr
|
||||||
|
|
||||||
|
GetSequenceEn() func(interface{}) ([]byte,error)
|
||||||
|
SetSequenceEn(func(interface{}) ([]byte,error))
|
||||||
|
GetSequenceDe() func([]byte) (interface{}, error)
|
||||||
|
SetSequenceDe(func([]byte) (interface{}, error))
|
||||||
|
|
||||||
|
HeartbeatTimeoutSec()int64
|
||||||
|
SetHeartbeatTimeoutSec(int64)
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package notify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/gob"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Register(data interface{}) {
|
||||||
|
gob.Register(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterAll(data []interface{}) {
|
||||||
|
for _, v := range data {
|
||||||
|
gob.Register(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func encode(src interface{}) ([]byte, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
enc := gob.NewEncoder(&buf)
|
||||||
|
err := enc.Encode(&src)
|
||||||
|
return buf.Bytes(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func Decode(src []byte) (interface{}, error) {
|
||||||
|
dec := gob.NewDecoder(bytes.NewReader(src))
|
||||||
|
var dst interface{}
|
||||||
|
err := dec.Decode(&dst)
|
||||||
|
return dst, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nmsg *SMsg) Decode() (interface{}, error) {
|
||||||
|
return Decode([]byte(nmsg.Value))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nmsg *CMsg) Decode() (interface{}, error) {
|
||||||
|
return Decode([]byte(nmsg.Value))
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
package starnotify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"b612.me/notify/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
starClient map[string]*notify.StarNotifyC
|
||||||
|
starServer map[string]*notify.StarNotifyS
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
starClient = make(map[string]*notify.StarNotifyC)
|
||||||
|
starServer = make(map[string]*notify.StarNotifyS)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(key, netype, value string) (*notify.StarNotifyC, error) {
|
||||||
|
client, err := notify.NewNotifyC(netype, value)
|
||||||
|
if err != nil {
|
||||||
|
return client, err
|
||||||
|
}
|
||||||
|
starClient[key] = client
|
||||||
|
return client, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClientWithTimeout(key, netype, value string, timeout time.Duration) (*notify.StarNotifyC, error) {
|
||||||
|
client, err := notify.NewNotifyCWithTimeOut(netype, value, timeout)
|
||||||
|
if err != nil {
|
||||||
|
return client, err
|
||||||
|
}
|
||||||
|
starClient[key] = client
|
||||||
|
return client, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteClient(key string) error {
|
||||||
|
client, ok := starClient[key]
|
||||||
|
if !ok {
|
||||||
|
return errors.New("Not Exists Yet!")
|
||||||
|
}
|
||||||
|
if client.Online {
|
||||||
|
client.ClientStop()
|
||||||
|
}
|
||||||
|
client = nil
|
||||||
|
delete(starClient, key)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServer(key, netype, value string) (*notify.StarNotifyS, error) {
|
||||||
|
server, err := notify.NewNotifyS(netype, value)
|
||||||
|
if err != nil {
|
||||||
|
return server, err
|
||||||
|
}
|
||||||
|
starServer[key] = server
|
||||||
|
return server, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteServer(key string) error {
|
||||||
|
server, ok := starServer[key]
|
||||||
|
if !ok {
|
||||||
|
return errors.New("Not Exists Yet!")
|
||||||
|
}
|
||||||
|
if server.Online {
|
||||||
|
server.ServerStop()
|
||||||
|
}
|
||||||
|
server = nil
|
||||||
|
delete(starServer, key)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func S(key string) *notify.StarNotifyS {
|
||||||
|
server, ok := starServer[key]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return server
|
||||||
|
}
|
||||||
|
|
||||||
|
func C(key string) *notify.StarNotifyC {
|
||||||
|
client, ok := starClient[key]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
func Server(key string) (*notify.StarNotifyS, error) {
|
||||||
|
server, ok := starServer[key]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("Not Exists Yet")
|
||||||
|
}
|
||||||
|
return server, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Client(key string) (*notify.StarNotifyC, error) {
|
||||||
|
client, ok := starClient[key]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("Not Exists Yet")
|
||||||
|
}
|
||||||
|
return client, nil
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package notify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync/atomic"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_ServerTuAndClientCommon(t *testing.T) {
|
||||||
|
server, err := NewNotifyS("tcp", "127.0.0.1:12345")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
server.SetNotify("notify", notify)
|
||||||
|
for i := 1; i <= 1; i++ {
|
||||||
|
go func() {
|
||||||
|
|
||||||
|
client, err := NewNotifyC("tcp", "127.0.0.1:12345")
|
||||||
|
if err != nil {
|
||||||
|
time.Sleep(time.Second * 2)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
//nowd = time.Now().UnixNano()
|
||||||
|
client.SendValueWait("notify", "client hello", time.Second*50)
|
||||||
|
//time.Sleep(time.Millisecond)
|
||||||
|
//fmt.Println("finished:", float64(time.Now().UnixNano()-nowd)/1000000)
|
||||||
|
//client.Send("notify", []byte("client hello"))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
time.Sleep(time.Second * 10)
|
||||||
|
server.ServerStop()
|
||||||
|
}()
|
||||||
|
<-server.Stoped()
|
||||||
|
//time.Sleep(time.Second * 5)
|
||||||
|
fmt.Println(count2)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var count2 int64
|
||||||
|
|
||||||
|
func notify(msg SMsg) string {
|
||||||
|
//fmt.Println(string(msg.Msg.Value))
|
||||||
|
//fmt.Println("called:", float64(time.Now().UnixNano()-nowd)/1000000)
|
||||||
|
|
||||||
|
go atomic.AddInt64(&count2, 1)
|
||||||
|
return "ok"
|
||||||
|
}
|
@ -0,0 +1,146 @@
|
|||||||
|
package notify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
//_ "net/http/pprof"
|
||||||
|
"sync/atomic"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_ServerTuAndClientCommon(t *testing.T) {
|
||||||
|
//go http.ListenAndServe("0.0.0.0:8888", nil)
|
||||||
|
noEn := func(key, bn []byte) []byte {
|
||||||
|
return bn
|
||||||
|
}
|
||||||
|
server := NewServer()
|
||||||
|
server.SetDefaultCommDecode(noEn)
|
||||||
|
server.SetDefaultCommEncode(noEn)
|
||||||
|
err := server.Listen("tcp", "127.0.0.1:12345")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
server.SetLink("notify", notify)
|
||||||
|
for i := 1; i <= 5000; i++ {
|
||||||
|
go func() {
|
||||||
|
client := NewClient()
|
||||||
|
client.SetMsgEn(noEn)
|
||||||
|
client.SetMsgDe(noEn)
|
||||||
|
client.SetSkipExchangeKey(true)
|
||||||
|
err = client.Connect("tcp", "127.0.0.1:12345")
|
||||||
|
if err != nil {
|
||||||
|
time.Sleep(time.Second * 2)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//client.SetLink("notify", notify)
|
||||||
|
for {
|
||||||
|
|
||||||
|
//nowd = time.Now().UnixNano()
|
||||||
|
client.SendWait("notify", []byte("client hello"),time.Second*15)
|
||||||
|
//time.Sleep(time.Millisecond)
|
||||||
|
//fmt.Println("finished:", float64(time.Now().UnixNano()-nowd)/1000000)
|
||||||
|
//client.Send("notify", []byte("client"))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
go func() {
|
||||||
|
time.Sleep(time.Second * 10)
|
||||||
|
server.Stop()
|
||||||
|
}()
|
||||||
|
<-server.StopMonitorChan()
|
||||||
|
fmt.Println(count2)
|
||||||
|
}
|
||||||
|
|
||||||
|
var count2 int64
|
||||||
|
|
||||||
|
func notify(msg *Message) {
|
||||||
|
//fmt.Println(string(msg.Msg.Value))
|
||||||
|
//fmt.Println("called:", float64(time.Now().UnixNano()-nowd)/1000000)
|
||||||
|
if msg.NetType == NET_SERVER {
|
||||||
|
atomic.AddInt64(&count2, 1)
|
||||||
|
msg.Reply([]byte("server reply"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_normal(t *testing.T) {
|
||||||
|
server, _ := net.Listen("udp", "127.0.0.1:12345")
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
conn, err := server.Accept()
|
||||||
|
if err == nil {
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
buf := make([]byte, 1024)
|
||||||
|
_, err := conn.Read(buf)
|
||||||
|
//fmt.Println("S RECV", string(buf[:i]))
|
||||||
|
atomic.AddInt64(&count2, 1)
|
||||||
|
if err == nil {
|
||||||
|
conn.Write([]byte("hello world server"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
time.Sleep(time.Second * 5)
|
||||||
|
for i := 1; i <= 100; i++ {
|
||||||
|
go func() {
|
||||||
|
conn, err := net.Dial("udp", "127.0.0.1:12345")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
//nowd = time.Now().UnixNano()
|
||||||
|
_, err := conn.Write([]byte("hello world client"))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
buf := make([]byte, 1024)
|
||||||
|
conn.Read(buf)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
time.Sleep(time.Second * 10)
|
||||||
|
fmt.Println(count2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_normal_udp(t *testing.T) {
|
||||||
|
ludp, _ := net.ResolveUDPAddr("udp", "127.0.0.1:12345")
|
||||||
|
conn, _ := net.ListenUDP("udp", ludp)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
buf := make([]byte, 1024)
|
||||||
|
_, addr, err := conn.ReadFromUDP(buf)
|
||||||
|
fmt.Println(time.Now(), "S RECV", addr.String())
|
||||||
|
atomic.AddInt64(&count2, 1)
|
||||||
|
if err == nil {
|
||||||
|
conn.WriteToUDP([]byte("hello world server"), addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
for i := 1; i <= 100; i++ {
|
||||||
|
go func() {
|
||||||
|
conn, err := net.Dial("udp", "127.0.0.1:12345")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
//nowd = time.Now().UnixNano()
|
||||||
|
_, err := conn.Write([]byte("hello world client"))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
buf := make([]byte, 1024)
|
||||||
|
conn.Read(buf)
|
||||||
|
fmt.Println(time.Now(), "C RECV")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
time.Sleep(time.Second * 10)
|
||||||
|
fmt.Println(count2)
|
||||||
|
}
|
Loading…
Reference in New Issue