first commit

master
daiao 3 years ago
parent 528a98ddfa
commit 4c40755d3b

@ -0,0 +1,11 @@
木兰宽松许可证
Copyright (c) [2019] [link1st]
[gowebsocket] is licensed under the Mulan PSL v1.
You can use this software according to the terms and conditions of the Mulan PSL v1.
You may obtain a copy of Mulan PSL v1 at:
http://license.coscl.org.cn/MulanPSL
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
PURPOSE.
See the Mulan PSL v1 for more details.

File diff suppressed because it is too large Load Diff

@ -0,0 +1,55 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-07-25
* Time: 12:11
*/
package common
const (
OK = 200 // Success
NotLoggedIn = 1000 // 未登录
ParameterIllegal = 1001 // 参数不合法
UnauthorizedUserId = 1002 // 非法的用户Id
Unauthorized = 1003 // 未授权
ServerError = 1004 // 系统错误
NotData = 1005 // 没有数据
ModelAddError = 1006 // 添加错误
ModelDeleteError = 1007 // 删除错误
ModelStoreError = 1008 // 存储错误
OperationFailure = 1009 // 操作失败
RoutingNotExist = 1010 // 路由不存在
)
// 根据错误码 获取错误信息
func GetErrorMessage(code uint32, message string) string {
var codeMessage string
codeMap := map[uint32]string{
OK: "Success",
NotLoggedIn: "未登录",
ParameterIllegal: "参数不合法",
UnauthorizedUserId: "非法的用户Id",
Unauthorized: "未授权",
NotData: "没有数据",
ServerError: "系统错误",
ModelAddError: "添加错误",
ModelDeleteError: "删除错误",
ModelStoreError: "存储错误",
OperationFailure: "操作失败",
RoutingNotExist: "路由不存在",
}
if message == "" {
if value, ok := codeMap[code]; ok {
// 存在
codeMessage = value
} else {
codeMessage = "未定义错误类型!"
}
} else {
codeMessage = message
}
return codeMessage
}

@ -0,0 +1,33 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-07-25
* Time: 12:11
*/
package common
type JsonResult struct {
Code uint32 `json:"code"`
Msg string `json:"msg"`
Data interface{} `json:"data"`
}
func Response(code uint32, message string, data interface{}) JsonResult {
message = GetErrorMessage(code, message)
jsonMap := grantMap(code, message, data)
return jsonMap
}
// 按照接口格式生成原数据数组
func grantMap(code uint32, message string, data interface{}) JsonResult {
jsonMap := JsonResult{
Code: code,
Msg: message,
Data: data,
}
return jsonMap
}

@ -0,0 +1,15 @@
app:
logFile: log/gin.log
httpPort: 8080
webSocketPort: 8089
rpcPort: 9001
httpUrl: 127.0.0.1:8080
webSocketUrl: 127.0.0.1:8089
redis:
addr: "localhost:6379"
password: ""
DB: 0
poolSize: 30
minIdleConns: 30

@ -0,0 +1,36 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-07-25
* Time: 12:11
*/
package controllers
import (
"github.com/gin-gonic/gin"
"gowebsocket/common"
"net/http"
)
type BaseController struct {
gin.Context
}
// 获取全部请求解析到map
func Response(c *gin.Context, code uint32, msg string, data map[string]interface{}) {
message := common.Response(code, msg, data)
// 允许跨域
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Origin", "*") // 这是允许访问所有域
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE") // 服务器支持的所有跨域请求的方法,为了避免浏览次请求的多次'预检'请求
c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session,X_Requested_With,Accept, Origin, Host, Connection, Accept-Encoding, Accept-Language,DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Pragma")
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar") // 跨域关键设置 让浏览器可以解析
c.Header("Access-Control-Allow-Credentials", "true") // 跨域请求是否需要带cookie信息 默认设置为true
c.Set("content-type", "application/json") // 设置返回格式是json
c.JSON(http.StatusOK, message)
return
}

@ -0,0 +1,38 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-07-25
* Time: 12:11
*/
package home
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/spf13/viper"
"gowebsocket/servers/websocket"
"net/http"
"strconv"
)
// 聊天页面
func Index(c *gin.Context) {
appIdStr := c.Query("appId")
appIdUint64, _ := strconv.ParseInt(appIdStr, 10, 32)
appId := uint32(appIdUint64)
if !websocket.InAppIds(appId) {
appId = websocket.GetDefaultAppId()
}
fmt.Println("http_request 聊天首页", appId)
data := gin.H{
"title": "聊天首页",
"appId": appId,
"httpUrl": viper.GetString("app.httpUrl"),
"webSocketUrl": viper.GetString("app.webSocketUrl"),
}
c.HTML(http.StatusOK, "index.tpl", data)
}

@ -0,0 +1,38 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-07-25
* Time: 12:11
*/
package systems
import (
"fmt"
"github.com/gin-gonic/gin"
"gowebsocket/common"
"gowebsocket/controllers"
"gowebsocket/servers/websocket"
"runtime"
)
// 查询系统状态
func Status(c *gin.Context) {
isDebug := c.Query("isDebug")
fmt.Println("http_request 查询系统状态", isDebug)
data := make(map[string]interface{})
numGoroutine := runtime.NumGoroutine()
numCPU := runtime.NumCPU()
// goroutine 的数量
data["numGoroutine"] = numGoroutine
data["numCPU"] = numCPU
// ClientManager 信息
data["managerInfo"] = websocket.GetManagerInfo(isDebug)
controllers.Response(c, common.OK, "", data)
}

@ -0,0 +1,122 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-07-25
* Time: 12:11
*/
package user
import (
"fmt"
"github.com/gin-gonic/gin"
"gowebsocket/common"
"gowebsocket/controllers"
"gowebsocket/lib/cache"
"gowebsocket/models"
"gowebsocket/servers/websocket"
"strconv"
)
// 查看全部在线用户
func List(c *gin.Context) {
appIdStr := c.Query("appId")
appIdUint64, _ := strconv.ParseInt(appIdStr, 10, 32)
appId := uint32(appIdUint64)
fmt.Println("http_request 查看全部在线用户", appId)
data := make(map[string]interface{})
userList := websocket.UserList(appId)
data["userList"] = userList
data["userCount"] = len(userList)
controllers.Response(c, common.OK, "", data)
}
// 查看用户是否在线
func Online(c *gin.Context) {
userId := c.Query("userId")
appIdStr := c.Query("appId")
appIdUint64, _ := strconv.ParseInt(appIdStr, 10, 32)
appId := uint32(appIdUint64)
fmt.Println("http_request 查看用户是否在线", userId, appIdStr)
data := make(map[string]interface{})
online := websocket.CheckUserOnline(appId, userId)
data["userId"] = userId
data["online"] = online
controllers.Response(c, common.OK, "", data)
}
// 给用户发送消息
func SendMessage(c *gin.Context) {
// 获取参数
appIdStr := c.PostForm("appId")
userId := c.PostForm("userId")
msgId := c.PostForm("msgId")
message := c.PostForm("message")
appIdUint64, _ := strconv.ParseInt(appIdStr, 10, 32)
appId := uint32(appIdUint64)
fmt.Println("http_request 给用户发送消息", appIdStr, userId, msgId, message)
// TODO::进行用户权限认证一般是客户端传入TOKEN然后检验TOKEN是否合法通过TOKEN解析出来用户ID
// 本项目只是演示所以直接过去客户端传入的用户ID(userId)
data := make(map[string]interface{})
if cache.SeqDuplicates(msgId) {
fmt.Println("给用户发送消息 重复提交:", msgId)
controllers.Response(c, common.OK, "", data)
return
}
sendResults, err := websocket.SendUserMessage(appId, userId, msgId, message)
if err != nil {
data["sendResultsErr"] = err.Error()
}
data["sendResults"] = sendResults
controllers.Response(c, common.OK, "", data)
}
// 给全员发送消息
func SendMessageAll(c *gin.Context) {
// 获取参数
appIdStr := c.PostForm("appId")
userId := c.PostForm("userId")
msgId := c.PostForm("msgId")
message := c.PostForm("message")
appIdUint64, _ := strconv.ParseInt(appIdStr, 10, 32)
appId := uint32(appIdUint64)
fmt.Println("http_request 给全体用户发送消息", appIdStr, userId, msgId, message)
data := make(map[string]interface{})
if cache.SeqDuplicates(msgId) {
fmt.Println("给用户发送消息 重复提交:", msgId)
controllers.Response(c, common.OK, "", data)
return
}
sendResults, err := websocket.SendUserMessageAll(appId, userId, msgId, models.MessageCmdMsg, message)
if err != nil {
data["sendResultsErr"] = err.Error()
}
data["sendResults"] = sendResults
controllers.Response(c, common.OK, "", data)
}

