从0开始的go+websocket构建五子棋对战系统
基本框架
直接照搬,不多解释。
dao为数据库处理层
po为实体类
middleware为中间件,cors处理跨域
app对request请求进行封装
router处理路由
service为服务层,进行数据处理逻辑

网络框架
使用gin作为整体的网络框架,文档在这里https://github.com/gin-gonic/gin
//main.go
func main() {
	engine := gin.Default()
	dao.Setup()
	service.Setup()
	router.Setup(engine)
	err := engine.Run(fmt.Sprintf(":%v", "5521"))
	if err != nil {
		panic(err)
	}
	//user := po.User{1, "wxy", "2020"}
	//service.Test(&user)
}
gin.default用来生成一个处理网络请求的实体。交由router进行处理
//router
func Setup(engine *gin.Engine) {
	//处理cors
	engine.Use(middleware.Cors())
	//静态文件
	//engine.Static("")
	user := engine.Group("/user")
	{
		hub := service.ExUserService
		user.POST("/test", app.HandlerFunc(hub.Test))
	}
}
捕获路由并交给对应的函数(hub.test)处理。
数据持久化
使用gorm进行数据库管理。在dao中实现对应函数。例如存储一个用户信息方法实现如下
//dao/user.go
type UserDao struct {
	Tx *gorm.DB
}
func (u UserDao) SaveUser(user *po.User) {
	err := u.Tx.Save(user).Error
	if err != nil {
		panic(err)
	}
}
在go中,会默认在将对象保存到类名(严格的讲这么说是不准确的,因为go并非面向对象语言)加s。如果一个user类的实体对象,保存后就会存储到users表中。
数据库的连接
	db, err := 		gorm.Open(mysql.Open(fmt.Sprintf("%v:%v@tcp(%v:%v)/%v?charset=utf8mb4&parseTime=True&loc=Local",
		"username", "password", "1.1.1.1", 3306, "dbname")), &gorm.Config{})
其中username,password分别填数据库的用户名密码。1.1.1.1替换成数据库ip,3306为端口,dbname是库名。
websocket架构
使用melody处理websocket请求。melody是基于github.com/gorilla/websocket 抽象出的websocket处理框架。可以直接使用。
文档在这里https://pkg.go.dev/gopkg.in/olahol/melody.v1#section-readme
下载该依赖需要换源,方法问谷歌。
实例化一个melody对象,然后为其设置收到msg时候的处理方法。最后在路由中指向该对象即可由其接收websocket请求。
//websocket.go
func InitMelody() *melody.Melody {
	m = melody.New()
	m.HandleMessage(Receive)
	return m
}
func Receive(s *melody.Session, msg []byte) {
	m.Broadcast(msg)
}
//router.go
	engine.GET("/ws", func(c *gin.Context) {
		m.HandleRequest(c.Writer, c.Request)
	})
上面的函数简单的进行了一个消息的复读。测试一下
 
然后测试一下由服务器主动发送数据。
 
加上对于用户连接和用户断开的处理逻辑
//websocket.go
func InitMelody() *melody.Melody {
	m = melody.New()
	m.HandleMessage(Receive)
	m.HandleConnect(Connect)
	m.HandleDisconnect(DisConnect)
	return m
}
func Connect(s *melody.Session) {
	id := uuid.NewV4().String()
	idMap.Store(id, s)
	s.Set("Id", id)
	if !service.ExPlayerService.Connect(id, "unNamed") {
		log.Print("此id已连接")
	} else {
		log.Print(id, "连接成功")
	}
}
func DisConnect(s *melody.Session) {
	id, ok := s.Get("Id")
	fmt.Println(id, ok)
	if ok == false {
		return
	}
	service.ExPlayerService.DisConnect(id.(string))
	idMap.LoadAndDelete(id)
	fmt.Println(id, "断开连接")
}
用idMap保存从id到s的映射。方便此后根据id进行消息的传送
然后在service层加上对应player的处理逻辑
//service.player
func (p PlayerService) Connect(id string, name string) bool {
	_, ok := maPlayer[id]
	if ok {
		return false
	}
	player := po.Player{
		Id:    id,
		Name:  name,
		Color: -1,
	}
	maPlayer[id] = player
	return true
}
func (p PlayerService) DisConnect(id string) bool {
	_, ok := maPlayer[id]
	if !ok {
		return false
	}
	delete(maPlayer, id)
	return true
}
持续更新中...

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号