IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 使用websocket搭建一个即时通讯工具 -> 正文阅读

[网络协议]使用websocket搭建一个即时通讯工具

使用websocket搭建一个即时通讯工具

使用语言

  • go
  • html+javascript

前端和后端可以跑的通

后端

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="send_btn">发送</button>-->
<button id="logout">退出</button>

<div id="info" class="info">
    <!--    <div>-->
    <!--        <div>Alex:</div>-->
    <!--        <div>hello world</div>-->
    <!--    </div>-->
</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 = () => {
        // 关闭websocket连接
        conn.close()
        conn = ""
        alert("关闭成功")
    }
    const send_data = (data, to_html = false) => {
        // 发送服务
        if (conn === '') alert("websocket没有连接,请检查websocket是否连接成功");
        else {
            conn.send(JSON.stringify(data))
            // 将发送的内容放在html里面
            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) => {
        /*
         id: 元素的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 => {
        // 将要发送的内容写到html里面
        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) {
	/*
		@desc: 把不同的用户加入同一个房间
		@auth: alex
		@data: 10/27/22
		@params conn: 当前用户的连接,
		@params 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) {
	/*
		@desc: 给同一房间的用户发送消息
		@auth: alex
		@data: 10/27/22
		@params msgType 用户传来的消息
	*/
	roomUserSlice := UserRoomDict[msgType.RoomNo]
	fmt.Println("roomUserSlice", roomUserSlice)
	for index := 0; index < len(roomUserSlice); index++ {
		//fmt.Println("room_user_slice", 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) {
	//RoomUserSlice := make([]RoomUser, 0) // 用户房间切片
	//UserConnSlice := make([]UserConn, 0) // 用户连接切片
	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 = () => {
        // 关闭websocket连接
        conn.close()
        conn = ""
        alert("关闭成功")
    }
    const send_data = (data, to_html = false) => {
        // 发送服务
        if (conn === '') alert("websocket没有连接,请检查websocket是否连接成功");
        else {
            conn.send(JSON.stringify(data))
            // 将发送的内容放在html里面
            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) => {
        /*
         id: 元素的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 => {
        // 将要发送的内容写到html里面
        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>

效果

请添加图片描述

如果那里写的不好,希望大家可以提出来啊,谢谢了。

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-10-31 12:34:56  更:2022-10-31 12:35:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 16:20:06-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码