@ -0,0 +1,28 @@
module gowebsocket
go 1.14
require (
github.com/fsnotify/fsnotify v1.4.8-0.20190312181446-1485a34d5d57 // indirect
github.com/gin-gonic/gin v1.7.2
github.com/go-playground/validator/v10 v10.8.0 // indirect
github.com/go-redis/redis v0.0.0-20190719092155-6bc7daa5b1e8
github.com/golang/protobuf v1.5.2
github.com/gorilla/websocket v1.4.2
github.com/hashicorp/hcl v1.0.1-0.20190611123218-cf7d376da96d // indirect
github.com/json-iterator/go v1.1.11 // indirect
github.com/mattn/go-isatty v0.0.13 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/pelletier/go-toml v1.4.1-0.20190725070617-84da2c4a25c5 // indirect
github.com/spf13/afero v1.2.2 // indirect
github.com/spf13/cast v1.3.1-0.20190531093228-c01685bb8421 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.4-0.20181223182923-24fa6976df40 // indirect
github.com/spf13/viper v1.4.1-0.20190728125013-1b33e8258e07
github.com/subosito/gotenv v1.1.1 // indirect
github.com/ugorji/go v1.2.6 // indirect
google.golang.org/grpc v1.21.0
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

239
go.sum

@ -0,0 +1,239 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.8-0.20190312181446-1485a34d5d57 h1:r+AdyYQnMjCqabCiXfAES7u0tbaqXlLXuZ5FT+5OEQs=
github.com/fsnotify/fsnotify v1.4.8-0.20190312181446-1485a34d5d57/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.7.2 h1:Tg03T9yM2xa8j6I3Z3oqLaQRSmKvxPd6g/2HJ6zICFA=
github.com/gin-gonic/gin v1.7.2/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-playground/validator/v10 v10.8.0 h1:1kAa0fCrnpv+QYdkdcRzrRM7AyYs5o8+jZdJCz9xj6k=
github.com/go-playground/validator/v10 v10.8.0/go.mod h1:9JhgTzTaE31GZDpH/HSvHiRJrJ3iKAgqqH0Bl/Ocjdk=
github.com/go-redis/redis v0.0.0-20190719092155-6bc7daa5b1e8 h1:+fE3DwORl28yutMsVT36NnW52u8el5F3Ye9bgXZtokc=
github.com/go-redis/redis v0.0.0-20190719092155-6bc7daa5b1e8/go.mod h1:nuQKdm6S7SnV28NJEN2ZNbKpddAM1O76Z2LMJcIxJVM=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/hcl v1.0.1-0.20190611123218-cf7d376da96d h1:r4iSf+UX1tNxFJZ64FsUoOfysT7TePSbRNz4/mYGUIE=
github.com/hashicorp/hcl v1.0.1-0.20190611123218-cf7d376da96d/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.4.1-0.20190725070617-84da2c4a25c5 h1:rW9pqjOLUVvikJFWrF53GlhmZNTFtsjjNA0LD2sYLvg=
github.com/pelletier/go-toml v1.4.1-0.20190725070617-84da2c4a25c5/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.3.1-0.20190531093228-c01685bb8421 h1:s+WESDalIlUupImv6znWrHX6XIRXuVTBsX633p7Ymms=
github.com/spf13/cast v1.3.1-0.20190531093228-c01685bb8421/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.4-0.20181223182923-24fa6976df40 h1:2gwxRRQ5I+FcDbxGtkIC9kWD7EFBewHjQqD8rDQAVQA=
github.com/spf13/pflag v1.0.4-0.20181223182923-24fa6976df40/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.4.1-0.20190728125013-1b33e8258e07 h1:Bxzp40S+I62o0BB0Jd23i3OYDtIP2Gs8NtP+Sv6rYHg=
github.com/spf13/viper v1.4.1-0.20190728125013-1b33e8258e07/go.mod h1:LLu5zwCkRPEBY0VPcRMqh58VtcO8Lp1DgqwstU7rYlk=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.1.1 h1:TWxckSF6WVKWbo2M3tMqCtWa9NFUgqM1SSynxmYONOI=
github.com/subosito/gotenv v1.1.1/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E=
github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

@ -0,0 +1,21 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-08-01
* Time: 18:13
*/
package helper
import (
"fmt"
"time"
)
func GetOrderIdTime() (orderId string) {
currentTime := time.Now().Nanosecond()
orderId = fmt.Sprintf("%d", currentTime)
return
}

@ -0,0 +1,91 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-07-25
* Time: 17:27
*/
package helper
import (
"net"
)
// 获取服务器Ip
// func GetServerIp() (ip string) {
// addrs, err := net.InterfaceAddrs()
// if err != nil {
// return ""
// }
// for _, address := range addrs {
// // 检查ip地址判断是否回环地址
// if ipNet, ok := address.(*net.IPNet); ok && !ipNet.IP.IsLoopback() {
// if ipNet.IP.To4() != nil {
// ip = ipNet.IP.String()
// }
// }
// }
// return
// }
/**** iprpc google
*** 1https://www.jianshu.com/p/301aabc06972
*** 2https://www.cnblogs.com/chaselogs/p/11301940.html
****/
func GetServerIp() string {
ip, err := externalIP()
if err != nil {
return ""
}
return ip.String()
}
func externalIP() (net.IP, error) {
ifaces, err := net.Interfaces()
if err != nil {
return nil, err
}
for _, iface := range ifaces {
if iface.Flags&net.FlagUp == 0 {
continue // interface down
}
if iface.Flags&net.FlagLoopback != 0 {
continue // loopback interface
}
addrs, err := iface.Addrs()
if err != nil {
return nil, err
}
for _, addr := range addrs {
ip := getIpFromAddr(addr)
if ip == nil {
continue
}
return ip, nil
}
}
return nil, err
}
func getIpFromAddr(addr net.Addr) net.IP {
var ip net.IP
switch v := addr.(type) {
case *net.IPNet:
ip = v.IP
case *net.IPAddr:
ip = v.IP
}
if ip == nil || ip.IsLoopback() {
return nil
}
ip = ip.To4()
if ip == nil {
return nil // not an ipv4 address
}
return ip
}

@ -0,0 +1,118 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-08-03
* Time: 15:23
*/
package cache
import (
"encoding/json"
"fmt"
"gowebsocket/lib/redislib"
"gowebsocket/models"
"strconv"
)
const (
serversHashKey = "acc:hash:servers" // 全部的服务器
serversHashCacheTime = 2 * 60 * 60 // key过期时间
serversHashTimeout = 3 * 60 // 超时时间
)
func getServersHashKey() (key string) {
key = fmt.Sprintf("%s", serversHashKey)
return
}
// 设置服务器信息
func SetServerInfo(server *models.Server, currentTime uint64) (err error) {
key := getServersHashKey()
value := fmt.Sprintf("%d", currentTime)
redisClient := redislib.GetClient()
number, err := redisClient.Do("hSet", key, server.String(), value).Int()
if err != nil {
fmt.Println("SetServerInfo", key, number, err)
return
}
if number != 1 {
return
}
redisClient.Do("Expire", key, serversHashCacheTime)
return
}
// 下线服务器信息
func DelServerInfo(server *models.Server) (err error) {
key := getServersHashKey()
redisClient := redislib.GetClient()
number, err := redisClient.Do("hDel", key, server.String()).Int()
if err != nil {
fmt.Println("DelServerInfo", key, number, err)
return
}
if number != 1 {
return
}
redisClient.Do("Expire", key, serversHashCacheTime)
return
}
func GetServerAll(currentTime uint64) (servers []*models.Server, err error) {
servers = make([]*models.Server, 0)
key := getServersHashKey()
redisClient := redislib.GetClient()
val, err := redisClient.Do("hGetAll", key).Result()
valByte, _ := json.Marshal(val)
fmt.Println("GetServerAll", key, string(valByte))
serverMap, err := redisClient.HGetAll(key).Result()
if err != nil {
fmt.Println("SetServerInfo", key, err)
return
}
for key, value := range serverMap {
valueUint64, err := strconv.ParseUint(value, 10, 64)
if err != nil {
fmt.Println("GetServerAll", key, err)
return nil, err
}
// 超时
if valueUint64+serversHashTimeout <= currentTime {
continue
}
server, err := models.StringToServer(key)
if err != nil {
fmt.Println("GetServerAll", key, err)
return nil, err
}
servers = append(servers, server)
}
return
}

@ -0,0 +1,62 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-07-26
* Time: 09:18
*/
package cache
import (
"fmt"
"gowebsocket/lib/redislib"
)
const (
submitAgainPrefix = "acc:submit:again:" // 数据不重复提交
)
/********************* 查询数据是否处理过 ************************/
// 获取数据提交去除key
func getSubmitAgainKey(from string, value string) (key string) {
key = fmt.Sprintf("%s%s:%s", submitAgainPrefix, from, value)
return
}
// 重复提交
// return true:重复提交 false:第一次提交
func submitAgain(from string, second int, value string) (isSubmitAgain bool) {
// 默认重复提交
isSubmitAgain = true
key := getSubmitAgainKey(from, value)
redisClient := redislib.GetClient()
number, err := redisClient.Do("setNx", key, "1").Int()
if err != nil {
fmt.Println("submitAgain", key, number, err)
return
}
if number != 1 {
return
}
// 第一次提交
isSubmitAgain = false
redisClient.Do("Expire", key, second)
return
}
// Seq 重复提交
func SeqDuplicates(seq string) (result bool) {
result = submitAgain("seq", 12*60*60, seq)
return
}

@ -0,0 +1,82 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-07-25
* Time: 17:28
*/
package cache
import (
"encoding/json"
"fmt"
"github.com/go-redis/redis"
"gowebsocket/lib/redislib"
"gowebsocket/models"
)
const (
userOnlinePrefix = "acc:user:online:" // 用户在线状态
userOnlineCacheTime = 24 * 60 * 60
)
/********************* 查询用户是否在线 ************************/
func getUserOnlineKey(userKey string) (key string) {
key = fmt.Sprintf("%s%s", userOnlinePrefix, userKey)
return
}
func GetUserOnlineInfo(userKey string) (userOnline *models.UserOnline, err error) {
redisClient := redislib.GetClient()
key := getUserOnlineKey(userKey)
data, err := redisClient.Get(key).Bytes()
if err != nil {
if err == redis.Nil {
fmt.Println("GetUserOnlineInfo", userKey, err)
return
}
fmt.Println("GetUserOnlineInfo", userKey, err)
return
}
userOnline = &models.UserOnline{}
err = json.Unmarshal(data, userOnline)
if err != nil {
fmt.Println("获取用户在线数据 json Unmarshal", userKey, err)
return
}
fmt.Println("获取用户在线数据", userKey, "time", userOnline.LoginTime, userOnline.HeartbeatTime, "AccIp", userOnline.AccIp, userOnline.IsLogoff)
return
}
// 设置用户在线数据
func SetUserOnlineInfo(userKey string, userOnline *models.UserOnline) (err error) {
redisClient := redislib.GetClient()
key := getUserOnlineKey(userKey)
valueByte, err := json.Marshal(userOnline)
if err != nil {
fmt.Println("设置用户在线数据 json Marshal", key, err)
return
}
_, err = redisClient.Do("setEx", key, userOnlineCacheTime, string(valueByte)).Result()
if err != nil {
fmt.Println("设置用户在线数据 ", key, err)
return
}
return
}

@ -0,0 +1,38 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-07-25
* Time: 14:18
*/
package redislib
import (
"fmt"
"github.com/go-redis/redis"
"github.com/spf13/viper"
)
var (
client *redis.Client
)
func ExampleNewClient() {
client = redis.NewClient(&redis.Options{
Addr: viper.GetString("redis.addr"),
Password: viper.GetString("redis.password"),
DB: viper.GetInt("redis.DB"),
PoolSize: viper.GetInt("redis.poolSize"),
MinIdleConns: viper.GetInt("redis.minIdleConns"),
})
pong, err := client.Ping().Result()
fmt.Println("初始化redis:", pong, err)
// Output: PONG <nil>
}
func GetClient() (c *redis.Client) {
return client
}

1
log/.gitignore vendored

@ -0,0 +1 @@
!.gitignore

@ -0,0 +1,21 @@
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] Loaded HTML Templates (3):
-
- index.html
- index.tpl
[GIN-debug] GET /user/list --> gowebsocket/controllers/user.List (3 handlers)
[GIN-debug] GET /user/online --> gowebsocket/controllers/user.Online (3 handlers)
[GIN-debug] POST /user/sendMessage --> gowebsocket/controllers/user.SendMessage (3 handlers)
[GIN-debug] POST /user/sendMessageAll --> gowebsocket/controllers/user.SendMessageAll (3 handlers)
[GIN-debug] GET /system/state --> gowebsocket/controllers/systems.Status (3 handlers)
[GIN-debug] GET /home/index --> gowebsocket/controllers/home.Index (3 handlers)
[GIN] 2021/11/20 - 00:19:19 | 200 | 2.067409ms | 127.0.0.1 | GET "/home/index"
[GIN] 2021/11/20 - 00:19:20 | 200 | 748.062µs | 127.0.0.1 | GET "/user/list?appId=101"
[GIN] 2021/11/20 - 00:19:30 | 200 | 1.828355ms | 127.0.0.1 | GET "/home/index?appId=104"
[GIN] 2021/11/20 - 00:19:31 | 200 | 490.323µs | 127.0.0.1 | GET "/user/list?appId=104"

@ -0,0 +1,95 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-07-25
* Time: 09:59
*/
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/spf13/viper"
"gowebsocket/lib/redislib"
"gowebsocket/routers"
"gowebsocket/servers/grpcserver"
"gowebsocket/servers/task"
"gowebsocket/servers/websocket"
"io"
"net/http"
"os"
"os/exec"
"time"
)
func main() {
initConfig()
initFile()
initRedis()
router := gin.Default()
// 初始化路由
routers.Init(router)
routers.WebsocketInit()
// 定时任务
task.Init()
// 服务注册
task.ServerInit()
go websocket.StartWebSocket()
// grpc
go grpcserver.Init()
go open()
httpPort := viper.GetString("app.httpPort")
http.ListenAndServe(":"+httpPort, router)
}
// 初始化日志
func initFile() {
// Disable Console Color, you don't need console color when writing the logs to file.
gin.DisableConsoleColor()
// Logging to a file.
logFile := viper.GetString("app.logFile")
f, _ := os.Create(logFile)
gin.DefaultWriter = io.MultiWriter(f)
}
func initConfig() {
viper.SetConfigName("config/app")
viper.AddConfigPath(".") // 添加搜索路径
err := viper.ReadInConfig()
if err != nil {
panic(fmt.Errorf("Fatal error config file: %s \n", err))
}
fmt.Println("config app:", viper.Get("app"))
fmt.Println("config redis:", viper.Get("redis"))
}
func initRedis() {
redislib.ExampleNewClient()
}
func open() {
time.Sleep(1000 * time.Millisecond)
httpUrl := viper.GetString("app.httpUrl")
httpUrl = "http://" + httpUrl + "/home/index"
fmt.Println("访问页面体验:", httpUrl)
cmd := exec.Command("open", httpUrl)
cmd.Output()
}

@ -0,0 +1,68 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-08-01
* Time: 10:40
*/
package models
import "gowebsocket/common"
const (
MessageTypeText = "text"
MessageCmdMsg = "msg"
MessageCmdEnter = "enter"
MessageCmdExit = "exit"
)
// 消息的定义
type Message struct {
Target string `json:"target"` // 目标
Type string `json:"type"` // 消息类型 text/img/
Msg string `json:"msg"` // 消息内容
From string `json:"from"` // 发送者
}
func NewTestMsg(from string, Msg string) (message *Message) {
message = &Message{
Type: MessageTypeText,
From: from,
Msg: Msg,
}
return
}
func getTextMsgData(cmd, uuId, msgId, message string) string {
textMsg := NewTestMsg(uuId, message)
head := NewResponseHead(msgId, cmd, common.OK, "Ok", textMsg)
return head.String()
}
// 文本消息
func GetMsgData(uuId, msgId, cmd, message string) string {
return getTextMsgData(cmd, uuId, msgId, message)
}
// 文本消息
func GetTextMsgData(uuId, msgId, message string) string {
return getTextMsgData("msg", uuId, msgId, message)
}
// 用户进入消息
func GetTextMsgDataEnter(uuId, msgId, message string) string {
return getTextMsgData("enter", uuId, msgId, message)
}
// 用户退出消息
func GetTextMsgDataExit(uuId, msgId, message string) string {
return getTextMsgData("exit", uuId, msgId, message)
}

@ -0,0 +1,28 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-07-27
* Time: 14:41
*/
package models
/************************ 请求数据 **************************/
// 通用请求数据格式
type Request struct {
Seq string `json:"seq"` // 消息的唯一Id
Cmd string `json:"cmd"` // 请求命令字
Data interface{} `json:"data,omitempty"` // 数据 json
}
// 登录请求数据
type Login struct {
ServiceToken string `json:"serviceToken"` // 验证用户是否登录
AppId uint32 `json:"appId,omitempty"`
UserId string `json:"userId,omitempty"`
}
// 心跳请求数据
type HeartBeat struct {
UserId string `json:"userId,omitempty"`
}

