栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Go语言

go语言实现聊天室(基础版)

Go语言 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

go语言实现聊天室(基础版)

介绍:
博主前几周学习了go语言的一些基础语法。于本周写出了这个小案列。在参考了

中文文档上的实例的同时,加入了与mysql数据库的结合。如果不想实现入库这部分功

能,可以自行略过。
一、分析:

想要实现聊天室,客户端和服务端必不可少。

1.客户端:

这里大家可以自己动手去写一个自己的客户端界面。也就是用户界面。博主这里写的比较随意。

而这部分重点在于与后端的交互:即发送请求与接收后端返回的数据。

后面我们根据代码再来详细介绍这部分。

2.服务端:

我这里是用go来实现的。大家也可以选择其他语言来实现。

这部分重点在于处理用户发来的数据。

2.效果展示:

3.代码实现: 大家一定要看注释,里面是各部分代码功能的具体描述!!。 1.客户端: 1.1 首先使用webscoket建立连接:
// 注意后面的chating,后端路由在调用的时候,和这个名字一致
var ws = new WebSocket("ws://127.0.0.1:8081/chating");
1.2 设置连接成功和连接失败出现的提示效果:
    // 建立失败:
    ws.onerror = function(){
        var data = "系统消息:建立连接失败!!"
        var p1 = document.createElement("p");
        p1.innerText = data;
        p1.style.marginTop = '10px';
        p1.style.color = 'red';
        chat.appendChild(p1);
    }
    // 建立成功
    ws.onopen = function(){
        var data = "系统消息:建立连接成功!!"
        var p1 = document.createElement("p");
        p1.style.marginTop = '10px';
        p1.style.color = 'green';
        p1.innerText = data;
        chat.appendChild(p1);
    }
1.3 设置回调函数的内容,也就是处理服务端向客户端发来的数据:
    ws.onmessage = function(e){
    // 首先对与后端传来的数据进行解析
        var msg = JSON.parse(e.data);
        var sender,user_name,name_list,change_type;
        switch(msg.type){
            //  当服务器表示连接成功时,前端向后端发送用户信息
            case 'handshake':
            // 注意发送的数据要是json格式
                var user_info = {'type':'login','content':uname}
                // 这个sendMsg是自定义的函数,用来讲用户传来的数据转换为json格式,然后调用websocket的send()方法,发送给服务端。
                sendMsg(user_info)
                return;
                // 如果当前用户的状态是刚登录或者离线,那么对于传来的数据进行处理,使得在前端界面上出现一定的提示(xxx下线、在线人数)
            case 'login':
            case 'logout':
                user_name = msg.content
                name_list = msg.user_list
                change_type = msg.type
                // dealUser也是自定义的函数,用来对传入的数据进行处理,在界面上显示出当前在线人数,用户上线或下线信息。
                dealUser(user_name,change_type,name_list)
                return;
                // 如果用户是在线状态,则获取用户昵称(用于在界面上显示,xxx发言)
            case 'user':
                sender = msg.from +":";
                break;
            case 'system':
                sender = "系统消息";
                break;
                // 如果用户重复登录,则结束整个程序(这部分写的不好,没有找到如何关闭当前用户窗口的办法,只能结束整个程序了。)
            case 'exist':
                window.open("about:blank","_self").close()

        }
        var data = sender + msg.content;
        // 将消息显示在聊天界面上。
        var p1 = document.createElement("p");
        p1.style.marginTop = '10px';
        p1.innerText = data;
        chat.appendChild(p1);
    }

下面看一下上面所使用的函数:
1.sendMsg()

    function sendMsg(msg){
        // 编码
        var data = JSON.stringify(msg);
        ws.send(data);
    }

2.dealUser

    function dealUser(uname,type,ulist){
        // 用户列表
        var uls = document.querySelector('.user_list');
        // 用户数量
        var ctn = document.querySelector('.ctn');
        // 每次操作移除所有用户
        while(uls.hasChildNodes()){
            uls.removeChild(uls.firstChild)
        }
        //  遍历后端返回的列表,显示所有在线用户
        for(var index in ulist){
            var user = document.createElement("li");
            user.innerHTML = ulist[index];
            uls.append(user);
        }
        // 在线人数
        console.log(ulist.length)
        ctn.innerHTML = ulist.length;
        var change = type == 'login' ? '上线':'下线';
        var data = '系统消息:'+ uname +'已'+change;
        //  创建页面提示标签
        var p1 = document.createElement("p");
        p1.style.marginTop = '10px';
        p1.style.color = 'gray';
        p1.innerText = data;
        chat.appendChild(p1);
    }
