1. 程式人生 > >Golang程式碼蒐集-基於websocket+vue.js的簡易聊天室

Golang程式碼蒐集-基於websocket+vue.js的簡易聊天室

前言

筆者學完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 = "[email protected]" ) //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}} &nbsp;&nbsp; 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) }} &nbsp;&nbsp;{{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: "[email protected]"
                    },
                    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>

相關推薦

Golang程式碼蒐集-基於websocket+vue.js簡易聊天

前言 筆者學完vue.js後,總是不斷地找個機會練練手,於是,在假期花了點時間使用websocket和vue.js,寫了一個簡單的聊天室,功能並不強大,只是實現了簡單的群聊功能,但是詳細地演示了websocket、chan、vue.js的應用,寫在這裡算是做記

Golang程式碼蒐集-基於RSA的公鑰加密私鑰解密-私鑰簽名公鑰驗證

首先由genkey.go生成公鑰和私檔案,在rsa.go裡使用生成的公鑰和私鑰進行加密和解密 //檔案 genkey.go //生成公鑰和私鑰 pem檔案 package main import ( "crypto/rand" "cry

Netty+Websocket 實現一個簡易聊天

後臺程式碼 /** * 服務端 */ public class ChatServer { public static void main(String[] args) throws Exception { int port=8080; //服務端預設埠 new Ch

基於Android ServerSocket的簡易聊天功能

上篇部落格介紹了Socket Socket的基本講解以及對於內部的方法使用做了一些簡單的整理,並且通過ServerSocket自己做了一個通過服務端(PC主機)與多臺手機進行通訊的Demo,實現了群發功能、指定手機發送訊息功能、顯示已連線的手機數量以及IP地址和埠號,通過這

vue學習筆記第一天-vue.js簡易留言板

fad ima con targe right 彈出框 n) ade ack <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> &l

# 程式碼約架?Vue.js和Binding.scala兩大框架作者的PK

作為一個知乎小透明,最近看了一場炸魚大片。兩天前,民工叔因為 Teambition 是 React 技術棧而離職 一文,引發了激烈的討論。其中民工叔偏向的技術選型Vue.js的作者出沒現場黑了一把Angular2,其它各吃瓜群眾表示還沒反應過來該如何站隊之時,B

SpringBoot整合WebSocket基於STOMP協議】進行點對點[一對一]和廣播[一對多]實時推送,內附簡易聊天demo

最近專案來了新需求,需要做一個實時推送的功能,伺服器主動推送訊息給客戶端,在網上經過一輪搜查之後,確定使用WebSocket來進行開發。以前經常聽說WebSocket的神奇之處,如今終於可以嘗試使用它了。1.淺談WebSocketWebSocket是在HTML5基礎上單個TC

Vue.js 簡易頂部導航欄

<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <title>title</title>    </head&g

Vue.js 簡易漸變輪播圖

直接將樣式和Vue屬性方法複製到自己的頁面即可使用,注意可能會發生的衝突。<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <title&

基於websocket簡易聊天的實現

用java實現基於websocket的簡易聊天室    WebSocket protocol 是HTML5一種新的協議。它實現了瀏覽器與伺服器全雙工通訊(full-duplex)。一開始的握手需要藉助HTTP請求完成。                          

php基於websocket搭建簡易聊天(socket)

前言 http連線分為短連線和長連線。短連線一般可以用ajax實現,長連線就是websocket。短連線實現起來比較簡單,但是太過於消耗資源。websocket高效不過相容存在點問題。websocket是html5的資源 前端 //連線socket

搭建Websocket簡易聊天

eui imp str %s 管理 關閉連接 string self stat 本文,我們通過Egret和Node.js實現一個在線聊天室的demo。主要包括:聊天,改用戶名,查看其他用戶在線狀態的功能。大致流程為,用戶訪問網頁,即進入聊天狀態,成為新遊客,通過底部的輸入框

NetCore MVC WebSocket 簡易聊天

前端程式碼:  function SocketHelper(params) { var options = $.extend({}, { uri: "ws://" + window.location.host + "//socket/Connect",

Unity 簡易聊天基於TCP)(1)

為了準備畢業設計,學習了伺服器與客戶端之間傳輸的一些簡單的知識,並跟著網路上的教程製作了一個簡易的Unity聊天室 伺服器:用C# .Net Framework寫的 結構分為:main(主函式)、Server類(用於伺服器的開啟和接收客戶端連線)、Client類(接收訊息和傳送訊息)、Message類(用

NetCore MVC WebSocket 簡易聊天

前端程式碼:  function SocketHelper(params) { var options = $.extend({}, { uri: "ws://" + window.location.host + "//socket/Connect",

基於epoll實現的c++聊天(全程式碼

早些時候為了更加熟悉網路程式設計,所以寫了一個聊天程式練練手,但那是純linux終端實現的,沒有介面,最近心血來潮翻出來加了個Qt的簡單介面,成了一個簡易的區域網聊天室,通過tcp伺服器來轉發訊息,其實最初實現的時候無介面情況下已經實現了單聊群聊檔案傳輸,udp檔案傳輸還實現

基於flask框架,使用websocket實現多人聊天功能

後端程式碼:   # web_socket 的收發機制 # web_socket --> web + socket --> http協議 + socket # web_socket協議就是ws協議 # 基於flask框架為web_socket提供服務 from flas

Node.js+websocket+mongodb實現即時聊天

高並發 集合 ejs 部署 思路 平臺 fff tro 賬號 ChatRoom Node.js+websocket+mongodb實現即時聊天室 A,nodejs簡介:Node.js是一個可以讓javascript運行在服務器端的平臺,它可以讓javascript脫離瀏覽器

基於Linux c 用socket和執行緒 實現的簡易聊天之伺服器

#include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #

實現簡易聊天(一)

ima log body .com 麻煩 導入 定義 右鍵 正常 預備工作: (1)讀取文件的時候可能會遇到多個文件一起傳,可以用線程池。 (2)發送不同類型的請求時,如發送的是聊天信息,發送的是文件,發送的是好友上線請求等,但對於接受者來說都是字節流無法分別,這就需要我們