多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
> ### 第四例 聊天室 * 客户端 ~~~ <!DOCTYPE HTML> <html > <head > <meta charset="utf-8" > <script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js" ></script > </head > <body > <form > <div id="div1" ></div > <hr > <textarea style="width: 300px;height: 100px;" id="content" ></textarea > <input type="button" value="发送" onclick="Send()" > </form > </body > <script > function GetQueryString(name) { var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); var r = window.location.search.substr(1).match(reg); if (r != null) return unescape(r[2]); return null; } //?roomId=1&userId=1000&userName=winnie&connType=1 var roomId = GetQueryString("roomId"); var userId = GetQueryString("userId"); var userName = GetQueryString("userName"); var connType = GetQueryString("connType"); var wsCopy; //服务端地址 //var wsUrl = "ws://127.0.0.1:8095/message?roomId=22&userId=222&userName=王五&connType=2"; var wsUrl = "ws://127.0.0.1:8095/message?roomId=" + roomId + "&userId=" + userId + "&userName=" + userName + "&connType=" + connType; //心跳检测 var heartTimeOut = 10 * 1000; //10s (1000 毫秒= 1 秒) - 设置时间比nginx超时时间短) //断线重连 var reConnTimeOut = 2 * 1000; //心跳检测 var heartCheck = { timer: null, timeout: heartTimeOut, start: function (ws) { this.timer = setInterval(function () { console.log("heartCheck"); ws.send("ping www.51websocket.cn"); }, this.timeout); } }; //断线重连 var reConnection = { timer: null, timeout: reConnTimeOut, url: wsUrl, start: function (ws) { if (!ws.token) { this.timer = setTimeout(function () { console.log("ReConnect"); createWebSocket(this.url); }, this.timeout); } } }; //创建连接 createWebSocket(wsUrl); function createWebSocket() { try { var ws = new WebSocket(wsUrl); init(ws); } catch (e) { console.log('catch'); reConnection.start(); } } function init(ws) { //TODO : 连接关闭时触发 ws.onclose = function () { clearInterval(heartCheck.timer); if (this.token == undefined) { //多种错误事件, 只会触发一个断线重连 this.token = false; } else if (this.token == false) { this.token = true; } console.log('Close'); reConnection.start(this); }; //通信发生错误时触发 ws.onerror = function () { clearInterval(heartCheck.timer); if (this.token == undefined) { this.token = false; } else if (this.token == false) { this.token = true; } console.log('Error'); reConnection.start(this); }; //连接建立时触发 ws.onopen = function () { clearTimeout(reConnection.timer); console.log('Connect'); //心跳检测重置 heartCheck.start(this); wsCopy = ws; }; //客户端接收服务端数据时触发 ws.onmessage = function (event) { $("#div1").append("<h3>" + event.data + "</h3>"); } } //向服务端发送消息 function Send() { wsCopy.send($("#content").val()); $("#content").val(""); } </script > </html > ~~~ * 服务端 ~~~ package main //如果是服务挂了, 重启的话, 重数据库里读取聊天信息 import ( "chatbox/zdbmodel" "chatbox/zlogs" "golang.org/x/net/websocket" "net/http" "strconv" "fmt" "time" "strings" ) //直播间信息 type LiveRoom struct { user map[int]*UserInfo //直播间用户 ch chan []byte //发送弹幕 msg [][]byte //历史弹幕 } //用户信息 type UserInfo struct { id int //用户Id name string //用户名称 conn *websocket.Conn //当前用户的连接 connType int //用户类型 1-游客 2-会员 } //公共函数 func commAtoi(s string) (int) { if n, err := strconv.Atoi(s); err == nil { return n } return 0 } var list map[int]*LiveRoom var roomUser map[int]*UserInfo var roomCh chan []byte var roomMsg [][]byte func svrConnHandler(conn *websocket.Conn) { //TODO : 表单数据处理 r := conn.Request() r.ParseForm() roomId := r.Form["roomId"][0] userId := r.Form["userId"][0] userName := r.Form["userName"][0] connType := r.Form["connType"][0] rId := commAtoi(roomId) uId := commAtoi(userId) cType := commAtoi(connType) //TODO : 构建结构体 uInfo := &UserInfo{ id: uId, name: userName, conn: conn, connType: cType, } _, ok := list[rId] if !ok { roomUser = make(map[int]*UserInfo, 100) roomCh = make(chan []byte, 100) roomMsg = make([][]byte, 100) } roomUser[uId] = uInfo list[rId] = &LiveRoom{ user: roomUser, ch: roomCh, msg: roomMsg, } //TODO : 接收客户端消息 go func() { var content [1024]byte for { connUserInfo := list[rId].user[uId] n, err := connUserInfo.conn.Read(content[:]) if err != nil { list[rId].user[uId].conn.Close() delete(list[rId].user, uId) break } if n > 0 { if string(content[:n]) == string("ping www.51websocket.cn") { content = [1024]byte{} continue } content2 := append([]byte(fmt.Sprint(userId, ":::", userName, ":::")), content[:n]...) list[rId].ch <- content2 content = [1024]byte{} } } }() //TODO : 向客户端发送消息 if !ok { for { select { case content := <-list[rId].ch: message := strings.Split(string(content), ":::") list[rId].msg = append(list[rId].msg, []byte("<span class='name'>"+message[1]+":"+"</span>"+message[2])) for _, v := range list[rId].user { v.conn.Write([]byte("<span class='name'>" + message[1] + ":" + "</span>" + message[2])) } } } //<--向客户端发送消息--> } else { for { time.Sleep(time.Second * 60 * 60) } } } func main() { list = make(map[int]*LiveRoom, 10) zlogs.InitLog() zdbmodel.InitDb() http.Handle("/message", websocket.Handler(svrConnHandler)) err := http.ListenAndServe(":8095", nil) zlogs.ErrorLog(err) } ~~~