1.4 由于用户发言方式在通过输入框input获取的,所以,我们可以给input添加上监听事件,当按下回车键时,发送数据:
    // 给input输入框添加监听事件
    var speak = document.querySelector('#speak');
    speak.onkeypress = function(e){
        if(e.which === 13){
        // 这里是处理用户发送数据的自定义的函数
            send();
        }else{
            console.log("发送失败");
        }
    }
    //  输入框发送信息
    function send(){
        var reg = new RegExp("rh","g");
        var data = speak.value;
        data = data.replace(reg,"");
        var msg = {
            'content':data.trim(),
            'type':'user'
        }
        sendMsg(msg)
        //  发送完毕,清空输入框
        speak.value = "";
    }
    
2.服务端: 1.首先我们先建立data结构体,用于对应前端传送数据的内容
// 数据信息

type  Data struct {
	User string `json:"user"`
	From string `json:"from"`
	Type string `json:"type"`
	Content string `json:"content"`
	UserList [] string `json:"user_list"`
}

2.启动路由,监听接口
// 这里是博主自己添加的数据库初始化函数,考虑到有些读者可能不需要连接数据库,就不放代码了。
	initDB()
	router := mux.NewRouter()
	// 注意:这里要和前端ws里面的名称相同。chat是要执行的回调函数。也是最重要的部分
	router.HandleFunc("/chating",chat)
	if err := http.ListenAndServe("127.0.0.1:8081",router);
	err != nil{
		fmt.Println("err1",err)
	}
3.建立客户端和管理者结构体

用以上名字有点不太合适哈。因为是聊天室,一般是多人在线。所以,我们建立一个管理者,用来管理所有在线的用户。相对而言比较方便。

// 用户结构
type Connection struct {
	// 连接
	con *websocket.Conn
	// 通道:用来传送数据
	sc chan[]byte
	// 数据
	data *Data
}
// 管理者结构体
type Manager struct {
	// 所有用户
	allUser map[*Connection]bool
	// 广播通道
	broast chan [] byte
	// 注册
	zc chan *Connection
	// 销毁
	xh chan *Connection
}
4.获取数据

这里我们可以考虑获取数据了,对于服务端来说,接收到用户的请求后,要读取用户请求的内容,然后给予用户反馈,而这个给予过程也就是写入数据到前端的过程。因此,我们需要写2个方法,一个读,一个写。

处理请求:读取:

// 该方法的接收者,是当前用户
func (u *Connection) reader()  {
	for{
		_,message,err := u.con.ReadMessage()
		// 报错,则说明读取失败。改用户可能不存在。销毁这个用户
		if err != nil{
		// 将这个用户存入管理者的销毁区中,告诫管理者,该用户不存在
			m.zc <- u
			break
		}
		// 解析接收到的数据
		err = json.Unmarshal(message,&u.data)
		if err != nil{
			fmt.Println("解析失败")
			return
		}
		switch u.data.Type {
		case "login":
		// 如果是登录状态,则数据内容里存的就是用户名。
				u.data.User = u.data.Content
				u.data.From = u.data.User
				
				//首先查询数据库中是否已经存在该用户
				//不存在则添加
				fmt.Println(!findUser(u.data.User))
				if !findUser(u.data.User){
					addUser(u.data.User)
					//添加到当前列表
					online_list = append(online_list,u.data.User)
					fmt.Println(len(online_list))
					//更新在线用户列表
					u.data.UserList = online_list
				}else{
				//	存在判断用户是否在线
					if isOnline(u.data.User){
						// 在线修改状态,返回给前端
						u.data.Type  = "exist"
					}else{
					//	 修改状态
						modify(u.data.User,1)
						//添加到当前列表
						online_list = append(online_list,u.data.User)
						fmt.Println(len(online_list))
						u.data.UserList = online_list
					}
				}
				//	将数据编码,放入到管理者的广播区域,用于广播给其他用户,当前用户的状态以及行为等。(上线、离线、发送数据等)
				dataNew,_ := json.Marshal(u.data)
				m.broast <- dataNew
		//	用户处理在线聊天状态
		case "user":
				u.data.Type = "user"
				dataNew,_ := json.Marshal(u.data)
				m.broast <- dataNew
		case "logout":
				u.data.Type = "logout"
				//修改用户状态
				modify(u.data.User,0)
				// 用户离线,需要从当前在线列表中删除,删除函数是自己写的哈。
				online_list = del(online_list,u.data.User)
				dataNew,_ :=  json.Marshal(u.data)
				m.broast <- dataNew
				// 将离线用户放入销毁区域
				m.xh <- u
		}
		
	}
}

