使用websocket搭建一个即时通讯工具
使用语言
前端和后端可以跑的通
后端
package main
import (
"encoding/json"
"flag"
"fmt"
"github.com/gorilla/websocket"
"log"
"net/http"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
return true
},
}
type MsgType struct {
Username string `json:"username"`
Value string `json:"value"`
}
var address = flag.String("addr", "localhost:8080", "http service address")
func handler(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println("aaa", err)
return
}
go func() {
for {
messageType, message, err := conn.ReadMessage()
jsonMsg := MsgType{}
fmt.Println("messageType", messageType)
_ = json.Unmarshal(message, &jsonMsg)
sendMsg := MsgType{Username: "alex xu", Value: "hello world"}
conn.WriteJSON(sendMsg)
if err != nil {
return
}
}
}()
}
func main() {
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe(*address, nil))
}
前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.info > div {
display: flex;
}
.info > div > div {
margin-right: 20px;
}
</style>
</head>
<body>
<input id="username" placeholder="请输入用户名">
<button id="login_btn">登陆</button>
<button id="logout">退出</button>
<div id="info" class="info">
</div>
<script>
const conn_websocket = () => {
conn = new WebSocket("ws://localhost:8080/echo")
conn.onerror = (event, err) => {
console.log('error', event)
conn = ""
}
conn.onopen = () => {
console.log('连接成功')
send_data({username: username_input.value, value: ""});
}
conn.onmessage = event => {
add_msg_to_html(JSON.parse(event.data))
}
conn.onclose = (event) => {
conn = ""
console.log("关闭了", event)
}
}
const close_websocket = () => {
conn.close()
conn = ""
alert("关闭成功")
}
const send_data = (data, to_html = false) => {
if (conn === '') alert("websocket没有连接,请检查websocket是否连接成功");
else {
conn.send(JSON.stringify(data))
if (to_html) add_msg_to_html(data)
}
}
const create_input_el = (id, placeholder) => {
let input = document.querySelector(`#${id}`);
if (!input) {
input = document.createElement("input");
input.setAttribute("id", id);
input.setAttribute("placeholder", placeholder)
}
return input
}
const create_btn_el = (id, content, click_call_func) => {
let btn_el = document.querySelector(`#${id}`)
if (!btn_el) {
btn_el = document.createElement("button");
btn_el.innerText = content
btn_el.onclick = click_call_func
}
return btn_el
};
const send_btn_handler = () => {
const send_input = document.querySelector("#send_data");
send_data({username: username_input.value, value: send_input.value}, true)
}
const add_msg_to_html = data => {
const div = document.createElement("div");
div.innerHTML = `
<div>${data.username}:</div>
<div>${data.value}</div>
`;
info.appendChild(div);
}
</script>
<script>
let conn = "";
const login_btn = document.querySelector("#login_btn");
const logout = document.querySelector("#logout");
const send_btn = document.querySelector("#send_btn");
const username_input = document.querySelector("#username");
const body = document.querySelector("body");
const info = document.querySelector("#info")
login_btn.onclick = async () => {
await conn_websocket()
body.appendChild(create_input_el("send_data", "请输入要发送的内容"));
body.appendChild(create_btn_el("send_btn", "发送", send_btn_handler));
}
logout.oncclick = close_websocket;
</script>
</body>
</html>
效果图
后端搭建开始搭建聊天室,让用户可以在一个聊天室里面聊天
在上面我们已经实现了发送和消息,这一步我们就要创建一个聊天室了,让他们可以在同一个房间内聊天
后端
package main
import (
"encoding/json"
"flag"
"fmt"
"github.com/gorilla/websocket"
"log"
"net/http"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
return true
},
}
type UserConn struct {
RoomNo string
userName string
conn *websocket.Conn
}
type MsgType struct {
Username string `json:"username"`
Value string `json:"value"`
RoomNo string `json:"room_no"`
}
var address = flag.String("addr", "localhost:8080", "http service address")
var UserRoomDict = make(map[string][]UserConn)
func joinRoom(conn *websocket.Conn, msgType MsgType) {
userConnSlice := make([]UserConn, 0)
userConnStruct := UserConn{userName: msgType.Username, conn: conn, RoomNo: msgType.RoomNo}
userConnSlice = append(userConnSlice, userConnStruct)
if UserRoomDict[msgType.RoomNo] != nil {
userConn := UserRoomDict[msgType.RoomNo]
exist := true
for index := 0; index < len(userConn); index++ {
if userConn[index].userName == msgType.Username {
exist = false
}
}
if exist {
UserRoomDict[msgType.RoomNo] = append(userConn, userConnStruct)
}
} else {
roomUserSlice := make([]UserConn, 0)
roomUserSlice = append(roomUserSlice, userConnStruct)
UserRoomDict[msgType.RoomNo] = roomUserSlice
}
}
func sendMsg(msgType MsgType) {
roomUserSlice := UserRoomDict[msgType.RoomNo]
fmt.Println("roomUserSlice", roomUserSlice)
for index := 0; index < len(roomUserSlice); index++ {
if msgType.Value != "" {
fmt.Println("room_user_slice", roomUserSlice[index])
userInfo := roomUserSlice[index]
if userInfo.userName != msgType.Username {
userInfo.conn.WriteJSON(MsgType{Username: msgType.Username, Value: msgType.Value})
}
}
}
}
func handler(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println("aaa", err)
return
}
messageChan := make(chan MsgType)
go func(messageChan chan MsgType) {
for {
_, message, err := conn.ReadMessage()
jsonMsg := MsgType{}
_ = json.Unmarshal(message, &jsonMsg)
messageChan <- jsonMsg
if err != nil {
return
}
}
}(messageChan)
go func(messageChan chan MsgType) {
for {
select {
case message := <-messageChan:
fmt.Println("message", message)
if message.Value == "" {
joinRoom(conn, message)
} else {
sendMsg(message)
}
}
}
}(messageChan)
}
func main() {
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe(*address, nil))
}
前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.info > div {
display: flex;
}
.info > div > div {
margin-right: 20px;
}
</style>
</head>
<body>
<label for="username"></label><input id="username" placeholder="请输入用户名">
<label for="room_no"></label> <input id="room_no" placeholder="请输入要加入的房间">
<button id="login_btn">登陆</button>
<button id="logout">退出</button>
<div id="info" class="info">
</div>
<script>
const conn_websocket = () => {
conn = new WebSocket("ws://localhost:8080/echo")
conn.onerror = (event, err) => {
console.log('error', event)
conn = ""
}
conn.onopen = () => {
console.log('连接成功')
send_data({username: username_input.value, value: "", room_no: room_no_input.value});
}
conn.onmessage = event => {
add_msg_to_html(JSON.parse(event.data))
}
conn.onclose = (event) => {
conn = ""
console.log("关闭了", event)
}
}
const close_websocket = () => {
conn.close()
conn = ""
alert("关闭成功")
}
const send_data = (data, to_html = false) => {
if (conn === '') alert("websocket没有连接,请检查websocket是否连接成功");
else {
conn.send(JSON.stringify(data))
if (to_html) add_msg_to_html(data)
}
}
const create_input_el = (id, placeholder) => {
let input = document.querySelector(`#${id}`);
if (!input) {
input = document.createElement("input");
input.setAttribute("id", id);
input.setAttribute("placeholder", placeholder)
}
return input
}
const create_btn_el = (id, content, click_call_func) => {
let btn_el = document.querySelector(`#${id}`)
if (!btn_el) {
btn_el = document.createElement("button");
btn_el.innerText = content
btn_el.onclick = click_call_func
}
return btn_el
};
const send_btn_handler = () => {
const send_input = document.querySelector("#send_data");
send_data({username: username_input.value, value: send_input.value, room_no: room_no_input.value}, true)
}
const add_msg_to_html = data => {
const div = document.createElement("div");
div.innerHTML = `
<div>${data.username}:</div>
<div>${data.value}</div>
`;
info.appendChild(div);
}
</script>
<script lang="ts">
let conn = "";
const login_btn = document.querySelector("#login_btn");
const logout = document.querySelector("#logout");
const send_btn = document.querySelector("#send_btn");
const username_input = document.querySelector("#username");
const room_no_input = document.querySelector("#room_no");
const body = document.querySelector("body");
const info = document.querySelector("#info")
login_btn.onclick = async () => {
if (room_no_input.value === "" || username_input.value === "") alert("请先输入用户名和选择房间");
else {
await conn_websocket()
body.appendChild(create_input_el("send_data", "请输入要发送的内容"));
body.appendChild(create_btn_el("send_btn", "发送", send_btn_handler));
}
}
logout.oncclick = close_websocket;
</script>
</body>
</html>
效果
如果那里写的不好,希望大家可以提出来啊,谢谢了。
|