一般做为一个聊天服务,经常有需求第三方发送消息的场景,如果能够提供一个http的api的写消息服务会方便很多,现在我们来简单实现下。
package main
/*
* socket服务+http服务
*/
import (
"bufio"
"encoding/json"
"fmt"
"math/rand"
"net"
"net/http"
"strings"
"sync"
"time"
"github.com/gin-gonic/gin"
)
//保存连接
var connList sync.Map
//消息结构{"from":"aaa","to":"bbb","body":"aaa","cmd":"sendMsg"}
type UserMsg struct {
From string `json:"from"`
To string `json:"to"`
Body string `json:"body"`
Cmd string `json:"cmd"`
}
//随机字符串
func randName() string {
var letterRunes = []rune("123456789")
rand.Seed(time.Now().UnixNano())
b := make([]rune, 6)
for i := range b {
b[i] = letterRunes[rand.Intn(len(letterRunes))]
}
return string(b)
}
func DealConn(conn net.Conn) {
// 处理完关闭连接
defer conn.Close()
fmt.Println("new conn\n")
name := randName()
connList.Store(name, conn)
conn.Write([]byte("you is : " + name + "\r\n"))
// 针对当前连接做发送和接受操作
for {
reader := bufio.NewReader(conn)
// 读取字符串, 直到碰到回车返回
str, err := reader.ReadString('\n')
// 数据读取正确
if err == nil {
// 去掉字符串尾部的回车
str = strings.TrimSpace(str)
var strStruct UserMsg
err := json.Unmarshal([]byte(str), &strStruct)
if err != nil {
fmt.Printf("msg json error, err:%v\n", err)
} else {
to := strStruct.To
cmd := strStruct.Cmd
if cmd == "sendMsg" {
toUser, ok := connList.Load(to)
if !ok {
fmt.Println("to user is not\n")
} else {
toUserConn, ok := toUser.(net.Conn)
if !ok {
fmt.Println("to user type wrong\n")
} else {
toUserConn.Write([]byte(strStruct.From + " say : " + strStruct.Body + "\r\n"))
}
}
} else {
fmt.Println("unkonw msg")
}
}
} else {
//删除保存的连接
connList.Delete(name)
fmt.Println("conn close\n")
break
}
}
}
func socketServer() {
// 建立tcp 服务
listen, err := net.Listen("tcp", "0.0.0.0:1215")
if err != nil {
fmt.Printf("listen failed, err:%v\n", err)
return
}
for {
// 等待客户端建立连接
fmt.Printf("accept conn ...... \n")
conn, err := listen.Accept()
if err != nil {
fmt.Printf("accept failed, err:%v\n", err)
continue
}
// 启动一个单独的 goroutine 去处理连接
go DealConn(conn)
}
}
func main() {
//socket服务
go socketServer()
r := gin.Default()
r.GET("/sendOne", func(c *gin.Context) {
to := c.DefaultQuery("to", "")
msg := c.DefaultQuery("msg", "")
toUser, ok := connList.Load(to)
if !ok {
c.String(http.StatusOK, "error1")
} else {
toUserConn, ok := toUser.(net.Conn)
if !ok {
c.String(http.StatusOK, "error2")
} else {
toUserConn.Write([]byte(msg + "\r\n"))
c.String(http.StatusOK, "ok")
}
}
})
r.GET("/sendAll", func(c *gin.Context) {
msg := c.DefaultQuery("msg", "")
//广播消息
connList.Range(func(k, userConn interface{}) bool {
userConn.(net.Conn).Write([]byte(msg + "\r\n"))
return true
})
c.String(http.StatusOK, "ok")
})
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
这样我们就简单实现了一个可以通过http的api来发消息的服务,是不是感觉很方便呢?
