├── .github
├── SSL流程.png
├── SSL流程.xml
├── 主流程图.png
└── 主流程图.xml
├── .vscode
├── launch.json
└── settings.json
├── README.md
├── api.go
├── encrypt.go
├── factory.go
├── filter.go
├── guid.go
├── listener.go
├── persistence
├── msgMemory.go
├── msgRedis.go
└── offlineMsg.go
├── pool
├── connClear.go
├── pool.go
└── poolMemory.go
├── protocol
└── protocol.go
├── proxy.go
├── queue
├── message.go
├── queue.go
└── queueMemory.go
├── sender.go
├── senderScheduler.go
├── serialize
├── json.go
└── serialize.go
├── ssl.go
└── tests
├── debug
└── main.go
/.github/SSL流程.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaogan18/msgserver/3a1c12aafadd190efaffe81805753e55d8fa62f1/.github/SSL流程.png
--------------------------------------------------------------------------------
/.github/SSL流程.xml:
--------------------------------------------------------------------------------
1 | 7Vpdj6s2EP01lm4fWoVv8wgJaaW20uruldo+esEhaAmOwLlJ+us7BkMAwyZNyG6V7j7s4vEnc874jM0iY745/JyT7fp3FtEU6bPogIwF0nXN1G34IyzHyuK4s8oQ50kkG50Mz8nfVBrrZrskokWnIWcs5cm2awxZltGQd2wkz9m+22zF0u6sWxJTxfAcklS1/pFEfF1Zse6c7L/QJF7XM2u2W9W8kPA1ztkuk/Mh3ViVP1X1htRjyRct1iRi+5bJCJAxzxnj1dPmMKep8G3ttqrfcqS2WXdOM35JB73q8J2kO/nqKLCQFyAPHmyEMfIdFDjI85G3lEvmx9pNsPqteNxt0t+SFU2TDEr+lubJhnKaQ00qzU8nm79fJ5w+b0kouu6BQGBb800KJQ0eAVNOoEvelNOUbIvkpZx1Bpachru8SL7Tr7SoqCOsbMfFTPOGEsK4gsEkvTQbyvJ1ac7pYdRlWgMEEJwyWHd+hCayQ4PdseasLO9PTGls6xZLzDoIiGRn3Ix9QggeJEgjCDsqYn1YWs7csiTj5XSWj6xFDx2W8zWLWUbSNj5X+Ux/02em1XWZawy4TPUYdm53mDHAcBu5c4QXgurYQ572yfAOWtbHMtz6bzLc+DcM1/GQywYYPoHDzME93PeR70liYx8FsJMvkRuUuzps79ZbPhWeSEANvTSJM7C9MM7ZBipoFnlCXoUtZeHrqC9ppAhsj30wN9vlIe3oECd5TN/2d8uhtfNymhIOwdJNAwY8Kkd7EpRpbei4R/c+i6uFyl5tMe0NZNhnBqpeTxkIXEqOrWaS0qMLbsKsJltX5OGhGvHEnsZ/FxFKDcDi6fVWvrAthSo/IsWaRrIfVEvq4MmYZKhMGtGmG2ljWT0U+rF8KW1M58xAE9Gmv63r9rS0cRXafH32ynRyibCNAhe5uth4KovvogB+z5HrCXYFBoLXxlrxlKtUg22ZdyU0F7pIToKpqgDZcSa1U3QgkpopXfEBxnImNLoAyU6y+JsoLH4076Su2OzuEramSoU7oBX6FOI6U0DyguchkCQ2hBa/Pjoedm/XHkhOjXvhoV0bNGEraMKHDxqsfVzMYAWiF1JQ2/xCs2/gY+2HW6VxLJWih4T/KbD6yXQtWf6rLFuOJsuttLbE9DrR/ND0qyd/hnFt+mWdGWhER6+QOk3lhCQDFEo5q06WsyaSv0C8qkR5qBjVZnrX/7OBjRTfKUh1VdjEDinQgL0RTj8enISMExyjsftQkPRzDQO77wiJPhIksOBFOBgkItv4n0Fi6gNRci8pG7gN62iZfrOW3fOY9w5nup4Wmfa1Zzr3zEDTadHA/U+ZtKvRtaFFQWJ6v4TluuTj8hP7/ZMPqx9lFycfs95A1n0O8WZ/HmPaQ/zA5c8nmy5lk2l0wbFnV7LJ0s4MNBGbjN6C6/1vMjbZI2zq52OPrfka7n4dsDQ1DbtbFqZ+MPyEACCoBfk9IFBPiyUEIgt+43AykIw9FCS9Ox3TUhG5XyKsXlYPXbrJ+7VWlY1cG2EHBSbyF+KTWuAg10QervJo0QBE0p+Xn+DmCC/LlvNSOTHyA1TfLj4qqnov87UtBVVIYiZBFYqn/5Sp1On070hG8A8=
--------------------------------------------------------------------------------
/.github/主流程图.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaogan18/msgserver/3a1c12aafadd190efaffe81805753e55d8fa62f1/.github/主流程图.png
--------------------------------------------------------------------------------
/.github/主流程图.xml:
--------------------------------------------------------------------------------
1 | 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
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // 使用 IntelliSense 了解相关属性。
3 | // 悬停以查看现有属性的描述。
4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Launch",
9 | "type": "go",
10 | "request": "launch",
11 | "mode": "debug",
12 | "remotePath": "",
13 | "port": 2345,
14 | "host": "127.0.0.1",
15 | "program": "${fileDirname}",
16 | "env": {},
17 | "args": [],
18 | "showLog": true
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "svn.ignoreMissingSvnWarning": true
3 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # msgserver
2 | ## 说明
3 | 一种基于socket的消息推送服务端,服务端保持与客户端tcp长连接,实现消息的推送或提醒。
4 | 目前支持消息模式有两种:1.全员广播;2.特定用户推送
5 | 现在还只能支持单台服务器部署,后期将提供分布式部署方案
6 |
7 | ## 快速使用
8 | ### 安装
9 | go get github.com/xiaogan18/msgserver
10 | ### 创建hello.go
11 | ```go
12 | package main
13 | import(
14 | "github.com/xiaogan18/msgserver"
15 | "fmt"
16 | "bufio"
17 | "strings"
18 | "os"
19 | )
20 | func main(){
21 | sdr,lster,err:=msgserver.NewDefaultServer(false) //不开启ssl加密传输
22 | if err!=nil{
23 | fmt.Println(err)
24 | }
25 | //开启一个协程 监听主机3366端口
26 | go func(){
27 | lster.Listen("127.0.0.1:3366")
28 | }()
29 | sdr.BeginSender()
30 |
31 | for{
32 | // 从标准输入读取字符串,以\n为分割
33 | fmt.Println("input a msg:")
34 | text, err := bufio.NewReader(os.Stdin).ReadString('\n')
35 | if(err==nil){
36 | text= strings.Replace(text,"\r\n","",1)
37 | if(text=="count"){
38 | fmt.Println(lster.OnlineCount())
39 | }else{
40 | sdr.SendNotice(text)
41 | }
42 | }
43 | }
44 | }
45 | ```
46 | ### 运行
47 | go run hello.go
48 | 使用客户端tcp连接到127.0.0.1:3366就可以正常收到消息了
49 | ## 详细描述
50 | ### 主要流程
51 | 
52 | ### 带参数构建服务
53 | ```go
54 | //函数签名
55 | func NewServer(poolType,queueType,serializer,protocolType string,OnSSL bool) (*SenderScheduler,*Listener,error)
56 | ```
57 | poolType:连接池类型(默认'default')
58 | queueType:队列类型(默认'default')
59 | serializer:序列化器类型(默认'json')
60 | protocolType:协议类型(默认'default')
61 | OnSSL:是否开启SSL加密
62 | ```go
63 | //带参数构建服务实体
64 | sdr,lster,err:=msgserver.NewServer("default","default","json","default",true)
65 | ```
66 | ### SSL握手
67 | 如果你开启了SSL,建立连接时将触发三次握手密钥交换,且之后的所有消息将使用加密传输。
68 | 密钥交换使用RSA非对称加密算法,密钥长度1024,填充方式PKCS1,密文过长使用分段加密方式
69 | 消息加密使用AES对称加密算法,密钥长度16,模式ECB,填充方式PKCS7
70 | 握手流程如下:
71 | 
72 | ### 过滤器
73 | 如果你想过滤某些连接,应用黑/白名单,可以使用过滤器
74 | ```go
75 | // 定义struct满足msgserver.Filter接口
76 | type testFilter struct{
77 | }
78 | func (this testFilter) OnFilter(conn net.Conn) bool{
79 | fmt.Println(conn.RemoteAddr().String())
80 | return true
81 | }
82 | ```
83 | ```go
84 | // 将过滤器应用到Lisenter
85 | lster.Filter=new(testFilter)
86 | ```
87 | ### 身份验证
88 | 如果你需要验证客户端的身份,可以定义身份验证函数。
89 | 其中string参数是建立连接后客户端发送来的一段字符串,验证失败服务端会主动断开连接
90 | ```go
91 | lster.OnAuthentication=func(data string) (string,error){
92 | user:=strings.Split(data,";")
93 | if len(user)==2 && user[1]=="123456"{
94 | return user[0],nil
95 | }else{
96 | return "",errors.New("password is wrong")
97 | }
98 | }
99 | ```
100 | ### 离线消息持久化
101 | 如果你需要保存用户离线后的消息,可以使用持久化器。
102 | 用户下次上线后会立即取出消息发送(只会对调用Sender.SendMessage()的消息进行持久化)
103 | ```go
104 | // 保存在内存
105 | sdr.Container=persistence.CreateMsgContainer("default")
106 | // 保存在redis
107 | sdr.Container=persistence.CreateMsgContainer("redis",
108 | &persistence.RedisOptions{Network:"tcp",Address:"127.0.0.1:6379",})
109 | ```
110 | ### HTTP API接口调用
111 | 如果你想通过http来控制消息发送,可以使用api监听
112 | ```go
113 | go func(){
114 | apier:=msgserver.Api{
115 | Sender:&sdr.Sender,
116 | }
117 | // 开启http api
118 | apier.Listen("127.0.0.1:3365","/msg/sender") //监听主机端口,服务目录“/msg/sender” Method="POST"
119 | }()
120 | ```
121 | http post body(To为空表示发送全员通知):
122 | {
123 | "To":"xiaogan18",
124 | "Content":"hello world"
125 | }
126 | ### Sender调度器参数
127 | ```go
128 | // 消息发送重试次数
129 | sdr.ResendTimes=2
130 | // 重试间隔(毫秒)
131 | sdr.ResendInterval=10*1000
132 | // 消息发送处理最大并行数设置
133 | sdr.MaxParallel=10
134 | //最小并行数设置
135 | sdr.MinParallel=1
136 | //队列中消息堆积阈值设置
137 | sdr.QueueBufferLen=1000
138 | ```
139 | ### 开始服务
140 | 如果你完成了所有配置,就可以按下面的方式开始服务了
141 | ```go
142 | //listener开始监听
143 | go func(){
144 | lster.Listen("127.0.0.1:3366")
145 | }()
146 | // 开始sender
147 | sdr.BeginSender()
148 | ```
149 |
--------------------------------------------------------------------------------
/api.go:
--------------------------------------------------------------------------------
1 | package msgserver
2 | import(
3 | "net/http"
4 | "strings"
5 | "encoding/json"
6 | "fmt"
7 | "io"
8 | )
9 | type Api struct{
10 | Sender *Sender
11 | }
12 | func(this *Api) Listen(addr,path string){
13 | h:=&httpHandler{
14 | sender:this.Sender,
15 | }
16 | h.router=make(map [string]func(*Sender,http.ResponseWriter,*http.Request),0)
17 | h.router[path]=msgSender
18 | fmt.Printf("api listen address %s,path '%s'[post]\n",addr,path)
19 | err:=http.ListenAndServe(addr,h)
20 | if err!=nil{
21 | panic(err)
22 | }
23 | }
24 | // 消息发送 http api
25 | func msgSender(sender *Sender,res http.ResponseWriter,req *http.Request){
26 | if strings.ToUpper(req.Method)!="POST"{
27 | return
28 | }
29 | var success=true
30 | defer func(){
31 | if(success){
32 | res.Write([]byte("1"))
33 | }else{
34 | res.Write([]byte("0"))
35 | }
36 | }()
37 | reader:= req.Body
38 | buffer:=make([]byte,256)
39 | le,err:=reader.Read(buffer)
40 | if err!=nil && err!=io.EOF{
41 | success=false
42 | return
43 | }
44 | buffer=buffer[:le]
45 | type msgContent struct{
46 | To string
47 | Content string
48 | }
49 | msg:=new(msgContent)
50 | if json.Unmarshal(buffer,msg)!=nil{
51 | success=false
52 | return
53 | }
54 | if msg.To==""{
55 | sender.SendNotice(msg.Content)
56 | }else{
57 | sender.SendMessage(msg.Content,msg.To)
58 | }
59 | }
60 |
61 | // http handler
62 | type httpHandler struct{
63 | sender *Sender
64 | router map [string]func(*Sender,http.ResponseWriter,*http.Request)
65 | }
66 | func(this *httpHandler) ServeHTTP(res http.ResponseWriter, req *http.Request){
67 | path:=req.URL.Path
68 | for k:=range this.router{
69 | if k==path{
70 | this.router[k](this.sender, res,req)
71 | break
72 | }
73 | }
74 | }
--------------------------------------------------------------------------------
/encrypt.go:
--------------------------------------------------------------------------------
1 | package msgserver
2 | import(
3 | "crypto/aes"
4 | "crypto/rsa"
5 | "crypto/x509"
6 | "time"
7 | "crypto/sha256"
8 | "bytes"
9 | "crypto/rand"
10 | "fmt"
11 | )
12 | type aes_encrypt struct{
13 | }
14 | // 随机生成一个key bits:16|24|32
15 | func(this aes_encrypt) newKey(bits int) []byte{
16 | //用当前时间sha256生成一个AES秘钥
17 | now:=time.Now().Unix()
18 | hash:=sha256.Sum256([]byte(fmt.Sprintf("%d",now)))
19 | return hash[:bits]
20 | }
21 | // aes加密 模式:ECB 填充:PKCS7
22 | func(this aes_encrypt) aesEncrypt(plantText,key []byte) ([]byte,error){
23 | block, err := aes.NewCipher(key) //选择加密算法 ECB
24 | if err != nil {
25 | return nil, err
26 | }
27 | plantText = this.pKCS7Padding(plantText, block.BlockSize())
28 |
29 | ciphertext := make([]byte, len(plantText))
30 | block.Encrypt(ciphertext,plantText)
31 | ciphertext=[]byte(tobase64String(ciphertext)) //密文以base64格式传输
32 | return ciphertext, nil
33 | }
34 | // aes解密 模式:ECB 填充:PKCS7
35 | func(this aes_encrypt) aesDecrypt(ciphertext,key []byte) ([]byte, error) {
36 | var err error
37 | ciphertext,err=decodeBase64(ciphertext) //密文以base64格式传输
38 | if err != nil {
39 | return nil, err
40 | }
41 | keyBytes := []byte(key)
42 | block, err := aes.NewCipher(keyBytes) //选择加密算法 ECB
43 | if err != nil {
44 | return nil, err
45 | }
46 | plantText := make([]byte, len(ciphertext))
47 | block.Decrypt(plantText, ciphertext)
48 | plantText = this.pKCS7UnPadding(plantText, block.BlockSize())
49 | return plantText, nil
50 | }
51 | // pkcs7填充
52 | func(this aes_encrypt) pKCS7Padding(ciphertext []byte, blockSize int) []byte {
53 | padding := blockSize - len(ciphertext)%blockSize
54 | padtext := bytes.Repeat([]byte{byte(padding)}, padding)
55 | return append(ciphertext, padtext...)
56 | }
57 | // 去除pkcs7填充
58 | func(this aes_encrypt) pKCS7UnPadding(plantText []byte, blockSize int) []byte {
59 | length := len(plantText)
60 | unpadding := int(plantText[length-1])
61 | return plantText[:(length - unpadding)]
62 | }
63 |
64 | type rsa_encrypt struct{
65 |
66 | }
67 | // 创建一对公私密钥 bits:1024|2048
68 | func(this rsa_encrypt) newKey(bits int) (privateKey,publicKey []byte,e error){
69 | p,err:=rsa.GenerateKey(rand.Reader,bits)
70 | if err!=nil{
71 | e=err
72 | return
73 | }
74 | privateKey= x509.MarshalPKCS1PrivateKey(p)
75 | publicKey,e= x509.MarshalPKIXPublicKey(&p.PublicKey)
76 | return
77 | }
78 | // rsa加密 PKCS1填充
79 | func(this rsa_encrypt) rsaEncrypt(plantText,publicKey []byte) ([]byte,error){
80 | cKey,err:= x509.ParsePKIXPublicKey(publicKey)
81 | if err!=nil{
82 | return nil,err
83 | }
84 | return rsa.EncryptPKCS1v15(rand.Reader,cKey.(*rsa.PublicKey),plantText)
85 | }
86 | // rsa解密 PKCS1填充
87 | func(this rsa_encrypt) rsaDecrypt(plantText,privateKey []byte) ([]byte,error){
88 | pKey,err:=x509.ParsePKCS1PrivateKey(privateKey)
89 | if(err!=nil){
90 | return nil,err
91 | }
92 | result:=make([]byte,0)
93 | length:=pKey.PublicKey.N.BitLen()/8 //单次密文长度
94 | fmt.Println(length)
95 | times:=len(plantText)/length
96 | for i:=0;i0{
44 | return msgColl,nil
45 | }else{
46 | return nil,NotFoundMsg
47 | }
48 | }
49 | func (this *MemoryContainer)Put(msg *OfflineMsg){
50 | this._msg_lock.Lock()
51 | this._user_lock.Lock()
52 | defer func(){
53 | this._user_lock.Unlock()
54 | this._msg_lock.Unlock()
55 | }()
56 | user:=msg.To
57 | if u,ok:=this.userMsgMap[user];ok{
58 | this.userMsgMap[user]=append(u,msg.MsgId)
59 | }else{
60 | this.userMsgMap[user]=[]string{msg.MsgId}
61 | }
62 | this.msgMap[msg.MsgId]=msg
63 | }
64 | // 清理过期消息
65 | // param interval 间隔(毫秒)
66 | func (this *MemoryContainer) gc(interval int){
67 | go func(interval int){
68 | for{
69 | invalid:=make([]string,0)
70 | this._msg_lock.Lock()
71 | for key:=range this.msgMap{
72 | if this.msgMap[key].KeepLiveTime.Before(time.Now()){
73 | invalid= append(invalid,key)
74 | }
75 | }
76 | if len(invalid) > 0{
77 | for _,k:=range invalid{
78 | delete(this.msgMap,k)
79 | }
80 | }
81 | this._msg_lock.Unlock()
82 | time.Sleep(time.Duration(interval) * time.Millisecond)
83 | }
84 | }(interval)
85 |
86 | }
--------------------------------------------------------------------------------
/persistence/msgRedis.go:
--------------------------------------------------------------------------------
1 | package persistence
2 | import(
3 | "github.com/gomodule/redigo/redis"
4 | "github.com/xiaogan18/msgserver/serialize"
5 | "log"
6 | "fmt"
7 | )
8 | const(
9 | userMsgPrefix="u_m_"
10 | )
11 | type RedisOptions struct{
12 | Network string
13 | Address string
14 | Password string
15 | }
16 | type RedisContainer struct{
17 | option *RedisOptions
18 | serializer serialize.Serialize
19 | }
20 | func(this *RedisContainer) try(){
21 | c,err:=this.dial()
22 | if err!=nil{
23 | panic(fmt.Sprintf("redis conn error:%s",err))
24 | }
25 | defer func(){
26 | c.Close()
27 | }()
28 | }
29 | func(this *RedisContainer) dial() (redis.Conn,error){
30 | var op=make([]redis.DialOption,0)
31 | if this.option.Password!=""{
32 | op=append(op,redis.DialPassword(this.option.Password))
33 | }
34 | c,err:=redis.Dial(this.option.Network,this.option.Address,op...)
35 | return c,err
36 | }
37 | func(this *RedisContainer) Get(id string) (*OfflineMsg,error){
38 | c,err:=this.dial()
39 | if err!=nil{
40 | return nil,err
41 | }
42 | defer func(){
43 | c.Close()
44 | }()
45 | // 从Redis中取出
46 | jsonStr,err:=redis.String(c.Do("GET",id))
47 | if err!=nil{
48 | return nil,err
49 | }
50 | // 反序列化
51 | msg:=new(OfflineMsg)
52 | if err=this.serializer.ToInterface([]byte(jsonStr),msg);err!=nil{
53 | return nil,err
54 | }
55 | //删除redis中的消息
56 | c.Do("DEL",id)
57 | return msg,nil
58 | }
59 | func(this *RedisContainer) GetUserMsg(userID string) ([]*OfflineMsg,error){
60 | c,err:=this.dial()
61 | if err!=nil{
62 | return nil,err
63 | }
64 | defer func(){
65 | c.Close()
66 | }()
67 | // 取出user下msg id
68 | msgs,err:= redis.Values(c.Do("SMEMBERS",userMsgPrefix + userID))
69 | if err!=nil{
70 | return nil,err
71 | }
72 | result:=make([]*OfflineMsg,0)
73 | // 循环取msg
74 | for _,k:=range msgs{
75 | id,_:=redis.String(k,nil)
76 | jsonStr,err:=redis.String(c.Do("GET",id))
77 | if err==nil{
78 | msg:=new(OfflineMsg)
79 | if err=this.serializer.ToInterface([]byte(jsonStr),msg);err==nil{
80 | result=append(result,msg)
81 | }
82 | // 删除消息
83 | c.Do("DEL",id)
84 | }
85 | }
86 | // 删除用户离线消息列表
87 | c.Do("DEL",userMsgPrefix + userID)
88 | return result,nil
89 | }
90 | func(this *RedisContainer) Put(msg *OfflineMsg){
91 | c,err:=this.dial()
92 | if err!=nil{
93 | log.Printf("offline msg input failed:%s",err)
94 | }
95 | defer func(){
96 | c.Close()
97 | }()
98 | json,_:=this.serializer.ToBytes(msg)
99 | c.Do("SET",msg.MsgId,string(json))
100 | c.Do("SADD",userMsgPrefix+msg.To,msg.MsgId)
101 | }
--------------------------------------------------------------------------------
/persistence/offlineMsg.go:
--------------------------------------------------------------------------------
1 | package persistence
2 | import(
3 | "time"
4 | "github.com/xiaogan18/msgserver/queue"
5 | "github.com/xiaogan18/msgserver/serialize"
6 | "errors"
7 | )
8 | var(
9 | NotFoundMsg=errors.New("msg is not found")
10 | )
11 | type MsgContainer interface{
12 | Get(id string) (*OfflineMsg,error)
13 | GetUserMsg(userID string) ([]*OfflineMsg,error)
14 | Put(*OfflineMsg)
15 | }
16 | type OfflineMsg struct{
17 | queue.Message
18 | KeepLiveTime time.Time
19 | }
20 |
21 | func CreateMsgContainer(t string,params ...interface{}) MsgContainer{
22 | switch(t){
23 | case "redis":
24 | p:=params[0].(*RedisOptions)
25 | c:=RedisContainer{
26 | serializer:serialize.CreateSerializer("json"),
27 | option:p,
28 | }
29 | c.try()
30 | return &c
31 | default:
32 | c:=MemoryContainer{
33 | msgMap:make(map[string]*OfflineMsg,0),
34 | userMsgMap:make(map[string][]string,0),
35 | }
36 | c.gc(1000*120)
37 | return &c
38 | }
39 | }
--------------------------------------------------------------------------------
/pool/connClear.go:
--------------------------------------------------------------------------------
1 | package pool
2 | import(
3 | "net"
4 | //"fmt"
5 | "time"
6 | )
7 | // 开始自动清理无效连接
8 | func (this *PoolMemory)beginConnGC(connList map[string]net.Conn,interval int64){
9 | go func(){
10 | for{
11 | list:=connList
12 | downConns:=make([]string,0)
13 | for key:=range list{
14 | conn:=list[key]
15 | if _,err:=conn.Write([]byte{0});err!=nil{
16 | downConns= append(downConns,key)
17 | }
18 | }
19 | //移除无效连接conn
20 | //fmt.Println("begin conn gc")
21 | this._lock.Lock()
22 | for _,k:=range downConns{
23 | // fmt.Println("remove a conn "+k)
24 | delete(list,k)
25 | }
26 | this._lock.Unlock()
27 | time.Sleep(time.Duration(interval) * time.Millisecond) //休眠
28 | }
29 | }()
30 | }
--------------------------------------------------------------------------------
/pool/pool.go:
--------------------------------------------------------------------------------
1 | package pool
2 | import(
3 | "net"
4 | )
5 |
6 | type Pool interface{
7 | Put(id string, conn net.Conn) error
8 | Get(id string) (net.Conn,error)
9 | Foreach(callback func(string))
10 | Clear()
11 | Count() int
12 | }
13 | func CreatePool(t string) (p Pool){
14 | switch(t){
15 | default:
16 | temp:=new(PoolMemory)
17 | temp.Init(60)
18 | p=temp
19 | break
20 | }
21 | return
22 | }
--------------------------------------------------------------------------------
/pool/poolMemory.go:
--------------------------------------------------------------------------------
1 | package pool
2 | import(
3 | "net"
4 | "errors"
5 | "sync"
6 | )
7 | var(
8 | Err_NotFoundConn=errors.New("conn is not found")
9 | )
10 |
11 | type PoolMemory struct{
12 | _lock sync.Mutex //互斥锁
13 | pool map[string]net.Conn
14 | }
15 | // 初始化连接池
16 | func(this *PoolMemory) Init(gcInterval int64){
17 | this.pool=make(map[string]net.Conn)
18 | this.beginConnGC(this.pool,gcInterval*1000)
19 | }
20 |
21 | func(this *PoolMemory) Put(id string, conn net.Conn) error{
22 | defer func(){
23 | this._lock.Unlock()
24 | }()
25 | this._lock.Lock()
26 | this.pool[id]=conn
27 | return nil
28 | }
29 | func(this *PoolMemory) Get(id string) (net.Conn,error){
30 | defer func(){
31 | this._lock.Unlock()
32 | }()
33 | this._lock.Lock()
34 | v,ok:=this.pool[id]
35 | if(!ok){
36 | return nil,Err_NotFoundConn
37 | }
38 | return v,nil
39 | }
40 | func(this *PoolMemory) Foreach(callback func(string)){
41 | if(this.pool!=nil && len(this.pool)>0){
42 | for v:= range this.pool{
43 | callback(v)
44 | }
45 | }
46 | }
47 | func(this *PoolMemory) Clear(){
48 | defer func(){
49 | this._lock.Unlock()
50 | this.pool=nil
51 | }()
52 | this._lock.Lock()
53 | for k:=range this.pool{
54 | this.pool[k].Close()
55 | }
56 | }
57 | func(this *PoolMemory) Count() int{
58 | if(this.pool==nil){
59 | return 0
60 | }
61 | defer func(){
62 | this._lock.Unlock()
63 | }()
64 | this._lock.Lock()
65 | return len(this.pool)
66 | }
--------------------------------------------------------------------------------
/protocol/protocol.go:
--------------------------------------------------------------------------------
1 | package protocol
2 | import(
3 | "bytes"
4 | )
5 |
6 | // 传输协议
7 | type Protocol interface{
8 | // 封包
9 | Packet(msg []byte) []byte
10 | // 解包
11 | Unpack(buffer []byte,readChan chan []byte,formatting func([]byte) []byte)
12 | }
13 | func CreatePro(t string)(p Protocol){
14 | switch(t){
15 | default:
16 | p=new(CustomPro)
17 | break
18 | }
19 | return
20 | }
21 |
22 | type CustomPro struct{
23 |
24 | }
25 | const(
26 | proSplitChar='|'
27 | )
28 | func(this *CustomPro) Packet(msg []byte) []byte{
29 | return append(msg,byte(proSplitChar))
30 | }
31 | func(this *CustomPro) Unpack(buffer []byte,readChan chan []byte,formatting func([]byte) []byte){
32 | go func(){
33 | startIndex:=0
34 | chars:=bytes.Runes(buffer)
35 | for i,c:=range chars{
36 | if c==proSplitChar{
37 | readChan<-buffer[startIndex:i]
38 | startIndex=i+1
39 | }
40 | }
41 | if startIndex >= len(buffer){
42 | readChan<-[]byte{}
43 | }else{
44 | if formatting!=nil{
45 | readChan<-formatting(buffer[startIndex:])
46 | }else{
47 | readChan<-buffer[startIndex:]
48 | }
49 | }
50 | }()
51 | }
--------------------------------------------------------------------------------
/proxy.go:
--------------------------------------------------------------------------------
1 | package msgserver
2 | import(
3 | "net"
4 | "github.com/xiaogan18/msgserver/protocol"
5 | "github.com/xiaogan18/msgserver/serialize"
6 | )
7 | type TcpProxy struct{
8 | Proto protocol.Protocol
9 | Seri serialize.Serialize
10 | IsOnSSL bool
11 | }
12 | // 发送消息
13 | func(this *TcpProxy) Write(conn net.Conn,data interface{}) (int,error){
14 | var err error
15 | var b []byte
16 | if b,err=this.Seri.ToBytes(data);err==nil{
17 | if this.IsOnSSL{ //是否开启SSL加密传输
18 | if b,err=Encrypt(b);err==nil{
19 | b=this.Proto.Packet(b)
20 | }
21 | }else{
22 | b=this.Proto.Packet(b)
23 | }
24 | return conn.Write(b)
25 | }
26 | return 0,err
27 | }
28 | // 接收消息
29 | func(this *TcpProxy) Read(conn net.Conn) (chan []byte,error){
30 | var err error
31 | var b []byte=make([]byte,128)
32 | var i int
33 | if i,err=conn.Read(b);err==nil{
34 | b=b[:i]
35 | rdChan:=make(chan []byte)
36 | if this.IsOnSSL{ //是否开启SSL加密传输
37 | format:=func(data []byte) []byte{
38 | if r,err:=Decrypt(data);err!=nil{
39 | return nil
40 | }else{
41 | return r
42 | }
43 | }
44 | this.Proto.Unpack(b,rdChan,format)
45 | }else{
46 | this.Proto.Unpack(b,rdChan,nil)
47 | }
48 | return rdChan,nil
49 | }
50 | return nil,err
51 | }
52 | // 反序列化
53 | func(this *TcpProxy) DeSerialize(data []byte,v interface{}) error{
54 | return this.Seri.ToInterface(data,v)
55 | }
56 | // 开始SSL握手
57 | func(this *TcpProxy) SSL(conn net.Conn) error{
58 | if(this.IsOnSSL){
59 | return handshake(conn)
60 | }
61 | return nil
62 | }
--------------------------------------------------------------------------------
/queue/message.go:
--------------------------------------------------------------------------------
1 | package queue
2 | import(
3 | "time"
4 | )
5 |
6 | type Message struct{
7 | MsgId string
8 | MsgType MessageType
9 | Content interface{}
10 | To string
11 | TrySendTimes int
12 | SendTime time.Time
13 | }
14 | //消息类型
15 | type MessageType int
16 | const(
17 | // 针对消息 发送给单个终端
18 | Message_ToOne MessageType=iota
19 | // 广播消息
20 | Message_ToAll
21 | )
22 |
--------------------------------------------------------------------------------
/queue/queue.go:
--------------------------------------------------------------------------------
1 | package queue
2 |
3 | type Queue interface{
4 | // 入队
5 | Enqueue(msg *Message) error
6 | // 出队
7 | Dequeue() (*Message,error)
8 | // 消息总数
9 | Count() int
10 | }
11 |
12 | func CreateQueue(t string) (q Queue){
13 | switch(t){
14 | default:
15 | q=new(QueueMemory)
16 | break
17 | }
18 | return
19 | }
--------------------------------------------------------------------------------
/queue/queueMemory.go:
--------------------------------------------------------------------------------
1 | package queue
2 | import(
3 | "errors"
4 | "container/list"
5 | "sync"
6 | )
7 | var(
8 | _lock=new (sync.Mutex) //互斥锁
9 | Error_QueueIsEmpty=errors.New("queue is already empty") //队列为空
10 | )
11 |
12 | type QueueMemory struct{
13 | queue *list.List
14 | //类型 0:先入先出
15 | queueType int
16 |
17 | }
18 | //入队
19 | func(this *QueueMemory) Enqueue(msg *Message) error{
20 | if this.queue==nil{
21 | this.queue=list.New()
22 | }
23 | this.queue.PushBack(msg)
24 | return nil
25 | }
26 | //出队
27 | func(this *QueueMemory) Dequeue() (*Message,error){
28 | if this.queue==nil || this.queue.Len()==0{
29 | return nil,Error_QueueIsEmpty
30 | }
31 | var msg *Message
32 | //判断队列类型
33 | switch(this.queueType){
34 | case 0: //先入先出
35 | ele:=this.queue.Front()
36 | _lock.Lock() //修改时使用 互斥锁
37 | msg= this.queue.Remove(ele).(*Message)
38 | _lock.Unlock()
39 | break
40 | }
41 | return msg,nil
42 | }
43 | func(this *QueueMemory) Count() int{
44 | if this.queue==nil{
45 | return 0
46 | }
47 | return this.queue.Len()
48 | }
--------------------------------------------------------------------------------
/sender.go:
--------------------------------------------------------------------------------
1 | package msgserver
2 | import(
3 | "github.com/xiaogan18/msgserver/queue"
4 | "github.com/xiaogan18/msgserver/pool"
5 | "github.com/xiaogan18/msgserver/persistence"
6 | "time"
7 | "fmt"
8 | )
9 |
10 | type Sender struct{
11 | pool pool.Pool
12 | queue queue.Queue
13 | // tcp代理类
14 | tcpProxy *TcpProxy
15 | // 消息持久化容器
16 | Container persistence.MsgContainer
17 | // 消息发送失败时回调
18 | FailedCallback func(error)
19 | // 失败重试次数
20 | ResendTimes int
21 | // 重试间隔(毫秒)
22 | ResendInterval int
23 | }
24 | // 发送消息
25 | func(this *Sender) SendMessage(msg interface{},user string) error{
26 | m:=queue.Message{
27 | MsgId:fmt.Sprintf("1%s",Guid()),
28 | MsgType:queue.Message_ToOne,
29 | Content:msg,
30 | To:user,
31 | }
32 | return this.queue.Enqueue(&m)
33 | }
34 | // 发送广播
35 | func(this *Sender) SendNotice(msg interface{}){
36 | this.pool.Foreach(func(uid string){
37 | m:=queue.Message{
38 | MsgId:fmt.Sprintf("2%s",Guid()),
39 | MsgType:queue.Message_ToAll,
40 | Content:msg,
41 | To:uid,
42 | }
43 | this.queue.Enqueue(&m)
44 | })
45 | }
46 | // 客户端新上线处理
47 | func(this *Sender) UpOnline(userID string){
48 | fmt.Printf("%s up line\n",userID)
49 | if this.Container==nil{
50 | return
51 | }
52 | msgs,err:=this.Container.GetUserMsg(userID)
53 | if err!=nil{
54 | return
55 | }
56 | for _,v:=range msgs{
57 | this.queue.Enqueue(&v.Message)
58 | }
59 | }
60 |
61 | // conn write
62 | func(this *Sender) send(msg *queue.Message) error{
63 | msg.TrySendTimes++
64 | msg.SendTime=time.Now()
65 |
66 | c,err:=this.pool.Get(msg.To)
67 | if(err==nil){
68 | _,err=this.tcpProxy.Write(c,msg.Content)
69 | }
70 | return err
71 | }
72 | // 消息发送失败处理
73 | func (this *Sender)msgSendFailed(msg *queue.Message){
74 | //推送消息不处理
75 | if(msg.MsgType==queue.Message_ToAll){
76 | return
77 | }
78 | //在重试次数范围内,消息重新入队
79 | if this.ResendTimes >= msg.TrySendTimes{
80 | this.queue.Enqueue(msg)
81 | return
82 | }
83 | if this.Container==nil{
84 | return
85 | }
86 | m:=&persistence.OfflineMsg{
87 | Message:*msg,
88 | }
89 | this.Container.Put(m)
90 |
91 | }
--------------------------------------------------------------------------------
/senderScheduler.go:
--------------------------------------------------------------------------------
1 | package msgserver
2 | import(
3 | "github.com/xiaogan18/msgserver/queue"
4 | "github.com/xiaogan18/msgserver/pool"
5 | "sync"
6 | "time"
7 | "errors"
8 | "fmt"
9 | )
10 | var(
11 | Err_TaskIsNotDefine=errors.New("task is not define")
12 | )
13 |
14 | // 初始化一个sender调度器
15 | func NewSender(p pool.Pool,q queue.Queue,proxy *TcpProxy) *SenderScheduler{
16 | this:=&SenderScheduler{}
17 | this.pool=p
18 | this.queue=q
19 | this.tcpProxy=proxy
20 | //定义任务
21 | this.task= func(m *queue.Message){
22 | //判断是否重发消息
23 | if m.TrySendTimes>0{
24 | sTime:=m.SendTime.Add(time.Duration(this.ResendInterval)*time.Millisecond)
25 | if(time.Now().Before(sTime)){
26 | this.queue.Enqueue(m) //未到重发时间,重新入队
27 | return
28 | }
29 | }
30 | if err:=this.send(m);err!=nil{
31 | if(this.FailedCallback!=nil){ //发送失败回调
32 | this.FailedCallback(err)
33 | }
34 | //失败处理
35 | this.msgSendFailed(m)
36 | }
37 | }
38 | return this
39 | }
40 | // 消息发送调度器
41 | type SenderScheduler struct{
42 | Sender
43 | // 最大并行数
44 | MaxParallel int
45 | // 最小并行数
46 | MinParallel int
47 | // 队列堆积消息阈值,达到就增加并行
48 | QueueBufferLen int
49 | // 当前并行数
50 | parallelNum int
51 | task func(*queue.Message)
52 | sync.Mutex
53 | }
54 |
55 | func(this *SenderScheduler) BeginSender() error{
56 | if this.task==nil{
57 | return Err_TaskIsNotDefine
58 | }
59 | this.parallelNum=0
60 | if this.MaxParallel<=0{
61 | this.MaxParallel=10
62 | }
63 | if this.MinParallel<=0{
64 | this.MinParallel=1
65 | }
66 | if this.QueueBufferLen<=0{
67 | this.QueueBufferLen=1000
68 | }
69 | // 开启最小并行数量sender
70 | for i:=0;i= this.QueueBufferLen{
77 | this.newGoroutine()
78 | }
79 | time.Sleep(time.Second*10)
80 | }
81 | }()
82 | return nil
83 | }
84 | func(this *SenderScheduler) newGoroutine(){
85 | defer func(){
86 | this.Unlock()
87 | }()
88 | this.Lock()
89 | // 判断是否达到最大并行
90 | if this.parallelNum>=this.MaxParallel{
91 | return
92 | }else{
93 | this.parallelNum++
94 | }
95 | go func(this *SenderScheduler){
96 | defer func(){
97 | if err:=recover();err!=nil{
98 | fmt.Println(err)
99 | }
100 | }()
101 | for{
102 | m,err:=this.queue.Dequeue() //取队列消息
103 | if err==queue.Error_QueueIsEmpty{
104 | this.Lock()
105 | b:=this.parallelNum<=this.MinParallel
106 | this.Unlock()
107 | //判断协程结束或阻塞
108 | if b{
109 | //已达到最小并行数量,休眠
110 | time.Sleep(time.Second*1)
111 | }else{
112 | break
113 | }
114 | }else if err==nil{
115 | //fmt.Println(*m)
116 | this.task(m)
117 | }
118 | }
119 | }(this)
120 | fmt.Println("a new sender groutine created")
121 | }
--------------------------------------------------------------------------------
/serialize/json.go:
--------------------------------------------------------------------------------
1 | package serialize
2 | import(
3 | "encoding/json"
4 | )
5 |
6 | type JsonSerialize struct{
7 |
8 | }
9 |
10 | func(this *JsonSerialize) ToBytes(content interface{}) ([]byte,error){
11 | //s:= content.(string)
12 | data,err:=json.Marshal(content)
13 | return data,err
14 | }
15 | func(this *JsonSerialize) ToInterface(bytes []byte,obj interface{}) error{
16 |
17 | return json.Unmarshal(bytes,obj)
18 | }
--------------------------------------------------------------------------------
/serialize/serialize.go:
--------------------------------------------------------------------------------
1 | package serialize
2 |
3 | type Serialize interface{
4 | ToBytes(content interface{}) ([]byte,error)
5 | ToInterface(bytes []byte,obj interface{}) error
6 | }
7 |
8 | func CreateSerializer(t string) (s Serialize){
9 | switch(t){
10 | default:
11 | s=new(JsonSerialize)
12 | break
13 | }
14 | return
15 | }
--------------------------------------------------------------------------------
/ssl.go:
--------------------------------------------------------------------------------
1 | package msgserver
2 | import(
3 | "net"
4 | "encoding/base64"
5 | "fmt"
6 | )
7 | var(
8 | rsa_privateKey []byte
9 | rsa_publicKey []byte
10 | secretKey []byte
11 | )
12 | func init(){
13 | // 创建rsa密钥对
14 | r:=rsa_encrypt{}
15 | if pr,pu,err:= r.newKey(1024);err==nil{
16 | rsa_privateKey=pr
17 | rsa_publicKey=pu
18 | }else{
19 | panic("failed to create rsa secret key!")
20 | }
21 | // 创建aes密钥
22 | secretKey=aes_encrypt{}.newKey(16)
23 | }
24 | // 开始三次握手 交换数据皆以base64格式
25 | func handshake(conn net.Conn) error{
26 | // 发送公钥
27 | base64PubKey:=tobase64String(rsa_publicKey)
28 | if _,err:=conn.Write([]byte(base64PubKey));err!=nil{
29 | return err
30 | }
31 | // 接收客户端密文
32 | text:=make([]byte,512)
33 | i,err:=conn.Read(text)
34 | if(err!=nil){
35 | return err
36 | }
37 | text=text[:i]
38 | text,err=decodeBase64(text)
39 | if(err!=nil){
40 | return err
41 | }
42 | // 解密得到客户端publicKey
43 | clientKeyBase64,err:= rsa_encrypt{}.rsaDecrypt(text,rsa_privateKey)
44 | if err!=nil{
45 | return err
46 | }
47 | // publick key base64解码
48 | clientKey,err:=decodeBase64(clientKeyBase64)
49 | if err!=nil{
50 | return err
51 | }
52 |
53 | // 使用客户端publicKey加密 对称密钥secretKey
54 | lastText,err:=rsa_encrypt{}.rsaEncrypt(secretKey,clientKey)
55 | if err!=nil{
56 | return err
57 | }
58 | // 将secretKey加密后的密文base64发送到客户端
59 | if _,err=conn.Write([]byte(tobase64String(lastText)));err!=nil{
60 | return err
61 | }
62 | return nil
63 | }
64 | // 加密
65 | func Encrypt(data []byte) ([]byte,error){
66 | return aes_encrypt{}.aesEncrypt(data,secretKey)
67 | }
68 | // 解密
69 | func Decrypt(data []byte) (result []byte,e error){
70 | defer func(){
71 | if err:=recover();err!=nil{
72 | e=fmt.Errorf("decrypt error:%s",err)
73 | fmt.Println(e)
74 | }
75 | }()
76 | result,e= aes_encrypt{}.aesDecrypt(data,secretKey)
77 | return
78 | }
79 |
80 | func tobase64String(data []byte) string{
81 | return base64.StdEncoding.EncodeToString(data)
82 | }
83 | func decodeBase64(data []byte) ([]byte,error){
84 | buffer:=make([]byte,512)
85 | i,err:=base64.StdEncoding.Decode(buffer,data)
86 | if err!=nil{
87 | return nil,err
88 | }
89 | return buffer[:i],nil
90 | }
--------------------------------------------------------------------------------
/tests/debug:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaogan18/msgserver/3a1c12aafadd190efaffe81805753e55d8fa62f1/tests/debug
--------------------------------------------------------------------------------
/tests/main.go:
--------------------------------------------------------------------------------
1 | package main
2 | import(
3 | "github.com/xiaogan18/msgserver"
4 | "bufio"
5 | "os"
6 | "fmt"
7 | "strings"
8 | "net"
9 | "errors"
10 | "github.com/xiaogan18/msgserver/persistence"
11 | )
12 | type testFilter struct{
13 | }
14 | func (this testFilter) OnFilter(conn net.Conn) bool{
15 | fmt.Println(conn.RemoteAddr().String())
16 | return true
17 | }
18 | func main(){
19 | sdr,lster,err:=msgserver.NewDefaultServer(true)
20 | if err!=nil{
21 | fmt.Println(err)
22 | }
23 |
24 | //设置连接过滤器
25 | lster.Filter=new(testFilter)
26 | //设置身份验证方法
27 | lster.OnAuthentication=func(data string) (string,error){
28 | user:=strings.Split(data,";")
29 | if len(user)==2 && user[1]=="123456"{
30 | return user[0],nil
31 | }else{
32 | return "",errors.New("password is wrong")
33 | }
34 | }
35 | //开启一个协程 监听主机3366端口
36 | go func(){
37 | lster.Listen("127.0.0.1:3366")
38 | }()
39 | // 开启协程 监听主机3365端口
40 | go func(){
41 | apier:=msgserver.Api{
42 | Sender:&sdr.Sender,
43 | }
44 | // 开启http api
45 | apier.Listen("127.0.0.1:3365","/msg/sender")
46 | }()
47 | // 消息发送重试次数
48 | sdr.ResendTimes=2
49 | // 重试间隔(毫秒)
50 | sdr.ResendInterval=10*1000
51 | // 设置离线消息容器
52 | sdr.Container=persistence.CreateMsgContainer("default")
53 | //sdr.Container=persistence.CreateMsgContainer("redis",
54 | // &persistence.RedisOptions{Network:"tcp",Address:"127.0.0.1:6379",})
55 | // 消息发送处理最大并行数设置
56 | sdr.MaxParallel=10
57 | //最小并行数设置
58 | sdr.MinParallel=1
59 | //队列中消息堆积阈值设置
60 | sdr.QueueBufferLen=1000
61 | // 开始sender
62 | sdr.BeginSender()
63 | for{
64 | // 从标准输入读取字符串,以\n为分割
65 | fmt.Print("input a msg\n")
66 | text, err := bufio.NewReader(os.Stdin).ReadString('\n')
67 | if(err==nil){
68 | text= strings.Replace(text,"\r\n","",1)
69 | if(text=="count"){
70 | fmt.Println(lster.OnlineCount())
71 | }else{
72 | sdr.SendNotice(text)
73 | }
74 | }
75 | }
76 | }
--------------------------------------------------------------------------------