@ -0,0 +1,49 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-08-01
* Time: 10:46
*/
package models
import "encoding/json"
/************************ 响应数据 **************************/
type Head struct {
Seq string `json:"seq"` // 消息的Id
Cmd string `json:"cmd"` // 消息的cmd 动作
Response *Response `json:"response"` // 消息体
}
type Response struct {
Code uint32 `json:"code"`
CodeMsg string `json:"codeMsg"`
Data interface{} `json:"data"` // 数据 json
}
// push 数据结构体
type PushMsg struct {
Seq string `json:"seq"`
Uuid uint64 `json:"uuid"`
Type string `json:"type"`
Msg string `json:"msg"`
}
// 设置返回消息
func NewResponseHead(seq string, cmd string, code uint32, codeMsg string, data interface{}) *Head {
response := NewResponse(code, codeMsg, data)
return &Head{Seq: seq, Cmd: cmd, Response: response}
}
func (h *Head) String() (headStr string) {
headBytes, _ := json.Marshal(h)
headStr = string(headBytes)
return
}
func NewResponse(code uint32, codeMsg string, data interface{}) *Response {
return &Response{Code: code, CodeMsg: codeMsg, Data: data}
}

@ -0,0 +1,49 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-08-03
* Time: 15:38
*/
package models
import (
"errors"
"fmt"
"strings"
)
type Server struct {
Ip string `json:"ip"` // ip
Port string `json:"port"` // 端口
}
func NewServer(ip string, port string) *Server {
return &Server{Ip: ip, Port: port}
}
func (s *Server) String() (str string) {
if s == nil {
return
}
str = fmt.Sprintf("%s:%s", s.Ip, s.Port)
return
}
func StringToServer(str string) (server *Server, err error) {
list := strings.Split(str, ":")
if len(list) != 2 {
return nil, errors.New("err")
}
server = &Server{
Ip: list[0],
Port: list[1],
}
return
}

@ -0,0 +1,109 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-07-25
* Time: 17:36
*/
package models
import (
"fmt"
"time"
)
const (
heartbeatTimeout = 3 * 60 // 用户心跳超时时间
)
// 用户在线状态
type UserOnline struct {
AccIp string `json:"accIp"` // acc Ip
AccPort string `json:"accPort"` // acc 端口
AppId uint32 `json:"appId"` // appId
UserId string `json:"userId"` // 用户Id
ClientIp string `json:"clientIp"` // 客户端Ip
ClientPort string `json:"clientPort"` // 客户端端口
LoginTime uint64 `json:"loginTime"` // 用户上次登录时间
HeartbeatTime uint64 `json:"heartbeatTime"` // 用户上次心跳时间
LogOutTime uint64 `json:"logOutTime"` // 用户退出登录的时间
Qua string `json:"qua"` // qua
DeviceInfo string `json:"deviceInfo"` // 设备信息
IsLogoff bool `json:"isLogoff"` // 是否下线
}
/********************** 数据处理 *********************************/
// 用户登录
func UserLogin(accIp, accPort string, appId uint32, userId string, addr string, loginTime uint64) (userOnline *UserOnline) {
userOnline = &UserOnline{
AccIp: accIp,
AccPort: accPort,
AppId: appId,
UserId: userId,
ClientIp: addr,
LoginTime: loginTime,
HeartbeatTime: loginTime,
IsLogoff: false,
}
return
}
// 用户心跳
func (u *UserOnline) Heartbeat(currentTime uint64) {
u.HeartbeatTime = currentTime
u.IsLogoff = false
return
}
// 用户退出登录
func (u *UserOnline) LogOut() {
currentTime := uint64(time.Now().Unix())
u.LogOutTime = currentTime
u.IsLogoff = true
return
}
/********************** 数据操作 *********************************/
// 用户是否在线
func (u *UserOnline) IsOnline() (online bool) {
if u.IsLogoff {
return
}
currentTime := uint64(time.Now().Unix())
if u.HeartbeatTime < (currentTime - heartbeatTimeout) {
fmt.Println("用户是否在线 心跳超时", u.AppId, u.UserId, u.HeartbeatTime)
return
}
if u.IsLogoff {
fmt.Println("用户是否在线 用户已经下线", u.AppId, u.UserId)
return
}
return true
}
// 用户是否在本台机器上
func (u *UserOnline) UserIsLocal(localIp, localPort string) (result bool) {
if u.AccIp == localIp && u.AccPort == localPort {
result = true
return
}
return
}

@ -0,0 +1,3 @@
#!/usr/bin/env bash
protoc --go_out=plugins=grpc:. im_protobuf.proto

