Golang代码搜集-基于websocket+vue.js的简易聊天室
2018-02-24 21:23
746 查看
前言
笔者学完vue.js后,总是不断地找个机会练练手,于是,在假期花了点时间使用websocket和vue.js,写了一个简单的聊天室,功能并不强大,只是实现了简单的群聊功能,但是详细地演示了websocket、chan、vue.js的应用,写在这里算是做记录了,指不定哪一天会用上。预览
提示:邮箱是用户唯一标识源码
main.go
//main.go package main import ( "encoding/json" "fmt" "io" "net/http" "sort" "strings" "time" "golang.org/x/net/websocket" ) const ( ServerID = "lhtzbj12@126.com" ) //LoginReq 登录请求 type LoginReq struct { Type string `json:"type"` //请求的类别 login表示登录 msg表消息 users用户列表 Data UserInfo `json:"data"` //用户信息 } //LoginRespData 登录返回 type LoginRespData struct { Result int `json:"result"` Msg string `json:"msg"` } //MesssageReq 发送消息请求 type MesssageReq struct { Type string `json:"type"` //请求的类别 login表示登录 msg表消息 users用户列表 Data Message `json:"data"` //用户发送的信息 } //TransferData 数据传输结构体 type TransferData struct { Type string `json:"type"` //数据类别 login表示登录 msg表消息 users用户列表 Data interface{} `json:"data"` //数据 } //Message 消息的结构体 type Message struct { From string `json:"from"` //来自谁 email To string `json:"to"` //发给谁 email 空表示表示所有人 Time string `json:"time"` //消息发出的时间 Cont string `json:"cont"` //消息内容 } //UserInfo 用户基本信息结构体 type UserInfo struct { NickName string `json:"nickname"` //昵称 Email string `json:"email"` //用户邮箱 Send2Client chan TransferData `json:"-"` //发给客户端的数据 } //RoomInfo 房间信息 type RoomInfo struct { RoomName string `json:"roomname"` //房间名称 OnlineNum int `json:"onlinenum"` //在线人数 OnlineUsers []UserInfo `json:"onlineusers"` //在线用户列表 ServerID string `json:"serverid"` //服务器标识 } var ( users = make(map[string]UserInfo) //用户列表 entering = make(chan UserInfo) //用户进入 leaving = make(chan UserInfo) //用户离开 transferDatas = make(chan TransferData, 10) //广播消息队列 ) func checkerr(err error) { if err != nil { fmt.Println(err) } } //解析数据包里的type func getType(str string) string { key := "\"type\":" index := strings.Index(str, key) str = str[index+len(key)+1:] index = strings.Index(str, "\"") return str[0:index] } //任务调度 用户进入、离开、消息分发 func taskSchedule() { //广播聊天室信息 bcroominfo := make(chan int, 1) for { select { case transData := <-transferDatas: handleTransData(transData) //有请求需要处理 case u := <-entering: users[u.Email] = u //有新用户进入 //广播聊天室信息 bcroominfo <- 1 case u := <-leaving: delete(users, u.Email) //有用户离开 //广播聊天室信息 bcroominfo <- 1 case <-bcroominfo: var data = TransferData{ Type: "roominfo", Data: RoomInfo{ RoomName: "简易测试聊天室", OnlineNum: len(users), OnlineUsers: usersMap2Slice(users), ServerID: ServerID, }, } transferDatas <- data } } } func handleTransData(data TransferData) { //当为msg时,发给部分用户,否则发给所有人 if data.Type == "msg" { msg := data.Data.(Message) to := strings.TrimSpace(msg.To) if to != "" { //发送给发送者 if u, ok := users[msg.From]; ok { u.Send2Client <- data } //如果接收者不为空,则发送给指定接收者 tos := strings.Split(to, ";") for _, t := range tos { //如果是自己的,则跳过 if t == msg.From { continue } if u, ok := users[t]; ok { //修改to为单个的目标 msg.To = t data.Data = msg u.Send2Client <- data } } } else { //发给所有人 for key := range users { users[key].Send2Client <- data } } } else { for _, v := range users { //发给所有人 v.Send2Client <- data } } } //Index 聊天室页面 func Index(w http.ResponseWriter, req *http.Request) { http.ServeFile(w, req, "index.html") } func main() { fmt.Println("服务启动...") //启用任务调度gorountine go taskSchedule() //聊天室页面 http.HandleFunc("/", Index) //聊天消息WebSocket http.Handle("/chat", websocket.Handler(chatRoom)) if err := http.ListenAndServe(":8080", nil); err != nil { fmt.Println("监听端口失败") } } // websocket请求处理 func chatRoom(ws *websocket.Conn) { defer ws.Close() //新建用户 user := UserInfo{ Send2Client: make(chan TransferData, 10), } go send2client(ws, user) for { var datastr string //收到消息 err := websocket.Message.Receive(ws, &datastr) if err != nil { //客户端断开连接 if err == io.EOF { //用户离开 leaving <- user fmt.Println("client left") break } } //解析数据包里的type dtype := getType(datastr) switch dtype { case "login": //用户登录 var logReq LoginReq err = json.Unmarshal([]byte(datastr), &logReq) if err == nil { //设置用户信息,并将用户存入列表 user.Email = logReq.Data.Email user.NickName = logReq.Data.NickName //有用户进入聊天室 entering <- user //发送欢迎语 sendMessage(user, ServerID, "欢迎进入聊天室") ///发送登录成功 sendLoginResult(user, 1, "登录成功") } case "msg": //收到用户发送消息 var msgReq MesssageReq err = json.Unmarshal([]byte(datastr), &msgReq) if err == nil { msgReq.Data.Time = time.Now().Format("2006-01-02 15:04:05") //将消息存入全局消息队列 var transData = TransferData{ Type: "msg", Data: msgReq.Data, } transferDatas <- transData } } } } //将用户消息chan里数据发送到客户端 func send2client(ws *websocket.Conn, user UserInfo) { for tdata := range user.Send2Client { b, _ := json.Marshal(tdata) websocket.Message.Send(ws, string(b)) } } func sendLoginResult(user UserInfo, result int, msg string) { //返回登录结果 var retunData = TransferData{ Type: "login", Data: LoginRespData{ Result: result, Msg: msg, }, } user.Send2Client <- retunData } //给用户发送消息 func sendMessage(user UserInfo, Form, Cont string) { //返回消息 dataout := TransferData{ Type: "msg", Data: Message{ From: Form, To: user.Email, Time: time.Now().Format("2006-01-02 15:04:05"), Cont: Cont, }, } user.Send2Client <- dataout } //将用户列表从map转成[] func usersMap2Slice(users map[string]UserInfo) []UserInfo { length := len(users) keys := make([]string, 0, length) result := make([]UserInfo, 0, length) for key := range users { keys = append(keys, key) } sort.Strings(keys) for _, key := range keys { result = append(result, users[key]) } return result }
index.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Websocket 与 vue.js测试的程序</title> <script src="https://cdn.bootcss.com/vue/2.5.13/vue.min.js"></script> <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" type="text/css" /> </head> <body> <div class="container"> <div class="row" id="chatroom"> <div class="col-md-6 col-sm-offset-3" v-show="showLoginPanel"> <div class="panel panel-info col-sm-offset-3"> <div class="panel-heading">登录</div> <div class="panel-body"> <form class="form-horizontal"> <div class="form-group"> <label class="col-sm-2 control-label">昵称</label> <div class="col-sm-10"> <input type="input" class="form-control" value="" v-model="curuser.nickname"> </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">邮箱</label> <div class="col-sm-10"> <input type="input" class="form-control" value="" v-model="curuser.email"> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <button type="button" @click="loginFun()" class="btn btn-default">登录</button> </div> </div> </form> </div> </div> </div> <div class="col-md-8" v-show="showChat"> <div class="alert alert-info"> 昵称: {{curuser.nickname}} E-mail: {{curuser.email}} </div> <div class="panel panel-primary"> <div class="panel-heading">{{roomname}} 在线 {{onlinenum}} 人</div> <ul class="list-group" style="height: 400px;overflow: auto;"> <li v-for="msg in messages" class="list-group-item"> <div>{{ convertfromto(msg) }} {{msg.time}} </div> <p>{{msg.cont}}</p> </li> </ul> </div> <div class="panel panel-info"> <div class="panel-heading">发送消息</div> <div class="panel-body"> <form class="form-horizontal"> <div class="form-group"> <label class="col-sm-2 control-label">发送给</label> <div class="col-sm-10"> <input class="form-control" rows="3" v-model="newmessageto" placeholder="为空将发给所有人" readonly> </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">内容</label> <div class="col-sm-10"> <textarea class="form-control" rows="3" v-model="newmessage.cont"></textarea> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <button type="button" @click="sendFun()" class="btn btn-default">发送</button> </div> </div> </form> </div> </div> </div> <div class="col-md-4"> <div class="panel panel-info" v-show="showChat"> <div class="panel-heading">用户列表</div> <table class="table"> <thead> <tr> <th></th> <th>昵称</th> <th>E-mail</th> </tr> </thead> <tbody> <tr v-for="user in onlineusers"> <th><input type="checkbox" value="{{user.email}}" v-on:change="siglechange(user.email)" v-bind:checked="singlechecked(user.email)"></th> <td>{{user.nickname}}</td> <td>{{user.email}}</td> </tr> </tbody> </table> </div> </div> </div> </div> <script> let app = undefined let ws = undefined appinit() wsinit() function appinit() { app = new Vue({ el: '#chatroom', data: { roomname: '简易聊天室', serverid:'', onlinenum: 0, onlineusers: [], islogin: false, messages: [], curuser: { nickname: "我是谁1", email: "lht1@126.com" }, newmessage: { cont: '', to: [], }, }, computed: { showLoginPanel: function () { return !this.islogin }, showChat: function () { return this.islogin }, newmessageto: function () { let to = this.newmessage.to if (to.length == 0) { return "所有人" } else { return to.join(";") } } }, methods: { sendFun: function () { let cont = this.newmessage.cont.trim() if (cont.length == 0) { alert("请输入内容") return } //发送消息 let msg = { type: "msg", data: { from: this.curuser.email, to: this.newmessage.to.join(";"), //多个用分号 cont: cont } } console.log(msg) ws.send(JSON.stringify(msg)); this.newmessage.cont = "" }, loginFun: function () { //登录 let request = { type: "login", data: this.curuser } ws.send(JSON.stringify(request)); }, singlechecked: function (id) { return this.newmessage.to.includes(id) }, siglechange: function (id) { //有则删除,没有则添加 let i = this.newmessage.to.indexOf(id) if (i > -1) { this.newmessage.to.splice(i, 1) } else { this.newmessage.to.push(id) } console.log(this.newmessage.to) }, convertfromto: function (msg) { let from, to if (msg.from === this.curuser.email) { from = "我" } else if (msg.from === this.serverid) { from = "服务器" } else { from = this.getuserinfo(msg.from) } if (msg.to.length === 0) { to = "所有人" } else { if (msg.to === this.curuser.email) { to = "我" } else { to = this.getuserinfo(msg.to) } } return `${from} to ${to}` }, getuserinfo: function (emails) { let es= emails.split(';') let length = es.length,names=[],count = 0 //最多显示三个人名 for(let i=0;i<length && count <4;i++){ let u= this.onlineusers.filter((x)=>{ return x.email == es[i] }) if(u.length>0){ count +=1 names.push(u[0].nickname) } } let ret = names.join(';') if(count > 3){ ret += '等' + length + '人' } return ret } } }) } function wsinit() { ws = new WebSocket("ws://localhost:8080/chat") try { ws.onopen = function () { alert("成功连接至服务器") } ws.onclose = function () { if (ws) { ws.close(); ws = null; } alert("连接服务器-关闭1") } ws.onmessage = function (ret) { console.log(ret.data); handleMessage(ret.data) } ws.onerror = function () { if (ws) { ws.close() ws = null } alert("连接服务器-关闭2") } } catch (e) { alert(e.message) } } //从服务器获取消息进行处理 function handleMessage(d) { data = JSON.parse(d) if (data.type === "msg") { //消息 app.messages.push(data.data) } else if (data.type === "roominfo") { //房间信息 app.roomname = data.data.roomname app.onlinenum = data.data.onlinenum app.onlineusers = data.data.onlineusers app.serverid = data.data.serverid } else if (data.type == "login") { //登录结果 if (data.data.result === 1) { app.islogin = true alert("登录成功") } else { alert("登录失败") } } } </script> </body> </html>
相关文章推荐
- 一步一步教您用websocket+nodeJS搭建简易聊天室(4)
- 一步一步教您用websocket+nodeJS搭建简易聊天室(1)
- 一步一步教您用websocket+nodeJS搭建简易聊天室(2)
- vue + socket.io实现一个简易聊天室示例代码
- 基于Node.js+socket.IO创建的Web聊天室
- 一步一步教您用websocket+nodeJS搭建简易聊天室(3)
- 基于webpack和vue.js搭建的H5端框架
- 基于vue.js 2.0,不使用webpack的nodejs服务,只在浏览器上单独使用在Element UI的Tree树形控件
- vue.js环境搭建(基于webpack和vue.js)
- 基于webpack和vue.js搭建开发环境
- nodejs +webSocket 多人聊天室
- Vue与Node.js通过socket.io通信的示例代码
- 基于Node.js,Express,Socket.io创建简单聊天室
- [js高手之路]Vue2.0基于vue-cli+webpack父子组件通信教程
- 详解基于webpack和vue.js搭建开发环境
- [js高手之路]Vue2.0基于vue-cli+webpack父子组件通信教程
- 基于webpack和vue.js搭建开发环境
- golang 2行代码在基于arm linux的树莓派、orangepi上运行http web服务
- vue + socket.io实现一个简易聊天室
- 基于java webRct webSocket 实现点对点视频 (需要代码的朋友) 扫码加我微信