golang|练手项目|在线聊天室(极简版)

简介:

  • 实现用户昵称上传以及多用户在线同一房间聊天(单聊天室)

展示

  • 1.server端(显示连接客户端ip和用户昵称及聊天室信息)
    在这里插入图片描述

  • 2.client端

  • 创建2个客户端client1,client2(客户端代码创建2份)

  • client1(输入昵称,输入发送消息)

  • 在这里插入图片描述

  • client2

  • 在这里插入图片描述

技术提升点:

  • 1.使用协程处理多客户端连接操作
  • 2.理解客户端与服务器之间如何传输数据以及对数据的处理
  • 3.使用channel实现多协程之间的数据交互
  • 4.熟悉golang语法

结构及其功能:

1.项目结构图

在这里插入图片描述

2.服务端

数据部分
  • broadcast:存储用户发送的消息,一个Msg类型的channel
  • onlineclient:存储用户ip与管道的map
  • Msg:存储消息的结构,由user和content组成,
  • Client:存储用户信息的结构,由addr和chan组成,
函数部分
  • MessageManager:监控broadcast中的信息并转为json格式后传入所有用户的管道
  • HandleConnect:客户端管理客户端连接,并将用户信息保存到onlineclient中
  • read:读取客户端信息并且保存到broadcast中
  • write:将用户管道中的信息返回客户端

3.客户端

1.函数部分
  • read:读取服务端返回的信息并且转化为对象存储,然后输出
  • write:获取标准输入流中的数据发送给服务端

项目代码

1.server
package main

import (
	"encoding/json"
	"fmt"
	"net"
	"strings"
)

// 消息的结构体:注意字段和结构体首字母大写
type Msg struct {
	Content string //  因为传递的数据结构为json
	User    string
}

// 客户信息结构体
type Client struct {
	Chan chan []byte //信息管道
	Addr string
}

// 广播管道
var broadcast = make(chan Msg)

// 组合标志名使用驼峰命名法
var onlineClient = make(map[string]Client) //此处map可否改为ip与管道对应?

// 昵称map
var nicks = make(map[string]string)

// 消息管理
func MessageManger() {
	for {
		msg := <-broadcast //不断的从广播管道中读取数据,没有数据则会阻塞
		for _, client := range onlineClient {
			msgByte, _ := json.Marshal(msg) //使用json格式对数据进行传输
			client.Chan <- msgByte
		}
	}
}

// 读取客户端的消息,放到broadcast中
func read(conn net.Conn) {
	for {
		buf := make([]byte, 1024)
		n, _ := conn.Read(buf)                        
		content := strings.TrimSpace(string(buf[:n])) //对客户端消息处理
		broadcast <- Msg{content, nicks[conn.RemoteAddr().String()]}
		fmt.Printf("%s的消息:%s\n", nicks[conn.RemoteAddr().String()], content)
	}
}

// 对客户管道写入broadcast中的数据
func write(conn net.Conn, client Client) {
	for {
		msg := <-client.Chan   //读取Msg
		_, _ = conn.Write(msg) //返回数据
	}
}

// 客户连接管理
func HandleConnect(conn net.Conn) {
	tip, _ := json.Marshal(Msg{"请输入昵称~~~", "系统消息"})
	_, _ = conn.Write(tip)
	//服务器接受数据标准流程:1.创建一个缓冲区,读取数据 2.从缓冲区提取数据并转化为string 3.对字符串进行去除左右空白符
	buf := make([]byte, 1024)
	n, _ := conn.Read(buf)
	nick := strings.TrimSpace(string(buf[:n]))
	addr := conn.RemoteAddr().String()
	nicks[addr] = string(nick)
	fmt.Printf("来自客户端%s的连接,昵称:%s\n", addr, nick)
	client := Client{Chan: make(chan []byte), Addr: addr}
	onlineClient[addr] = client
	content := string(nick) + "上线了~~~~"
	//tip := Msg{User:"系统消息", Content:content}
	//在可以不使用变量存储的操作,一般只省略一步
	broadcast <- Msg{Content: content, User: "系统消息"} //此处使用匿名变量代码更简洁
	go read(conn)
	go write(conn, client)
}

func main() {
	fmt.Println("在线聊天室开启~~~~")
	//创建一个监听器
	listener, err := net.Listen("tcp", "127.0.0.1:8080")
	//错误处理
	if err != nil {
		fmt.Println(err)
		return
	}
	//服务器开启监听广播
	go MessageManger()
	//接受每个客户端的连接
	for {
		conn, err := listener.Accept() //获取连接
		if err != nil {
			fmt.Println("") //连接错误输出错误信息,重新连接
			continue
		}
		//为每个客户端的连接创建协程
		go HandleConnect(conn)
	}

}
2.client
package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"net"
	"os"
)

type Msg struct {
	Content string
	User    string
}

// 从服务端读取数据
func read(conn net.Conn) {
	for {
		res := make([]byte, 1024) 
		n, err := conn.Read(res)
		if err != nil {
			fmt.Println(err)
			return
		}
		result := res[:n] 
		var msg Msg
		_ = json.Unmarshal(result, &msg) //json转结构体
		fmt.Printf("[%s]:%s\n", msg.User, msg.Content)
	}
}

// 往服务端写入数据
func write(conn net.Conn) {
	for {

		reader := bufio.NewReader(os.Stdin)
		content, _ := reader.ReadString('\n')
		_, _ = conn.Write([]byte(content))
	}
}

func main() {
	fmt.Println("客户端启动~~~~")
	conn, err := net.Dial("tcp", "127.0.0.1:8080")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer conn.Close() //客户端控制连接关闭

	go read(conn)

	write(conn)
}

结尾

现在是凌晨三点,从11点30开始写,刚开始想着30分钟完成,12点睡觉,没想到居然拖到现在。写文章的中间总是参杂着许多细碎的事情,不是去洗衣服,就是吃东西,看微信,抖音,这些与写文章无关的事物。唉,做事总是如此,设立目标,却不贯彻执行,面对有点难度的事情,潜意识反应总是逃避,并顺带找一个看似正当的借口麻痹自己(如:好像有点饿,先吃点东西再看看怎么解决吧),以减轻内心的谴责。但回归理性又何尝不明白,逃避并没有解决问题,不断的逃避,问题就会不断累积,总会有一天会陷入无处可逃的境地。在理性与感性中内心不断的徘徊,好在还有一丝毅力,也算是坚持写完。经此一番,有不少感悟:1.定下目标贯彻执行!2.遇到困难直面解决!3.坚持做正确的事情!

哈哈,亲爱的你如果都看到了这,还有什么理由不关注我呢?

最后的最后祝各位都能得偿所愿,期待咱们的下一次相遇!

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值