@ -0,0 +1,740 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: im_protobuf.proto
package protobuf
import (
context "context"
fmt "fmt"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// 查询用户是否在线
type QueryUsersOnlineReq struct {
AppId uint32 `protobuf:"varint,1,opt,name=appId,proto3" json:"appId,omitempty"`
UserId string `protobuf:"bytes,2,opt,name=userId,proto3" json:"userId,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *QueryUsersOnlineReq) Reset() { *m = QueryUsersOnlineReq{} }
func (m *QueryUsersOnlineReq) String() string { return proto.CompactTextString(m) }
func (*QueryUsersOnlineReq) ProtoMessage() {}
func (*QueryUsersOnlineReq) Descriptor() ([]byte, []int) {
return fileDescriptor_62691ff0994625a6, []int{0}
}
func (m *QueryUsersOnlineReq) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_QueryUsersOnlineReq.Unmarshal(m, b)
}
func (m *QueryUsersOnlineReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_QueryUsersOnlineReq.Marshal(b, m, deterministic)
}
func (m *QueryUsersOnlineReq) XXX_Merge(src proto.Message) {
xxx_messageInfo_QueryUsersOnlineReq.Merge(m, src)
}
func (m *QueryUsersOnlineReq) XXX_Size() int {
return xxx_messageInfo_QueryUsersOnlineReq.Size(m)
}
func (m *QueryUsersOnlineReq) XXX_DiscardUnknown() {
xxx_messageInfo_QueryUsersOnlineReq.DiscardUnknown(m)
}
var xxx_messageInfo_QueryUsersOnlineReq proto.InternalMessageInfo
func (m *QueryUsersOnlineReq) GetAppId() uint32 {
if m != nil {
return m.AppId
}
return 0
}
func (m *QueryUsersOnlineReq) GetUserId() string {
if m != nil {
return m.UserId
}
return ""
}
type QueryUsersOnlineRsp struct {
RetCode uint32 `protobuf:"varint,1,opt,name=retCode,proto3" json:"retCode,omitempty"`
ErrMsg string `protobuf:"bytes,2,opt,name=errMsg,proto3" json:"errMsg,omitempty"`
Online bool `protobuf:"varint,3,opt,name=online,proto3" json:"online,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *QueryUsersOnlineRsp) Reset() { *m = QueryUsersOnlineRsp{} }
func (m *QueryUsersOnlineRsp) String() string { return proto.CompactTextString(m) }
func (*QueryUsersOnlineRsp) ProtoMessage() {}
func (*QueryUsersOnlineRsp) Descriptor() ([]byte, []int) {
return fileDescriptor_62691ff0994625a6, []int{1}
}
func (m *QueryUsersOnlineRsp) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_QueryUsersOnlineRsp.Unmarshal(m, b)
}
func (m *QueryUsersOnlineRsp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_QueryUsersOnlineRsp.Marshal(b, m, deterministic)
}
func (m *QueryUsersOnlineRsp) XXX_Merge(src proto.Message) {
xxx_messageInfo_QueryUsersOnlineRsp.Merge(m, src)
}
func (m *QueryUsersOnlineRsp) XXX_Size() int {
return xxx_messageInfo_QueryUsersOnlineRsp.Size(m)
}
func (m *QueryUsersOnlineRsp) XXX_DiscardUnknown() {
xxx_messageInfo_QueryUsersOnlineRsp.DiscardUnknown(m)
}
var xxx_messageInfo_QueryUsersOnlineRsp proto.InternalMessageInfo
func (m *QueryUsersOnlineRsp) GetRetCode() uint32 {
if m != nil {
return m.RetCode
}
return 0
}
func (m *QueryUsersOnlineRsp) GetErrMsg() string {
if m != nil {
return m.ErrMsg
}
return ""
}
func (m *QueryUsersOnlineRsp) GetOnline() bool {
if m != nil {
return m.Online
}
return false
}
// 发送消息
type SendMsgReq struct {
Seq string `protobuf:"bytes,1,opt,name=seq,proto3" json:"seq,omitempty"`
AppId uint32 `protobuf:"varint,2,opt,name=appId,proto3" json:"appId,omitempty"`
UserId string `protobuf:"bytes,3,opt,name=userId,proto3" json:"userId,omitempty"`
Cms string `protobuf:"bytes,4,opt,name=cms,proto3" json:"cms,omitempty"`
Type string `protobuf:"bytes,5,opt,name=type,proto3" json:"type,omitempty"`
Msg string `protobuf:"bytes,6,opt,name=msg,proto3" json:"msg,omitempty"`
IsLocal bool `protobuf:"varint,7,opt,name=isLocal,proto3" json:"isLocal,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SendMsgReq) Reset() { *m = SendMsgReq{} }
func (m *SendMsgReq) String() string { return proto.CompactTextString(m) }
func (*SendMsgReq) ProtoMessage() {}
func (*SendMsgReq) Descriptor() ([]byte, []int) {
return fileDescriptor_62691ff0994625a6, []int{2}
}
func (m *SendMsgReq) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SendMsgReq.Unmarshal(m, b)
}
func (m *SendMsgReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SendMsgReq.Marshal(b, m, deterministic)
}
func (m *SendMsgReq) XXX_Merge(src proto.Message) {
xxx_messageInfo_SendMsgReq.Merge(m, src)
}
func (m *SendMsgReq) XXX_Size() int {
return xxx_messageInfo_SendMsgReq.Size(m)
}
func (m *SendMsgReq) XXX_DiscardUnknown() {
xxx_messageInfo_SendMsgReq.DiscardUnknown(m)
}
var xxx_messageInfo_SendMsgReq proto.InternalMessageInfo
func (m *SendMsgReq) GetSeq() string {
if m != nil {
return m.Seq
}
return ""
}
func (m *SendMsgReq) GetAppId() uint32 {
if m != nil {
return m.AppId
}
return 0
}
func (m *SendMsgReq) GetUserId() string {
if m != nil {
return m.UserId
}
return ""
}
func (m *SendMsgReq) GetCms() string {
if m != nil {
return m.Cms
}
return ""
}
func (m *SendMsgReq) GetType() string {
if m != nil {
return m.Type
}
return ""
}
func (m *SendMsgReq) GetMsg() string {
if m != nil {
return m.Msg
}
return ""
}
func (m *SendMsgReq) GetIsLocal() bool {
if m != nil {
return m.IsLocal
}
return false
}
type SendMsgRsp struct {
RetCode uint32 `protobuf:"varint,1,opt,name=retCode,proto3" json:"retCode,omitempty"`
ErrMsg string `protobuf:"bytes,2,opt,name=errMsg,proto3" json:"errMsg,omitempty"`
SendMsgId string `protobuf:"bytes,3,opt,name=sendMsgId,proto3" json:"sendMsgId,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SendMsgRsp) Reset() { *m = SendMsgRsp{} }
func (m *SendMsgRsp) String() string { return proto.CompactTextString(m) }
func (*SendMsgRsp) ProtoMessage() {}
func (*SendMsgRsp) Descriptor() ([]byte, []int) {
return fileDescriptor_62691ff0994625a6, []int{3}
}
func (m *SendMsgRsp) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SendMsgRsp.Unmarshal(m, b)
}
func (m *SendMsgRsp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SendMsgRsp.Marshal(b, m, deterministic)
}
func (m *SendMsgRsp) XXX_Merge(src proto.Message) {
xxx_messageInfo_SendMsgRsp.Merge(m, src)
}
func (m *SendMsgRsp) XXX_Size() int {
return xxx_messageInfo_SendMsgRsp.Size(m)
}
func (m *SendMsgRsp) XXX_DiscardUnknown() {
xxx_messageInfo_SendMsgRsp.DiscardUnknown(m)
}
var xxx_messageInfo_SendMsgRsp proto.InternalMessageInfo
func (m *SendMsgRsp) GetRetCode() uint32 {
if m != nil {
return m.RetCode
}
return 0
}
func (m *SendMsgRsp) GetErrMsg() string {
if m != nil {
return m.ErrMsg
}
return ""
}
func (m *SendMsgRsp) GetSendMsgId() string {
if m != nil {
return m.SendMsgId
}
return ""
}
// 发送消息
type SendMsgAllReq struct {
Seq string `protobuf:"bytes,1,opt,name=seq,proto3" json:"seq,omitempty"`
AppId uint32 `protobuf:"varint,2,opt,name=appId,proto3" json:"appId,omitempty"`
UserId string `protobuf:"bytes,3,opt,name=userId,proto3" json:"userId,omitempty"`
Cms string `protobuf:"bytes,4,opt,name=cms,proto3" json:"cms,omitempty"`
Type string `protobuf:"bytes,5,opt,name=type,proto3" json:"type,omitempty"`
Msg string `protobuf:"bytes,6,opt,name=msg,proto3" json:"msg,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SendMsgAllReq) Reset() { *m = SendMsgAllReq{} }
func (m *SendMsgAllReq) String() string { return proto.CompactTextString(m) }
func (*SendMsgAllReq) ProtoMessage() {}
func (*SendMsgAllReq) Descriptor() ([]byte, []int) {
return fileDescriptor_62691ff0994625a6, []int{4}
}
func (m *SendMsgAllReq) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SendMsgAllReq.Unmarshal(m, b)
}
func (m *SendMsgAllReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SendMsgAllReq.Marshal(b, m, deterministic)
}
func (m *SendMsgAllReq) XXX_Merge(src proto.Message) {
xxx_messageInfo_SendMsgAllReq.Merge(m, src)
}
func (m *SendMsgAllReq) XXX_Size() int {
return xxx_messageInfo_SendMsgAllReq.Size(m)
}
func (m *SendMsgAllReq) XXX_DiscardUnknown() {
xxx_messageInfo_SendMsgAllReq.DiscardUnknown(m)
}
var xxx_messageInfo_SendMsgAllReq proto.InternalMessageInfo
func (m *SendMsgAllReq) GetSeq() string {
if m != nil {
return m.Seq
}
return ""
}
func (m *SendMsgAllReq) GetAppId() uint32 {
if m != nil {
return m.AppId
}
return 0
}
func (m *SendMsgAllReq) GetUserId() string {
if m != nil {
return m.UserId
}
return ""
}
func (m *SendMsgAllReq) GetCms() string {
if m != nil {
return m.Cms
}
return ""
}
func (m *SendMsgAllReq) GetType() string {
if m != nil {
return m.Type
}
return ""
}
func (m *SendMsgAllReq) GetMsg() string {
if m != nil {
return m.Msg
}
return ""
}
type SendMsgAllRsp struct {
RetCode uint32 `protobuf:"varint,1,opt,name=retCode,proto3" json:"retCode,omitempty"`
ErrMsg string `protobuf:"bytes,2,opt,name=errMsg,proto3" json:"errMsg,omitempty"`
SendMsgId string `protobuf:"bytes,3,opt,name=sendMsgId,proto3" json:"sendMsgId,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SendMsgAllRsp) Reset() { *m = SendMsgAllRsp{} }
func (m *SendMsgAllRsp) String() string { return proto.CompactTextString(m) }
func (*SendMsgAllRsp) ProtoMessage() {}
func (*SendMsgAllRsp) Descriptor() ([]byte, []int) {
return fileDescriptor_62691ff0994625a6, []int{5}
}
func (m *SendMsgAllRsp) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SendMsgAllRsp.Unmarshal(m, b)
}
func (m *SendMsgAllRsp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SendMsgAllRsp.Marshal(b, m, deterministic)
}
func (m *SendMsgAllRsp) XXX_Merge(src proto.Message) {
xxx_messageInfo_SendMsgAllRsp.Merge(m, src)
}
func (m *SendMsgAllRsp) XXX_Size() int {
return xxx_messageInfo_SendMsgAllRsp.Size(m)
}
func (m *SendMsgAllRsp) XXX_DiscardUnknown() {
xxx_messageInfo_SendMsgAllRsp.DiscardUnknown(m)
}
var xxx_messageInfo_SendMsgAllRsp proto.InternalMessageInfo
func (m *SendMsgAllRsp) GetRetCode() uint32 {
if m != nil {
return m.RetCode
}
return 0
}
func (m *SendMsgAllRsp) GetErrMsg() string {
if m != nil {
return m.ErrMsg
}
return ""
}
func (m *SendMsgAllRsp) GetSendMsgId() string {
if m != nil {
return m.SendMsgId
}
return ""
}
// 获取用户列表
type GetUserListReq struct {
AppId uint32 `protobuf:"varint,1,opt,name=appId,proto3" json:"appId,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GetUserListReq) Reset() { *m = GetUserListReq{} }
func (m *GetUserListReq) String() string { return proto.CompactTextString(m) }
func (*GetUserListReq) ProtoMessage() {}
func (*GetUserListReq) Descriptor() ([]byte, []int) {
return fileDescriptor_62691ff0994625a6, []int{6}
}
func (m *GetUserListReq) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetUserListReq.Unmarshal(m, b)
}
func (m *GetUserListReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetUserListReq.Marshal(b, m, deterministic)
}
func (m *GetUserListReq) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetUserListReq.Merge(m, src)
}
func (m *GetUserListReq) XXX_Size() int {
return xxx_messageInfo_GetUserListReq.Size(m)
}
func (m *GetUserListReq) XXX_DiscardUnknown() {
xxx_messageInfo_GetUserListReq.DiscardUnknown(m)
}
var xxx_messageInfo_GetUserListReq proto.InternalMessageInfo
func (m *GetUserListReq) GetAppId() uint32 {
if m != nil {
return m.AppId
}
return 0
}
type GetUserListRsp struct {
RetCode uint32 `protobuf:"varint,1,opt,name=retCode,proto3" json:"retCode,omitempty"`
ErrMsg string `protobuf:"bytes,2,opt,name=errMsg,proto3" json:"errMsg,omitempty"`
UserId []string `protobuf:"bytes,3,rep,name=userId,proto3" json:"userId,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GetUserListRsp) Reset() { *m = GetUserListRsp{} }
func (m *GetUserListRsp) String() string { return proto.CompactTextString(m) }
func (*GetUserListRsp) ProtoMessage() {}
func (*GetUserListRsp) Descriptor() ([]byte, []int) {
return fileDescriptor_62691ff0994625a6, []int{7}
}
func (m *GetUserListRsp) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetUserListRsp.Unmarshal(m, b)
}
func (m *GetUserListRsp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetUserListRsp.Marshal(b, m, deterministic)
}
func (m *GetUserListRsp) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetUserListRsp.Merge(m, src)
}
func (m *GetUserListRsp) XXX_Size() int {
return xxx_messageInfo_GetUserListRsp.Size(m)
}
func (m *GetUserListRsp) XXX_DiscardUnknown() {
xxx_messageInfo_GetUserListRsp.DiscardUnknown(m)
}
var xxx_messageInfo_GetUserListRsp proto.InternalMessageInfo
func (m *GetUserListRsp) GetRetCode() uint32 {
if m != nil {
return m.RetCode
}
return 0
}
func (m *GetUserListRsp) GetErrMsg() string {
if m != nil {
return m.ErrMsg
}
return ""
}
func (m *GetUserListRsp) GetUserId() []string {
if m != nil {
return m.UserId
}
return nil
}
func init() {
proto.RegisterType((*QueryUsersOnlineReq)(nil), "protobuf.QueryUsersOnlineReq")
proto.RegisterType((*QueryUsersOnlineRsp)(nil), "protobuf.QueryUsersOnlineRsp")
proto.RegisterType((*SendMsgReq)(nil), "protobuf.SendMsgReq")
proto.RegisterType((*SendMsgRsp)(nil), "protobuf.SendMsgRsp")
proto.RegisterType((*SendMsgAllReq)(nil), "protobuf.SendMsgAllReq")
proto.RegisterType((*SendMsgAllRsp)(nil), "protobuf.SendMsgAllRsp")
proto.RegisterType((*GetUserListReq)(nil), "protobuf.GetUserListReq")
proto.RegisterType((*GetUserListRsp)(nil), "protobuf.GetUserListRsp")
}
func init() { proto.RegisterFile("im_protobuf.proto", fileDescriptor_62691ff0994625a6) }
var fileDescriptor_62691ff0994625a6 = []byte{
// 413 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x53, 0x4d, 0xab, 0xd3, 0x40,
0x14, 0x35, 0xcd, 0x7b, 0xed, 0xcb, 0x95, 0xca, 0x73, 0x7c, 0xe8, 0x18, 0x14, 0xca, 0x2c, 0xa4,
0x0b, 0xc9, 0x42, 0x17, 0x6e, 0x6d, 0xbb, 0x90, 0x42, 0x8b, 0x35, 0xc5, 0x8d, 0x08, 0xa5, 0x4d,
0xae, 0x21, 0x90, 0x34, 0xd3, 0xb9, 0xa9, 0xd8, 0x5f, 0xe0, 0xbf, 0xd0, 0xbf, 0x2a, 0x33, 0xf9,
0x6a, 0x35, 0x75, 0x51, 0x10, 0x57, 0xb9, 0xe7, 0xde, 0x73, 0x0f, 0x67, 0x2e, 0x27, 0xf0, 0x30,
0x4e, 0x57, 0x52, 0x65, 0x79, 0xb6, 0xd9, 0x7f, 0xf1, 0x4c, 0xc1, 0x6e, 0x2a, 0x2c, 0x26, 0xf0,
0xe8, 0xc3, 0x1e, 0xd5, 0xe1, 0x23, 0xa1, 0xa2, 0xf7, 0xdb, 0x24, 0xde, 0xa2, 0x8f, 0x3b, 0x76,
0x07, 0xd7, 0x6b, 0x29, 0xa7, 0x21, 0xb7, 0x06, 0xd6, 0xb0, 0xef, 0x17, 0x80, 0x3d, 0x86, 0xee,
0x9e, 0x50, 0x4d, 0x43, 0xde, 0x19, 0x58, 0x43, 0xc7, 0x2f, 0x91, 0x58, 0xb5, 0x88, 0x90, 0x64,
0x1c, 0x7a, 0x0a, 0xf3, 0x49, 0x16, 0x62, 0x29, 0x53, 0x41, 0x2d, 0x84, 0x4a, 0xcd, 0x29, 0xaa,
0x84, 0x0a, 0xa4, 0xfb, 0x99, 0x59, 0xe7, 0xf6, 0xc0, 0x1a, 0xde, 0xf8, 0x25, 0x12, 0x3f, 0x2c,
0x80, 0x25, 0x6e, 0xc3, 0x39, 0x45, 0xda, 0xdd, 0x2d, 0xd8, 0x84, 0x3b, 0x23, 0xea, 0xf8, 0xba,
0x6c, 0xfc, 0x76, 0xda, 0xfd, 0xda, 0xc7, 0x7e, 0xf5, 0x7e, 0x90, 0x12, 0xbf, 0x2a, 0xf6, 0x83,
0x94, 0x18, 0x83, 0xab, 0xfc, 0x20, 0x91, 0x5f, 0x9b, 0x96, 0xa9, 0x35, 0x2b, 0xa5, 0x88, 0x77,
0x0b, 0x56, 0x4a, 0x91, 0x7e, 0x50, 0x4c, 0xb3, 0x2c, 0x58, 0x27, 0xbc, 0x67, 0xfc, 0x55, 0x50,
0x7c, 0x6e, 0xfc, 0x5d, 0xf4, 0xf0, 0x67, 0xe0, 0x50, 0xb1, 0x5f, 0x9b, 0x6d, 0x1a, 0xe2, 0xbb,
0x05, 0xfd, 0x52, 0x7e, 0x94, 0x24, 0xff, 0xf1, 0x02, 0x62, 0x75, 0x62, 0xe4, 0x1f, 0x3c, 0xf5,
0x05, 0x3c, 0x78, 0x87, 0xb9, 0x0e, 0xd2, 0x2c, 0xa6, 0xfc, 0x6c, 0x14, 0xc5, 0xa7, 0x53, 0xde,
0xa5, 0x69, 0xab, 0x8f, 0x63, 0x37, 0xc7, 0x79, 0xf5, 0xb3, 0x03, 0xce, 0x28, 0x08, 0x96, 0xa8,
0xbe, 0xa2, 0x62, 0x3e, 0xdc, 0xfe, 0x1e, 0x6e, 0xf6, 0xdc, 0xab, 0x7f, 0xa8, 0x96, 0xbf, 0xc7,
0xfd, 0xdb, 0x98, 0xa4, 0xb8, 0xc7, 0xde, 0x40, 0xaf, 0x3c, 0x23, 0xbb, 0x6b, 0xb8, 0x4d, 0xc2,
0xdd, 0x96, 0xae, 0x59, 0x7c, 0x5b, 0xe7, 0x6c, 0x94, 0x24, 0xec, 0xc9, 0x1f, 0xac, 0x22, 0x1e,
0x6e, 0xfb, 0xc0, 0x28, 0x4c, 0xe0, 0xfe, 0xd1, 0xe1, 0x18, 0x6f, 0x98, 0xa7, 0x77, 0x77, 0xcf,
0x4c, 0xb4, 0xc8, 0xf8, 0x25, 0x3c, 0x8d, 0x33, 0x2f, 0x52, 0x32, 0xf0, 0xf0, 0xdb, 0x3a, 0x95,
0x09, 0x52, 0xcd, 0x1e, 0xf7, 0x17, 0x65, 0x65, 0xbe, 0x0b, 0x6b, 0xd3, 0x35, 0xa3, 0xd7, 0xbf,
0x02, 0x00, 0x00, 0xff, 0xff, 0x53, 0x3e, 0x6a, 0x5a, 0x89, 0x04, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// AccServerClient is the client API for AccServer service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type AccServerClient interface {
// 查询用户是否在线
QueryUsersOnline(ctx context.Context, in *QueryUsersOnlineReq, opts ...grpc.CallOption) (*QueryUsersOnlineRsp, error)
// 发送消息
SendMsg(ctx context.Context, in *SendMsgReq, opts ...grpc.CallOption) (*SendMsgRsp, error)
// 发送消息
SendMsgAll(ctx context.Context, in *SendMsgAllReq, opts ...grpc.CallOption) (*SendMsgAllRsp, error)
// 获取用户列表
GetUserList(ctx context.Context, in *GetUserListReq, opts ...grpc.CallOption) (*GetUserListRsp, error)
}
type accServerClient struct {
cc *grpc.ClientConn
}
func NewAccServerClient(cc *grpc.ClientConn) AccServerClient {
return &accServerClient{cc}
}
func (c *accServerClient) QueryUsersOnline(ctx context.Context, in *QueryUsersOnlineReq, opts ...grpc.CallOption) (*QueryUsersOnlineRsp, error) {
out := new(QueryUsersOnlineRsp)
err := c.cc.Invoke(ctx, "/protobuf.AccServer/QueryUsersOnline", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *accServerClient) SendMsg(ctx context.Context, in *SendMsgReq, opts ...grpc.CallOption) (*SendMsgRsp, error) {
out := new(SendMsgRsp)
err := c.cc.Invoke(ctx, "/protobuf.AccServer/SendMsg", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *accServerClient) SendMsgAll(ctx context.Context, in *SendMsgAllReq, opts ...grpc.CallOption) (*SendMsgAllRsp, error) {
out := new(SendMsgAllRsp)
err := c.cc.Invoke(ctx, "/protobuf.AccServer/SendMsgAll", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *accServerClient) GetUserList(ctx context.Context, in *GetUserListReq, opts ...grpc.CallOption) (*GetUserListRsp, error) {
out := new(GetUserListRsp)
err := c.cc.Invoke(ctx, "/protobuf.AccServer/GetUserList", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// AccServerServer is the server API for AccServer service.
type AccServerServer interface {
// 查询用户是否在线
QueryUsersOnline(context.Context, *QueryUsersOnlineReq) (*QueryUsersOnlineRsp, error)
// 发送消息
SendMsg(context.Context, *SendMsgReq) (*SendMsgRsp, error)
// 发送消息
SendMsgAll(context.Context, *SendMsgAllReq) (*SendMsgAllRsp, error)
// 获取用户列表
GetUserList(context.Context, *GetUserListReq) (*GetUserListRsp, error)
}
// UnimplementedAccServerServer can be embedded to have forward compatible implementations.
type UnimplementedAccServerServer struct {
}
func (*UnimplementedAccServerServer) QueryUsersOnline(ctx context.Context, req *QueryUsersOnlineReq) (*QueryUsersOnlineRsp, error) {
return nil, status.Errorf(codes.Unimplemented, "method QueryUsersOnline not implemented")
}
func (*UnimplementedAccServerServer) SendMsg(ctx context.Context, req *SendMsgReq) (*SendMsgRsp, error) {
return nil, status.Errorf(codes.Unimplemented, "method SendMsg not implemented")
}
func (*UnimplementedAccServerServer) SendMsgAll(ctx context.Context, req *SendMsgAllReq) (*SendMsgAllRsp, error) {
return nil, status.Errorf(codes.Unimplemented, "method SendMsgAll not implemented")
}
func (*UnimplementedAccServerServer) GetUserList(ctx context.Context, req *GetUserListReq) (*GetUserListRsp, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetUserList not implemented")
}
func RegisterAccServerServer(s *grpc.Server, srv AccServerServer) {
s.RegisterService(&_AccServer_serviceDesc, srv)
}
func _AccServer_QueryUsersOnline_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(QueryUsersOnlineReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AccServerServer).QueryUsersOnline(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/protobuf.AccServer/QueryUsersOnline",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AccServerServer).QueryUsersOnline(ctx, req.(*QueryUsersOnlineReq))
}
return interceptor(ctx, in, info, handler)
}
func _AccServer_SendMsg_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SendMsgReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AccServerServer).SendMsg(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/protobuf.AccServer/SendMsg",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AccServerServer).SendMsg(ctx, req.(*SendMsgReq))
}
return interceptor(ctx, in, info, handler)
}
func _AccServer_SendMsgAll_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SendMsgAllReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AccServerServer).SendMsgAll(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/protobuf.AccServer/SendMsgAll",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AccServerServer).SendMsgAll(ctx, req.(*SendMsgAllReq))
}
return interceptor(ctx, in, info, handler)
}
func _AccServer_GetUserList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetUserListReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AccServerServer).GetUserList(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/protobuf.AccServer/GetUserList",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AccServerServer).GetUserList(ctx, req.(*GetUserListReq))
}
return interceptor(ctx, in, info, handler)
}
var _AccServer_serviceDesc = grpc.ServiceDesc{
ServiceName: "protobuf.AccServer",
HandlerType: (*AccServerServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "QueryUsersOnline",
Handler: _AccServer_QueryUsersOnline_Handler,
},
{
MethodName: "SendMsg",
Handler: _AccServer_SendMsg_Handler,
},
{
MethodName: "SendMsgAll",
Handler: _AccServer_SendMsgAll_Handler,
},
{
MethodName: "GetUserList",
Handler: _AccServer_GetUserList_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "im_protobuf.proto",
}

@ -0,0 +1,80 @@
syntax = "proto3";
option java_multiple_files = true;
option java_package = "io.grpc.examples.protobuf";
option java_outer_classname = "ProtobufProto";
package protobuf;
// The AccServer service definition.
service AccServer {
// 线
rpc QueryUsersOnline (QueryUsersOnlineReq) returns (QueryUsersOnlineRsp) {
}
//
rpc SendMsg (SendMsgReq) returns (SendMsgRsp) {
}
//
rpc SendMsgAll (SendMsgAllReq) returns (SendMsgAllRsp) {
}
//
rpc GetUserList (GetUserListReq) returns (GetUserListRsp) {
}
}
// 线
message QueryUsersOnlineReq {
uint32 appId = 1; // AppID
string userId = 2; // ID
}
message QueryUsersOnlineRsp {
uint32 retCode = 1;
string errMsg = 2;
bool online = 3;
}
//
message SendMsgReq {
string seq = 1; //
uint32 appId = 2; // appId/Id
string userId = 3; // ID
string cms = 4; // cms : msg/enter/exit
string type = 5; // type text
string msg = 6; // msg
bool isLocal = 7; // acc:true()
}
message SendMsgRsp {
uint32 retCode = 1;
string errMsg = 2;
string sendMsgId = 3;
}
//
message SendMsgAllReq {
string seq = 1; //
uint32 appId = 2; // appId/Id
string userId = 3; // ID
string cms = 4; // cms : msg/enter/exit
string type = 5; // type text
string msg = 6; // msg
}
message SendMsgAllRsp {
uint32 retCode = 1;
string errMsg = 2;
string sendMsgId = 3;
}
//
message GetUserListReq {
uint32 appId = 1;
}
message GetUserListRsp {
uint32 retCode = 1;
string errMsg = 2;
repeated string userId = 3;
}

@ -0,0 +1,19 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-07-25
* Time: 16:02
*/
package routers
import (
"gowebsocket/servers/websocket"
)
// Websocket 路由
func WebsocketInit() {
websocket.Register("login", websocket.LoginController)
websocket.Register("heartbeat", websocket.HeartbeatController)
websocket.Register("ping", websocket.PingController)
}

@ -0,0 +1,42 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-07-25
* Time: 12:20
*/
package routers
import (
"github.com/gin-gonic/gin"
"gowebsocket/controllers/home"
"gowebsocket/controllers/systems"
"gowebsocket/controllers/user"
)
func Init(router *gin.Engine) {
router.LoadHTMLGlob("views/**/*")
// 用户组
userRouter := router.Group("/user")
{
userRouter.GET("/list", user.List)
userRouter.GET("/online", user.Online)
userRouter.POST("/sendMessage", user.SendMessage)
userRouter.POST("/sendMessageAll", user.SendMessageAll)
}
// 系统
systemRouter := router.Group("/system")
{
systemRouter.GET("/state", systems.Status)
}
// home
homeRouter := router.Group("/home")
{
homeRouter.GET("/index", home.Index)
}
// router.POST("/user/online", user.Online)
}

@ -0,0 +1,149 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-08-03
* Time: 16:43
*/
package grpcclient
import (
"context"
"errors"
"fmt"
"google.golang.org/grpc"
"gowebsocket/common"
"gowebsocket/models"
"gowebsocket/protobuf"
"time"
)
// rpc client
// 给全体用户发送消息
// link::https://github.com/grpc/grpc-go/blob/master/examples/helloworld/greeter_client/main.go
func SendMsgAll(server *models.Server, seq string, appId uint32, userId string, cmd string, message string) (sendMsgId string, err error) {
// Set up a connection to the server.
conn, err := grpc.Dial(server.String(), grpc.WithInsecure())
if err != nil {
fmt.Println("连接失败", server.String())
return
}
defer conn.Close()
c := protobuf.NewAccServerClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
req := protobuf.SendMsgAllReq{
Seq: seq,
AppId: appId,
UserId: userId,
Cms: cmd,
Msg: message,
}
rsp, err := c.SendMsgAll(ctx, &req)
if err != nil {
fmt.Println("给全体用户发送消息", err)
return
}
if rsp.GetRetCode() != common.OK {
fmt.Println("给全体用户发送消息", rsp.String())
err = errors.New(fmt.Sprintf("发送消息失败 code:%d", rsp.GetRetCode()))
return
}
sendMsgId = rsp.GetSendMsgId()
fmt.Println("给全体用户发送消息 成功:", sendMsgId)
return
}
// 获取用户列表
// link::https://github.com/grpc/grpc-go/blob/master/examples/helloworld/greeter_client/main.go
func GetUserList(server *models.Server, appId uint32) (userIds []string, err error) {
userIds = make([]string, 0)
conn, err := grpc.Dial(server.String(), grpc.WithInsecure())
if err != nil {
fmt.Println("连接失败", server.String())
return
}
defer conn.Close()
c := protobuf.NewAccServerClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
req := protobuf.GetUserListReq{
AppId: appId,
}
rsp, err := c.GetUserList(ctx, &req)
if err != nil {
fmt.Println("获取用户列表 发送请求错误:", err)
return
}
if rsp.GetRetCode() != common.OK {
fmt.Println("获取用户列表 返回码错误:", rsp.String())
err = errors.New(fmt.Sprintf("发送消息失败 code:%d", rsp.GetRetCode()))
return
}
userIds = rsp.GetUserId()
fmt.Println("获取用户列表 成功:", userIds)
return
}
// rpc client
// 发送消息
// link::https://github.com/grpc/grpc-go/blob/master/examples/helloworld/greeter_client/main.go
func SendMsg(server *models.Server, seq string, appId uint32, userId string, cmd string, msgType string, message string) (sendMsgId string, err error) {
// Set up a connection to the server.
conn, err := grpc.Dial(server.String(), grpc.WithInsecure())
if err != nil {
fmt.Println("连接失败", server.String())
return
}
defer conn.Close()
c := protobuf.NewAccServerClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
req := protobuf.SendMsgReq{
Seq: seq,
AppId: appId,
UserId: userId,
Cms: cmd,
Type: msgType,
Msg: message,
IsLocal: false,
}
rsp, err := c.SendMsg(ctx, &req)
if err != nil {
fmt.Println("发送消息", err)
return
}
if rsp.GetRetCode() != common.OK {
fmt.Println("发送消息", rsp.String())
err = errors.New(fmt.Sprintf("发送消息失败 code:%d", rsp.GetRetCode()))
return
}
sendMsgId = rsp.GetSendMsgId()
fmt.Println("发送消息 成功:", sendMsgId)
return
}

@ -0,0 +1,153 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-08-03
* Time: 16:43
*/
package grpcserver
import (
"context"
"fmt"
"github.com/golang/protobuf/proto"
"github.com/spf13/viper"
"google.golang.org/grpc"
"gowebsocket/common"
"gowebsocket/models"
"gowebsocket/protobuf"
"gowebsocket/servers/websocket"
"log"
"net"
)
type server struct {
}
func setErr(rsp proto.Message, code uint32, message string) {
message = common.GetErrorMessage(code, message)
switch v := rsp.(type) {
case *protobuf.QueryUsersOnlineRsp:
v.RetCode = code
v.ErrMsg = message
case *protobuf.SendMsgRsp:
v.RetCode = code
v.ErrMsg = message
case *protobuf.SendMsgAllRsp:
v.RetCode = code
v.ErrMsg = message
case *protobuf.GetUserListRsp:
v.RetCode = code
v.ErrMsg = message
default:
}
}
// 查询用户是否在线
func (s *server) QueryUsersOnline(c context.Context, req *protobuf.QueryUsersOnlineReq) (rsp *protobuf.QueryUsersOnlineRsp, err error) {
fmt.Println("grpc_request 查询用户是否在线", req.String())
rsp = &protobuf.QueryUsersOnlineRsp{}
online := websocket.CheckUserOnline(req.GetAppId(), req.GetUserId())
setErr(req, common.OK, "")
rsp.Online = online
return rsp, nil
}
// 给本机用户发消息
func (s *server) SendMsg(c context.Context, req *protobuf.SendMsgReq) (rsp *protobuf.SendMsgRsp, err error) {
fmt.Println("grpc_request 给本机用户发消息", req.String())
rsp = &protobuf.SendMsgRsp{}
if req.GetIsLocal() {
// 不支持
setErr(rsp, common.ParameterIllegal, "")
return
}
data := models.GetMsgData(req.GetUserId(), req.GetSeq(), req.GetCms(), req.GetMsg())
sendResults, err := websocket.SendUserMessageLocal(req.GetAppId(), req.GetUserId(), data)
if err != nil {
fmt.Println("系统错误", err)
setErr(rsp, common.ServerError, "")
return rsp, nil
}
if !sendResults {
fmt.Println("发送失败", err)
setErr(rsp, common.OperationFailure, "")
return rsp, nil
}
setErr(rsp, common.OK, "")
fmt.Println("grpc_response 给本机用户发消息", rsp.String())
return
}
// 给本机全体用户发消息
func (s *server) SendMsgAll(c context.Context, req *protobuf.SendMsgAllReq) (rsp *protobuf.SendMsgAllRsp, err error) {
fmt.Println("grpc_request 给本机全体用户发消息", req.String())
rsp = &protobuf.SendMsgAllRsp{}
data := models.GetMsgData(req.GetUserId(), req.GetSeq(), req.GetCms(), req.GetMsg())
websocket.AllSendMessages(req.GetAppId(), req.GetUserId(), data)
setErr(rsp, common.OK, "")
fmt.Println("grpc_response 给本机全体用户发消息:", rsp.String())
return
}
// 获取本机用户列表
func (s *server) GetUserList(c context.Context, req *protobuf.GetUserListReq) (rsp *protobuf.GetUserListRsp, err error) {
fmt.Println("grpc_request 获取本机用户列表", req.String())
appId := req.GetAppId()
rsp = &protobuf.GetUserListRsp{}
// 本机
userList := websocket.GetUserList(appId)
setErr(rsp, common.OK, "")
rsp.UserId = userList
fmt.Println("grpc_response 获取本机用户列表:", rsp.String())
return
}
// rpc server
// link::https://github.com/grpc/grpc-go/blob/master/examples/helloworld/greeter_server/main.go
func Init() {
rpcPort := viper.GetString("app.rpcPort")
fmt.Println("rpc server 启动", rpcPort)
lis, err := net.Listen("tcp", ":"+rpcPort)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
protobuf.RegisterAccServerServer(s, &server{})
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}

@ -0,0 +1,37 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-07-31
* Time: 15:17
*/
package task
import (
"fmt"
"gowebsocket/servers/websocket"
"runtime/debug"
"time"
)
func Init() {
Timer(3*time.Second, 30*time.Second, cleanConnection, "", nil, nil)
}
// 清理超时连接
func cleanConnection(param interface{}) (result bool) {
result = true
defer func() {
if r := recover(); r != nil {
fmt.Println("ClearTimeoutConnections stop", r, string(debug.Stack()))
}
}()
fmt.Println("定时任务,清理超时连接", param)
websocket.ClearTimeoutConnections()
return
}

@ -0,0 +1,55 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-08-03
* Time: 15:44
*/
package task
import (
"fmt"
"gowebsocket/lib/cache"
"gowebsocket/servers/websocket"
"runtime/debug"
"time"
)
func ServerInit() {
Timer(2*time.Second, 60*time.Second, server, "", serverDefer, "")
}
// 服务注册
func server(param interface{}) (result bool) {
result = true
defer func() {
if r := recover(); r != nil {
fmt.Println("服务注册 stop", r, string(debug.Stack()))
}
}()
server := websocket.GetServer()
currentTime := uint64(time.Now().Unix())
fmt.Println("定时任务,服务注册", param, server, currentTime)
cache.SetServerInfo(server, currentTime)
return
}
// 服务下线
func serverDefer(param interface{}) (result bool) {
defer func() {
if r := recover(); r != nil {
fmt.Println("服务下线 stop", r, string(debug.Stack()))
}
}()
fmt.Println("服务下线", param)
server := websocket.GetServer()
cache.DelServerInfo(server)
return
}

@ -0,0 +1,46 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-07-29
* Time: 14:15
*/
package task
import "time"
type TimerFunc func(interface{}) bool
/**
*
* @delay
* @tick
* @fun function
* @param fun
*/
func Timer(delay, tick time.Duration, fun TimerFunc, param interface{}, funcDefer TimerFunc, paramDefer interface{}) {
go func() {
defer func() {
if funcDefer != nil {
funcDefer(paramDefer)
}
}()
if fun == nil {
return
}
t := time.NewTimer(delay)
defer t.Stop()
for {
select {
case <-t.C:
if fun(param) == false {
return
}
t.Reset(tick)
}
}
}()
}

@ -0,0 +1,144 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-07-27
* Time: 13:12
*/
package websocket
import (
"encoding/json"
"fmt"
"github.com/go-redis/redis"
"gowebsocket/common"
"gowebsocket/lib/cache"
"gowebsocket/models"
"time"
)
// ping
func PingController(client *Client, seq string, message []byte) (code uint32, msg string, data interface{}) {
code = common.OK
fmt.Println("webSocket_request ping接口", client.Addr, seq, message)
data = "pong"
return
}
// 用户登录
func LoginController(client *Client, seq string, message []byte) (code uint32, msg string, data interface{}) {
code = common.OK
currentTime := uint64(time.Now().Unix())
request := &models.Login{}
if err := json.Unmarshal(message, request); err != nil {
code = common.ParameterIllegal
fmt.Println("用户登录 解析数据失败", seq, err)
return
}
fmt.Println("webSocket_request 用户登录", seq, "ServiceToken", request.ServiceToken)
// TODO::进行用户权限认证一般是客户端传入TOKEN然后检验TOKEN是否合法通过TOKEN解析出来用户ID
// 本项目只是演示所以直接过去客户端传入的用户ID
if request.UserId == "" || len(request.UserId) >= 20 {
code = common.UnauthorizedUserId
fmt.Println("用户登录 非法的用户", seq, request.UserId)
return
}
if !InAppIds(request.AppId) {
code = common.Unauthorized
fmt.Println("用户登录 不支持的平台", seq, request.AppId)
return
}
if client.IsLogin() {
fmt.Println("用户登录 用户已经登录", client.AppId, client.UserId, seq)
code = common.OperationFailure
return
}
client.Login(request.AppId, request.UserId, currentTime)
// 存储数据
userOnline := models.UserLogin(serverIp, serverPort, request.AppId, request.UserId, client.Addr, currentTime)
err := cache.SetUserOnlineInfo(client.GetKey(), userOnline)
if err != nil {
code = common.ServerError
fmt.Println("用户登录 SetUserOnlineInfo", seq, err)
return
}
// 用户登录
login := &login{
AppId: request.AppId,
UserId: request.UserId,
Client: client,
}
clientManager.Login <- login
fmt.Println("用户登录 成功", seq, client.Addr, request.UserId)
return
}
// 心跳接口
func HeartbeatController(client *Client, seq string, message []byte) (code uint32, msg string, data interface{}) {
code = common.OK
currentTime := uint64(time.Now().Unix())
request := &models.HeartBeat{}
if err := json.Unmarshal(message, request); err != nil {
code = common.ParameterIllegal
fmt.Println("心跳接口 解析数据失败", seq, err)
return
}
fmt.Println("webSocket_request 心跳接口", client.AppId, client.UserId)
if !client.IsLogin() {
fmt.Println("心跳接口 用户未登录", client.AppId, client.UserId, seq)
code = common.NotLoggedIn
return
}
userOnline, err := cache.GetUserOnlineInfo(client.GetKey())
if err != nil {
if err == redis.Nil {
code = common.NotLoggedIn
fmt.Println("心跳接口 用户未登录", seq, client.AppId, client.UserId)
return
} else {
code = common.ServerError
fmt.Println("心跳接口 GetUserOnlineInfo", seq, client.AppId, client.UserId, err)
return
}
}
client.Heartbeat(currentTime)
userOnline.Heartbeat(currentTime)
err = cache.SetUserOnlineInfo(client.GetKey(), userOnline)
if err != nil {
code = common.ServerError
fmt.Println("心跳接口 SetUserOnlineInfo", seq, client.AppId, client.UserId, err)
return
}
return
}

@ -0,0 +1,108 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-07-27
* Time: 14:38
*/
package websocket
import (
"encoding/json"
"fmt"
"gowebsocket/common"
"gowebsocket/models"
"sync"
)
type DisposeFunc func(client *Client, seq string, message []byte) (code uint32, msg string, data interface{})
var (
handlers = make(map[string]DisposeFunc)
handlersRWMutex sync.RWMutex
)
// 注册
func Register(key string, value DisposeFunc) {
handlersRWMutex.Lock()
defer handlersRWMutex.Unlock()
handlers[key] = value
return
}
func getHandlers(key string) (value DisposeFunc, ok bool) {
handlersRWMutex.RLock()
defer handlersRWMutex.RUnlock()
value, ok = handlers[key]
return
}
// 处理数据
func ProcessData(client *Client, message []byte) {
fmt.Println("处理数据", client.Addr, string(message))
defer func() {
if r := recover(); r != nil {
fmt.Println("处理数据 stop", r)
}
}()
request := &models.Request{}
err := json.Unmarshal(message, request)
if err != nil {
fmt.Println("处理数据 json Unmarshal", err)
client.SendMsg([]byte("数据不合法"))
return
}
requestData, err := json.Marshal(request.Data)
if err != nil {
fmt.Println("处理数据 json Marshal", err)
client.SendMsg([]byte("处理数据失败"))
return
}
seq := request.Seq
cmd := request.Cmd
var (
code uint32
msg string
data interface{}
)
// request
fmt.Println("acc_request", cmd, client.Addr)
// 采用 map 注册的方式
if value, ok := getHandlers(cmd); ok {
code, msg, data = value(client, seq, requestData)
} else {
code = common.RoutingNotExist
fmt.Println("处理数据 路由不存在", client.Addr, "cmd", cmd)
}
msg = common.GetErrorMessage(code, msg)
responseHead := models.NewResponseHead(seq, cmd, code, msg, data)
headByte, err := json.Marshal(responseHead)
if err != nil {
fmt.Println("处理数据 json Marshal", err)
return
}
client.SendMsg(headByte)
fmt.Println("acc_response send", client.Addr, client.AppId, client.UserId, "cmd", cmd, "code", code)
return
}

@ -0,0 +1,182 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-07-25
* Time: 16:24
*/
package websocket
import (
"fmt"
"github.com/gorilla/websocket"
"runtime/debug"
)
const (
// 用户连接超时时间
heartbeatExpirationTime = 6 * 60
)
// 用户登录
type login struct {
AppId uint32
UserId string
Client *Client
}
// 读取客户端数据
func (l *login) GetKey() (key string) {
key = GetUserKey(l.AppId, l.UserId)
return
}
// 用户连接
type Client struct {
Addr string // 客户端地址
Socket *websocket.Conn // 用户连接
Send chan []byte // 待发送的数据
AppId uint32 // 登录的平台Id app/web/ios
UserId string // 用户Id用户登录以后才有
FirstTime uint64 // 首次连接事件
HeartbeatTime uint64 // 用户上次心跳时间
LoginTime uint64 // 登录时间 登录以后才有
}
// 初始化
func NewClient(addr string, socket *websocket.Conn, firstTime uint64) (client *Client) {
client = &Client{
Addr: addr,
Socket: socket,
Send: make(chan []byte, 100),
FirstTime: firstTime,
HeartbeatTime: firstTime,
}
return
}
// 读取客户端数据
func (c *Client) GetKey() (key string) {
key = GetUserKey(c.AppId, c.UserId)
return
}
// 读取客户端数据
func (c *Client) read() {
defer func() {
if r := recover(); r != nil {
fmt.Println("write stop", string(debug.Stack()), r)
}
}()
defer func() {
fmt.Println("读取客户端数据 关闭send", c)
close(c.Send)
}()
for {
_, message, err := c.Socket.ReadMessage()
if err != nil {
fmt.Println("读取客户端数据 错误", c.Addr, err)
return
}
// 处理程序
fmt.Println("读取客户端数据 处理:", string(message))
ProcessData(c, message)
}
}
// 向客户端写数据
func (c *Client) write() {
defer func() {
if r := recover(); r != nil {
fmt.Println("write stop", string(debug.Stack()), r)
}
}()
defer func() {
clientManager.Unregister <- c
c.Socket.Close()
fmt.Println("Client发送数据 defer", c)
}()
for {
select {
case message, ok := <-c.Send:
if !ok {
// 发送数据错误 关闭连接
fmt.Println("Client发送数据 关闭连接", c.Addr, "ok", ok)
return
}
c.Socket.WriteMessage(websocket.TextMessage, message)
}
}
}
// 读取客户端数据
func (c *Client) SendMsg(msg []byte) {
if c == nil {
return
}
defer func() {
if r := recover(); r != nil {
fmt.Println("SendMsg stop:", r, string(debug.Stack()))
}
}()
c.Send <- msg
}
// 读取客户端数据
func (c *Client) close() {
close(c.Send)
}
// 用户登录
func (c *Client) Login(appId uint32, userId string, loginTime uint64) {
c.AppId = appId
c.UserId = userId
c.LoginTime = loginTime
// 登录成功=心跳一次
c.Heartbeat(loginTime)
}
// 用户心跳
func (c *Client) Heartbeat(currentTime uint64) {
c.HeartbeatTime = currentTime
return
}
// 心跳超时
func (c *Client) IsHeartbeatTimeout(currentTime uint64) (timeout bool) {
if c.HeartbeatTime+heartbeatExpirationTime <= currentTime {
timeout = true
}
return
}
// 是否登录了
func (c *Client) IsLogin() (isLogin bool) {
// 用户登录了
if c.UserId != "" {
isLogin = true
return
}
return
}

@ -0,0 +1,384 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-07-25
* Time: 16:24
*/
package websocket
import (
"fmt"
"gowebsocket/helper"
"gowebsocket/lib/cache"
"gowebsocket/models"
"sync"
"time"
)
// 连接管理
type ClientManager struct {
Clients map[*Client]bool // 全部的连接
ClientsLock sync.RWMutex // 读写锁
Users map[string]*Client // 登录的用户 // appId+uuid
UserLock sync.RWMutex // 读写锁
Register chan *Client // 连接连接处理
Login chan *login // 用户登录处理
Unregister chan *Client // 断开连接处理程序
Broadcast chan []byte // 广播 向全部成员发送数据
}
func NewClientManager() (clientManager *ClientManager) {
clientManager = &ClientManager{
Clients: make(map[*Client]bool),
Users: make(map[string]*Client),
Register: make(chan *Client, 1000),
Login: make(chan *login, 1000),
Unregister: make(chan *Client, 1000),
Broadcast: make(chan []byte, 1000),
}
return
}
// 获取用户key
func GetUserKey(appId uint32, userId string) (key string) {
key = fmt.Sprintf("%d_%s", appId, userId)
return
}
/************************** manager ***************************************/
func (manager *ClientManager) InClient(client *Client) (ok bool) {
manager.ClientsLock.RLock()
defer manager.ClientsLock.RUnlock()
// 连接存在,在添加
_, ok = manager.Clients[client]
return
}
// GetClients
func (manager *ClientManager) GetClients() (clients map[*Client]bool) {
clients = make(map[*Client]bool)
manager.ClientsRange(func(client *Client, value bool) (result bool) {
clients[client] = value
return true
})
return
}
// 遍历
func (manager *ClientManager) ClientsRange(f func(client *Client, value bool) (result bool)) {
manager.ClientsLock.RLock()
defer manager.ClientsLock.RUnlock()
for key, value := range manager.Clients {
result := f(key, value)
if result == false {
return
}
}
return
}
// GetClientsLen
func (manager *ClientManager) GetClientsLen() (clientsLen int) {
clientsLen = len(manager.Clients)
return
}
// 添加客户端
func (manager *ClientManager) AddClients(client *Client) {
manager.ClientsLock.Lock()
defer manager.ClientsLock.Unlock()
manager.Clients[client] = true
}
// 删除客户端
func (manager *ClientManager) DelClients(client *Client) {
manager.ClientsLock.Lock()
defer manager.ClientsLock.Unlock()
if _, ok := manager.Clients[client]; ok {
delete(manager.Clients, client)
}
}
// 获取用户的连接
func (manager *ClientManager) GetUserClient(appId uint32, userId string) (client *Client) {
manager.UserLock.RLock()
defer manager.UserLock.RUnlock()
userKey := GetUserKey(appId, userId)
if value, ok := manager.Users[userKey]; ok {
client = value
}
return
}
// GetClientsLen
func (manager *ClientManager) GetUsersLen() (userLen int) {
userLen = len(manager.Users)
return
}
// 添加用户
func (manager *ClientManager) AddUsers(key string, client *Client) {
manager.UserLock.Lock()
defer manager.UserLock.Unlock()
manager.Users[key] = client
}
// 删除用户
func (manager *ClientManager) DelUsers(client *Client) (result bool) {
manager.UserLock.Lock()
defer manager.UserLock.Unlock()
key := GetUserKey(client.AppId, client.UserId)
if value, ok := manager.Users[key]; ok {
// 判断是否为相同的用户
if value.Addr != client.Addr {
return
}
delete(manager.Users, key)
result = true
}
return
}
// 获取用户的key
func (manager *ClientManager) GetUserKeys() (userKeys []string) {
userKeys = make([]string, 0)
manager.UserLock.RLock()
defer manager.UserLock.RUnlock()
for key := range manager.Users {
userKeys = append(userKeys, key)
}
return
}
// 获取用户的key
func (manager *ClientManager) GetUserList(appId uint32) (userList []string) {
userList = make([]string, 0)
manager.UserLock.RLock()
defer manager.UserLock.RUnlock()
for _, v := range manager.Users {
if v.AppId == appId {
userList = append(userList, v.UserId)
}
}
fmt.Println("GetUserList len:", len(manager.Users))
return
}
// 获取用户的key
func (manager *ClientManager) GetUserClients() (clients []*Client) {
clients = make([]*Client, 0)
manager.UserLock.RLock()
defer manager.UserLock.RUnlock()
for _, v := range manager.Users {
clients = append(clients, v)
}
return
}
// 向全部成员(除了自己)发送数据
func (manager *ClientManager) sendAll(message []byte, ignoreClient *Client) {
clients := manager.GetUserClients()
for _, conn := range clients {
if conn != ignoreClient {
conn.SendMsg(message)
}
}
}
// 向全部成员(除了自己)发送数据
func (manager *ClientManager) sendAppIdAll(message []byte, appId uint32, ignoreClient *Client) {
clients := manager.GetUserClients()
for _, conn := range clients {
if conn != ignoreClient && conn.AppId == appId {
conn.SendMsg(message)
}
}
}
// 用户建立连接事件
func (manager *ClientManager) EventRegister(client *Client) {
manager.AddClients(client)
fmt.Println("EventRegister 用户建立连接", client.Addr)
// client.Send <- []byte("连接成功")
}
// 用户登录
func (manager *ClientManager) EventLogin(login *login) {
client := login.Client
// 连接存在,在添加
if manager.InClient(client) {
userKey := login.GetKey()
manager.AddUsers(userKey, login.Client)
}
fmt.Println("EventLogin 用户登录", client.Addr, login.AppId, login.UserId)
orderId := helper.GetOrderIdTime()
SendUserMessageAll(login.AppId, login.UserId, orderId, models.MessageCmdEnter, "哈喽~")
}
// 用户断开连接
func (manager *ClientManager) EventUnregister(client *Client) {
manager.DelClients(client)
// 删除用户连接
deleteResult := manager.DelUsers(client)
if deleteResult == false {
// 不是当前连接的客户端
return
}
// 清除redis登录数据
userOnline, err := cache.GetUserOnlineInfo(client.GetKey())
if err == nil {
userOnline.LogOut()
cache.SetUserOnlineInfo(client.GetKey(), userOnline)
}
// 关闭 chan
// close(client.Send)
fmt.Println("EventUnregister 用户断开连接", client.Addr, client.AppId, client.UserId)
if client.UserId != "" {
orderId := helper.GetOrderIdTime()
SendUserMessageAll(client.AppId, client.UserId, orderId, models.MessageCmdExit, "用户已经离开~")
}
}
// 管道处理程序
func (manager *ClientManager) start() {
for {
select {
case conn := <-manager.Register:
// 建立连接事件
manager.EventRegister(conn)
case login := <-manager.Login:
// 用户登录
manager.EventLogin(login)
case conn := <-manager.Unregister:
// 断开连接事件
manager.EventUnregister(conn)
case message := <-manager.Broadcast:
// 广播事件
clients := manager.GetClients()
for conn := range clients {
select {
case conn.Send <- message:
default:
close(conn.Send)
}
}
}
}
}
/************************** manager info ***************************************/
// 获取管理者信息
func GetManagerInfo(isDebug string) (managerInfo map[string]interface{}) {
managerInfo = make(map[string]interface{})
managerInfo["clientsLen"] = clientManager.GetClientsLen() // 客户端连接数
managerInfo["usersLen"] = clientManager.GetUsersLen() // 登录用户数
managerInfo["chanRegisterLen"] = len(clientManager.Register) // 未处理连接事件数
managerInfo["chanLoginLen"] = len(clientManager.Login) // 未处理登录事件数
managerInfo["chanUnregisterLen"] = len(clientManager.Unregister) // 未处理退出登录事件数
managerInfo["chanBroadcastLen"] = len(clientManager.Broadcast) // 未处理广播事件数
if isDebug == "true" {
addrList := make([]string, 0)
clientManager.ClientsRange(func(client *Client, value bool) (result bool) {
addrList = append(addrList, client.Addr)
return true
})
users := clientManager.GetUserKeys()
managerInfo["clients"] = addrList // 客户端列表
managerInfo["users"] = users // 登录用户列表
}
return
}
// 获取用户所在的连接
func GetUserClient(appId uint32, userId string) (client *Client) {
client = clientManager.GetUserClient(appId, userId)
return
}
// 定时清理超时连接
func ClearTimeoutConnections() {
currentTime := uint64(time.Now().Unix())
clients := clientManager.GetClients()
for client := range clients {
if client.IsHeartbeatTimeout(currentTime) {
fmt.Println("心跳时间超时 关闭连接", client.Addr, client.UserId, client.LoginTime, client.HeartbeatTime)
client.Socket.Close()
}
}
}
// 获取全部用户
func GetUserList(appId uint32) (userList []string) {
fmt.Println("获取全部用户", appId)
userList = clientManager.GetUserList(appId)
return
}
// 全员广播
func AllSendMessages(appId uint32, userId string, data string) {
fmt.Println("全员广播", appId, userId, data)
ignoreClient := clientManager.GetUserClient(appId, userId)
clientManager.sendAppIdAll([]byte(data), appId, ignoreClient)
}

@ -0,0 +1,115 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-07-25
* Time: 16:04
*/
package websocket
import (
"fmt"
"net/http"
"time"
"gowebsocket/helper"
"gowebsocket/models"
"github.com/gorilla/websocket"
"github.com/spf13/viper"
)
const (
defaultAppId = 101 // 默认平台Id
)
var (
clientManager = NewClientManager() // 管理者
appIds = []uint32{defaultAppId, 102, 103, 104} // 全部的平台
serverIp string
serverPort string
)
func GetAppIds() []uint32 {
return appIds
}
func GetServer() (server *models.Server) {
server = models.NewServer(serverIp, serverPort)
return
}
func IsLocal(server *models.Server) (isLocal bool) {
if server.Ip == serverIp && server.Port == serverPort {
isLocal = true
}
return
}
func InAppIds(appId uint32) (inAppId bool) {
for _, value := range appIds {
if value == appId {
inAppId = true
return
}
}
return
}
func GetDefaultAppId() (appId uint32) {
appId = defaultAppId
return
}
// 启动程序
func StartWebSocket() {
serverIp = helper.GetServerIp()
webSocketPort := viper.GetString("app.webSocketPort")
rpcPort := viper.GetString("app.rpcPort")
serverPort = rpcPort
http.HandleFunc("/acc", wsPage)
// 添加处理程序
go clientManager.start()
fmt.Println("WebSocket 启动程序成功", serverIp, serverPort)
http.ListenAndServe(":"+webSocketPort, nil)
}
func wsPage(w http.ResponseWriter, req *http.Request) {
// 升级协议
conn, err := (&websocket.Upgrader{CheckOrigin: func(r *http.Request) bool {
fmt.Println("升级协议", "ua:", r.Header["User-Agent"], "referer:", r.Header["Referer"])
return true
}}).Upgrade(w, req, nil)
if err != nil {
http.NotFound(w, req)
return
}
fmt.Println("webSocket 建立连接:", conn.RemoteAddr().String())
currentTime := uint64(time.Now().Unix())
client := NewClient(conn.RemoteAddr().String(), conn, currentTime)
go client.read()
go client.write()
// 用户连接事件
clientManager.Register <- client
}

@ -0,0 +1,165 @@
/**
* Created by GoLand.
* User: link1st
* Date: 2019-07-30
* Time: 12:27
*/
package websocket
import (
"errors"
"fmt"
"github.com/go-redis/redis"
"gowebsocket/lib/cache"
"gowebsocket/models"
"gowebsocket/servers/grpcclient"
"time"
)
// 查询所有用户
func UserList(appId uint32) (userList []string) {
userList = make([]string, 0)
currentTime := uint64(time.Now().Unix())
servers, err := cache.GetServerAll(currentTime)
if err != nil {
fmt.Println("给全体用户发消息", err)
return
}
for _, server := range servers {
var (
list []string
)
if IsLocal(server) {
list = GetUserList(appId)
} else {
list, _ = grpcclient.GetUserList(server, appId)
}
userList = append(userList, list...)
}
return
}
// 查询用户是否在线
func CheckUserOnline(appId uint32, userId string) (online bool) {
// 全平台查询
if appId == 0 {
for _, appId := range GetAppIds() {
online, _ = checkUserOnline(appId, userId)
if online == true {
break
}
}
} else {
online, _ = checkUserOnline(appId, userId)
}
return
}
// 查询用户 是否在线
func checkUserOnline(appId uint32, userId string) (online bool, err error) {
key := GetUserKey(appId, userId)
userOnline, err := cache.GetUserOnlineInfo(key)
if err != nil {
if err == redis.Nil {
fmt.Println("GetUserOnlineInfo", appId, userId, err)
return false, nil
}
fmt.Println("GetUserOnlineInfo", appId, userId, err)
return
}
online = userOnline.IsOnline()
return
}
// 给用户发送消息
func SendUserMessage(appId uint32, userId string, msgId, message string) (sendResults bool, err error) {
data := models.GetTextMsgData(userId, msgId, message)
client := GetUserClient(appId, userId)
if client != nil {
// 在本机发送
sendResults, err = SendUserMessageLocal(appId, userId, data)
if err != nil {
fmt.Println("给用户发送消息", appId, userId, err)
}
return
}
key := GetUserKey(appId, userId)
info, err := cache.GetUserOnlineInfo(key)
if err != nil {
fmt.Println("给用户发送消息失败", key, err)
return false, nil
}
if !info.IsOnline() {
fmt.Println("用户不在线", key)
return false, nil
}
server := models.NewServer(info.AccIp, info.AccPort)
msg, err := grpcclient.SendMsg(server, msgId, appId, userId, models.MessageCmdMsg, models.MessageCmdMsg, message)
if err != nil {
fmt.Println("给用户发送消息失败", key, err)
return false, err
}
fmt.Println("给用户发送消息成功-rpc", msg)
sendResults = true
return
}
// 给本机用户发送消息
func SendUserMessageLocal(appId uint32, userId string, data string) (sendResults bool, err error) {
client := GetUserClient(appId, userId)
if client == nil {
err = errors.New("用户不在线")
return
}
// 发送消息
client.SendMsg([]byte(data))
sendResults = true
return
}
// 给全体用户发消息
func SendUserMessageAll(appId uint32, userId string, msgId, cmd, message string) (sendResults bool, err error) {
sendResults = true
currentTime := uint64(time.Now().Unix())
servers, err := cache.GetServerAll(currentTime)
if err != nil {
fmt.Println("给全体用户发消息", err)
return
}
for _, server := range servers {
if IsLocal(server) {
data := models.GetMsgData(userId, msgId, cmd, message)
AllSendMessages(appId, userId, data)
} else {
grpcclient.SendMsgAll(server, msgId, appId, userId, cmd, message)
}
}
return
}

@ -0,0 +1,459 @@
<!DOCTYPE html>
<html>
<head>
<title>{{ .title }}</title>
<style type="text/css">
/*公共样式*/
body,
h1,
h2,
h3,
h4,
p,
ul,
ol,
li,
form,
button,
input,
textarea,
th,
td {
margin: 0;
padding: 0
}
body,
button,
input,
select,
textarea {
font: 12px/1.5 Microsoft YaHei UI Light, tahoma, arial, "\5b8b\4f53";
*line-height: 1.5;
-ms-overflow-style: scrollbar
}
h1,
h2,
h3,
h4 {
font-size: 100%
}
ul,
ol {
list-style: none
}
a {
text-decoration: none
}
a:hover {
text-decoration: underline
}
img {
border: 0
}
button,
input,
select,
textarea {
font-size: 100%
}
table {
border-collapse: collapse;
border-spacing: 0
}
/*rem*/
html {
font-size: 62.5%;
}
body {
font: 16px/1.5 "microsoft yahei", 'tahoma';
}
body .mobile-page {
font-size: 1.6rem;
}
/*浮动*/
.fl {
float: left;
}
.fr {
float: right;
}
.clearfix:after {
content: '';
display: block;
height: 0;
clear: both;
visibility: hidden;
}
body {
background-color: #F5F5F5;
}
.mobile-page {
max-width: 600px;
}
.mobile-page .admin-img,
.mobile-page .user-img {
width: 45px;
height: 45px;
}
i.triangle-admin,
i.triangle-user {
width: 0;
height: 0;
position: absolute;
top: 10px;
display: inline-block;
border-top: 10px solid transparent;
border-bottom: 10px solid transparent;
}
.mobile-page i.triangle-admin {
left: 4px;
border-right: 12px solid #fff;
}
.mobile-page i.triangle-user {
right: 4px;
border-left: 12px solid #9EEA6A;
}
.mobile-page .admin-group,
.mobile-page .user-group {
padding: 6px;
display: flex;
display: -webkit-flex;
}
.mobile-page .admin-group {
justify-content: flex-start;
-webkit-justify-content: flex-start;
}
.mobile-page .user-group {
justify-content: flex-end;
-webkit-justify-content: flex-end;
}
.mobile-page .admin-reply,
.mobile-page .user-reply {
display: inline-block;
padding: 8px;
border-radius: 4px;
background-color: #fff;
margin: 0 15px 12px;
}
.mobile-page .admin-reply {
box-shadow: 0px 0px 2px #ddd;
}
.mobile-page .user-reply {
text-align: left;
background-color: #9EEA6A;
box-shadow: 0px 0px 2px #bbb;
}
.mobile-page .user-msg,
.mobile-page .admin-msg {
width: 75%;
position: relative;
}
.mobile-page .user-msg {
text-align: right;
}
/*界面*/
.interface {
width: 1000px;
height: 700px;
}
.personnel-list {
float: left;
width: 200px;
height: 500px;
background-color: #bbbbbb;
border-style: solid;
border-color: #000000;
overflow: scroll;
}
/*聊天框*/
.chat-with {
float: left;
width: 600px;
height: 400px;
background-color: #bbbbbb;
border-style: solid;
border-color: #000000;
overflow: scroll;
}
.send-msg {
float: left;
width: 600px;
height: 100px;
background-color: #bbbbbb;
border-style: solid;
border-color: #000000;
overflow: scroll;
}
</style>
</head>
<body>
<div class="mobile-page">
<div class="interface">
<div class="personnel-list">
<ul class="personnel-list-ul">
</ul>
<!-- 在线列表 -->
<!-- 用户列表
进入的时候拉取用户列表
有人加入的时候添加
有人退出以后删除 -->
</div>
<div class="chat-with">
<div class="admin-group">
<div class="admin-img">
管理员
</div>
<div class="admin-msg">
<i class="triangle-admin"></i>
<span class="admin-reply">欢迎加入聊天~</span>
</div>
</div>
</div>
<div class="send-msg">
<!-- <input type="text" name="msg" placeholder="你想要发送的消息" value="" size="35">
<button type="submit"> send</button> -->
<form onsubmit="return doSubmit();">
<input type="text" name="msg" placeholder="你想要发送的消息" value="" size="35"/>
<input type="button" name="button" value="send" />
</form>
</div>
</div>
<script src="http://91vh.com/js/jquery-2.1.4.min.js"></script>
<script type="text/javascript">
function currentTime() {
let timeStamp = (new Date()).valueOf();
return timeStamp
}
function randomNumber(minNum, maxNum) {
switch (arguments.length) {
case 1:
return parseInt(Math.random() * minNum + 1, 10);
break;
case 2:
return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10);
break;
default:
return 0;
break;
}
}
function sendId() {
let timeStamp = currentTime();
let randId = randomNumber(100000, 999999);
let id = timeStamp + "-" + randId;
return id
}
function msg(name, msg) {
let html = '<div class="admin-group">' +
'<div class="admin-img" >' + name + '</div>' +
// '<img class="admin-img" src="http://localhost/public/img/aa.jpg" />'+
'<div class="admin-msg">' +
'<i class="triangle-admin"></i>' +
'<span class="admin-reply">' + msg + '</span>' +
'</div>' +
'</div>';
return html
}
function myMsg(name, msg) {
let html = '<div class="user-group">' +
'<div class="user-msg">' +
'<span class="user-reply">' + msg + '</span>' +
'<i class="triangle-user"></i>' +
'</div>' +
'<div class="user-img" >' + name + '</div>' +
// '<img class="user-img" src="http://localhost/public/img/cc.jpg" />'+
'</div>';
return html
}
function userDiv(name) {
let html = '<div id="' + name + '">' +
name +
'</div>';
return html
}
function addChatWith(msg) {
$(".chat-with").append(msg);
// 页面滚动条置底
$('.chat-with').animate({ scrollTop: document.body.clientHeight + 10000 + 'px' }, 80);
}
function addUserList(name) {
music = "<li id=\"" + name + "\">" + name + "</li>";
$(".personnel-list-ul").append(music);
}
function delUserList(name) {
$("#" + name).remove();
}
// 连接webSocket
ws = new WebSocket("ws://127.0.0.1:8089/acc");
ws.onopen = function(evt) {
console.log("Connection open ...");
// // 连接以后
// person = prompt("请输入你的名字", "hello-" + currentTime());
// if (person != null) {
// console.log("用户准备登陆:" + person);
// ws.send('{"seq":"' + sendId() + '","cmd":"login","data":{"userId":"' + person + '","appId":101}}');
// }
person = randomNumber(10000, 99999)
console.log("用户准备登陆:" + person);
ws.send('{"seq":"' + sendId() + '","cmd":"login","data":{"userId":"' + person + '","appId":101}}');
// 定时心跳
setInterval(heartbeat, 30 * 1000)
};
// 收到消息
ws.onmessage = function(evt) {
console.log("Received Message: " + evt.data);
data_array = JSON.parse(evt.data);
console.log(data_array);
if (data_array.cmd === "msg") {
data = data_array.response.data
addChatWith(msg(data.from, data.msg))
} else if (data_array.cmd === "enter") {
data = data_array.response.data
addChatWith(msg("管理员", "欢迎" + data.from + "加入~"))
addUserList(data.from)
} else if (data_array.cmd === "exit") {
data = data_array.response.data
addChatWith(msg("管理员", data.from + "悄悄的离开了~"))
delUserList(data.from)
}
};
ws.onclose = function(evt) {
console.log("Connection closed.");
};
// 心跳
function heartbeat() {
console.log("定时心跳:" + person);
ws.send('{"seq":"' + sendId() + '","cmd":"heartbeat","data":{}}');
}
// 点击按钮事件
// $("button").click(function() {
$("input[name='button']").click(function() {
sendMsg();
});
// 回车提交
function doSubmit() {
sendMsg();
return false;
}
function sendMsg() {
let msg = $("input[name='msg']").val()
console.log("button 点击:" + msg);
if (msg !== "") {
$.ajax({
type: "POST",
url: 'http://127.0.0.1:8080/user/sendMessageAll',
data: {
appId: "101",
userId: person,
msgId: sendId(),
message: msg,
},
contentType: "application/x-www-form-urlencoded",
success: function(data) {
console.log(data);
addChatWith(myMsg(person, msg))
$("input[name='msg']").val("");
}
});
}
}
setTimeout(function() { getUserList(); }, 500); // 1秒后将会调用执行
function getUserList() {
$.ajax({
type: "GET",
url: "http://127.0.0.1:8080/user/list",
dataType: "json",
success: function(data) {
console.log("user list:" + data.code + "userList:" + data.data.userList);
if (data.code != 200) {
return false
}
var music = "";
//i表示在data中的索引位置n表示包含的信息的对象
$.each(data.data.userList, function(i, n) {
//获取对象中属性为optionsValue的值
let name = n
if (n == person) {
name = name + "(自己)"
}
music += "<li id=\"" + n + "\">" + name + "</li>";
});
$(".personnel-list-ul").append(music);
return false
}
});
}
</script>
</div>
</body>
</html>

