使用Go+WebSocket练习总结

本文记录了使用Golang和Websocket开发网页对战游戏过程中遇到的问题与解决方案,包括开发环境搭建、文件结构设计、界面展示及解决共享数据问题等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文章仅记录使用 Golang + Websocket 开发网页对战游戏项目时遇到的问题及解决方案。
项目地址: WebGame2
该项目目前未使用任何 GL 相关API,只是使用最简单的HTML+CSS技术完成。

开发环境

后端

前端

界面简单展示

五子棋游戏
页面切换没有记录在内,可以通过左上角登录信息观察变化。测试时已经在另一个页面登录第二个账号。

文件结构

目录结构
主要结构说明

+---- [ProjectPath]
	| ---- bin				// 打包可执行程序目录
		| ---- WebGame.exe
	| ---- db 				// 程序数据目录
		| ---- [yyyyMD] 		// 日志文件
		| ---- upload  			// 上传目录
		| ---- WebGame.db		// sqlite3 日志文件
	| ---- src				// go源文件目录
		| ---- task				// 消息推送包, 配合 WsService.sendTaskData() 使用
		| ---- games			// 游戏开发包
			| ---- wzq				// 五子棋游戏包
		| ---- ws				// websocket处理包
		| ---- main				// mainbao, 程序入口包
	|  ---- ui 				// 前端代码(angular 8)目录
	| ---- WebGame.ini 		// 配置文件

踩坑&填坑

每个连接对象保存的数据被共享了?

连接监听, http.Handle() 处理的每个 websocket.Conn 都在新协程中处理

// WsService.go
http.Handle(conf.Cfgr.Ws.Url, websocket.Handler(func(conn *websocket.Conn) {
    ch := make(chan int)
    go handleConn(conn, ch)
    <-ch
}))

原来的代码

// WsService.go
var WsClient = &wsClient{}

type wsClient struct {
  // ...
  conn *websocket.Conn
  id 	string // 每个连接唯一标识
}


http.Handle(conf.Cfgr.Ws.Url, websocket.Handler(func(conn *websocket.Conn) {
    go handleConn(conn)
}))

func handleConn(conn *websocket.Conn) {
    id, _ := uuid.NewV4() // 为每个客户端产生唯一标识
	WsClient.conn = conn
	WsClient.id = string(id)
	...
}

测试结果发现 WsClient 是一个共享的 *wsClient,后来又测试不用指针创建 var WsClient = wsClient{}任然出现相同的问题:WsClient.id只能保存最后一个值,即使是子业务对象也是一样。
实验结果表明:协程并不会重复执行&wsClient{}wsClient{}new(wsClient{})这样的代码,需要手动完成创建对象的动作:

// WsService.go
const (
    BOX_SIZE       = 9 // 格子大小
    WIN_HITS_COUNT = 5 // 胜利条件: 连线个数
)

func handleConn(conn *websocket.Conn, ch chan int) {
    ws := &wsClient{} // 这里必须使用新的对象
	// ...
    ws.gameWzq = new(wzq.GameWzq) // 每个子游戏服务必须使用各自的对象(除非该服务没有保存状态)
	// ...
}

同时由于这个特性,正好可以用在游戏房间管理上:

// games/wzq/roomMgr.go
var RoomMgr = &mgr{
    games: make(map[int]*gameA),
}

// 管理类
type mgr struct {
    mu    sync.Mutex
    games map[int]*gameA // 所有五子棋游戏
}

// 五子棋游戏
type gameA struct {
    rooms map[int]*room // 所有房间
}

// 游戏房间
type room struct {
    No        int           `json:"no"`
    P1        model.Account `json:"p1"`
    P2        model.Account `json:"p2"`
    playingId int           // 可落子玩家
    stones    [BOX_SIZE][BOX_SIZE]int
}

至此问题得到解决。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值