├── .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 | ![image](https://github.com/xiaogan18/msgserver/blob/master/.github/主流程图.png) 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 | ![image](https://github.com/xiaogan18/msgserver/blob/master/.github/SSL流程.png) 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 | } --------------------------------------------------------------------------------