返回给客户端数据:写

// 向前端写入数据
func (u *Connection)write()  {
	for message := range u.sc{
		u.con.WriteMessage(websocket.TextMessage,message)
	}
	u.con.Close()
}
5.回调函数chat

还记得路由连接成功后的回调函数嘛,这个才是重中之重,前面的功能我们已经完成。下一步就是写这个回调函数

// 在线用户列表
var online_list = [] string{}
// 这个博主也不太理解,大家可以查询一下
var wsNew = &websocket.Upgrader{ReadBufferSize: 512,WriteBufferSize: 512,CheckOrigin:func(r *http.Request)bool{return true}}
func chat(w http.ResponseWriter,r *http.Request)  {
//	升级协议
	ws,err := wsNew.Upgrade(w,r,nil)
	if err != nil{
		return
	}
//	初始化一个用户
	 u := &Connection{con: ws,sc:make(chan[]byte),data:&Data{}}
//	 把该用户传入管理者的注册区域(在管理区域中已经修改用户的状态为handshake了)
	m.zc <- u
//	向前端写入数据(当前端判断用户的状态为handshake,则发送用户登录的信息,修改了状态为login)
	go u.write()
	u.reader()
	// 这里博主也不是很理解哈,有知道了,可以在评论区告诉一下博主哈。
	defer func() {
		u.data.Type = "logout"
		online_list = del(online_list,u.data.User)
		u.data.UserList = online_list
		u.data.Content = u.data.User
		//修改用户状态
		modify(u.data.Content,0)
		dataNew,_ := json.Marshal(u.data)
		m.broast <- dataNew
		m.xh <- u
	}()

}

6.管理者:

上面部分完成后,就差管理者了。主要是负责用户消息的广播、用户的销毁。

// 管理方法
func (m *Manager)run()  {
	for{
		select {
		// 获取注册的用户,存入用户列表。并修改状态为连接成功.讲修改后的状态写入数据通道,传递给前端
		case u := <- m.zc:
			m.allUser[u] = true
			u.data.Type = "handshake"
			u.data.UserList = online_list
			dataNew,_ := json.Marshal(u.data)
			u.sc <- dataNew
		case u := <-m.xh:
			// 判断退出的用户是否存在于用户列表,在,则删除改用户,关闭通道
			if _,ok := m.allUser[u];ok{
				delete(m.allUser,u)
				close(u.sc)
			}
		case data := <- m.broast:
			// 遍历所有用户列表。讲广播信息写入每个用用户的通道。供前端读取
			for u := range m.allUser{
				select {
				case u.sc <- data:
				default:
					delete(m.allUser,u)
					close(u.sc)
				}
			}
		}
	}
}
7.启动管理者方法

最后,我们不要忘记启动这个管理者的协程哈。回到路由连接那里:

func main()  {
	// 连接数据库
	initDB()
	go m.run()
	router := mux.NewRouter()
	router.HandleFunc("/chating",chat)
	if err := http.ListenAndServe("127.0.0.1:8081",router);
	err != nil{
		fmt.Println("err1",err)
	}

}

这样我们就实现了一个简单的聊天室了哈。

总结:

以上内容算是对于go基础语法的巩固,如有错误,欢迎大家提出

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/945306.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号