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}} 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: "[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)發送不同類型的請求時,如發送的是聊天信息,發送的是文件,發送的是好友上線請求等,但對於接受者來說都是字節流無法分別,這就需要我們