File diff suppressed because one or more lines are too long

@ -0,0 +1,60 @@
## 目录
- 1、项目说明
- 1.1 goWebSocket
- 1.2 项目体验
- 2、介绍webSocket
- 2.1 webSocket 是什么
- 2.2 webSocket的兼容性
- 2.3 为什么要用webSocket
- 2.4 webSocket建立过程
- 3、如何实现基于webSocket的长连接系统
- 3.1 使用go实现webSocket服务端
- 3.1.1 启动端口监听
- 3.1.2 升级协议
- 3.1.3 客户端连接的管理
- 3.1.4 注册客户端的socket的写的异步处理程序
- 3.1.5 注册客户端的socket的读的异步处理程序
- 3.1.6 接收客户端数据并处理
- 3.1.7 使用路由的方式处理客户端的请求数据
- 3.1.8 防止内存溢出和Goroutine不回收
- 3.2 使用javaScript实现webSocket客户端
- 3.2.1 启动并注册监听程序
- 3.2.2 发送数据
- 3.3 发送消息
- 3.3.1 文本消息
- 3.3.2 图片和语言消息
- 4、goWebSocket 项目
- 4.1 项目说明
- 4.2 项目依赖
- 4.3 项目启动
- 4.4 接口文档
- 4.4.1 HTTP接口文档
- 4.4.1.1 接口说明
- 4.4.1.2 聊天页面
- 4.4.1.3 获取房间用户列表
- 4.4.1.4 查询用户是否在线
- 4.4.1.5 给用户发送消息
- 4.4.1.6 给全员用户发送消息
- 4.4.2 RPC接口文档
- 4.4.2.1 接口说明
- 4.4.2.2 查询用户是否在线
- 4.4.2.3 发送消息
- 4.4.2.4 给指定房间所有用户发送消息
- 4.4.2.5 获取房间内全部用户
- 5、webSocket项目Nginx配置
- 5.1 为什么要配置Nginx
- 5.2 nginx配置
- 5.3 问题处理
- 6、压测
- 6.1 Linux内核优化
- 6.2 压测准备
- 6.3 压测数据
- 7、如何基于webSocket实现一个分布式Im
- 7.1 说明
- 7.2 架构
- 7.3 分布式系统部署
- 8、回顾和反思
- 8.1 在其它系统应用
- 8.2 需要完善、优化
- 8.3 总结
- 9、参考文献
Loading…
Cancel
Save