GO利用nc实现聊天室功能

package main

import (
	"fmt"
	"net"
	"strings"
)

//创建用户结构体(包含用户通道)
type User struct {
	name string
	id   string
	msg  chan string
}

//创建map存储用户
var AllUsers = make(map[string]User, 10)
//var AllUsers = make([]byte, 1024)
//创建全局通道(客户端在全局管道中收发消息)
var message = make(chan string, 1024)

//封装err(简化文中多次if err != nil的情况)
func checkErr(err error) {
	if err != nil {
		fmt.Println(err)
	}
	return
}

func main() {
	//创建服务
	listen, err := net.Listen("tcp", ":8088")
	checkErr(err)

	//启动全局监听(将客户端发送的数据发送到所有的客户端)
	go broadcast()

	//监听服务(for循环表示持续监听)
	for true {
		//创建连接
		conn, err := listen.Accept()
		checkErr(err)
		go headler(conn)
	}
}

func headler(conn net.Conn) {
	//获取瀛湖ip和port作为参数,传给newUser
	ctAddr := conn.RemoteAddr().String()
	//创建一个用户实例(管道有缓冲,防止读管道发生阻塞)
	newUser := User{ctAddr, ctAddr, make(chan string, 1024)}
	//把用户存储到map中或者slice中
	AllUsers[newUser.id] = newUser
	//AllUsers = append(AllUsers,newUser)如果使用切片,在声明AllUsers时,应当声明为slice

	//server端接收用户发送的数据(需要先创建一个buf用来接收和读取用户发送的数据)
	buf := make([]byte, 1024)
	cnt, err := conn.Read(buf)
	checkErr(err)
	//客户端发送数据到管道中,自己监听管道将数据再写回自己的客户端
	go ClientMsg(&newUser, conn)

	//业务逻辑处理
	//上线通知(将登陆信息发送到广播的管道中,所有客户端都可以读取的到)
	loginfo := fmt.Sprintf("%s:%s上线了", newUser.name, newUser.id)
	message <- loginfo
	//命令:who获取用户输入数据,判断是否符合命令关键字(实现查询所有登陆的用户)
	input := string(buf[:cnt-1])
	userdata := make([]string,128)
	if input == "\\who" && len(input) == 4 {
		for _, users := range AllUsers {
			userdata = append(userdata,users.name)
		}
		r := strings.Join(userdata, "\n")
		//将查询出来的数据写入用户的管道中,用户自己调用ClientMsg方法,将数据返回到客户端
		newUser.msg <- r
	}else if cnt == 0 || input == "\\quit" {
		//在用户池中删除用户
		delete(AllUsers,newUser.id)
		//通知所有用户,某一个用户退出当前系统
		logout := fmt.Sprintf("%s退出群聊",newUser.name)
		message<-logout
		//关闭连接
		conn.Close()
		//return
	}else {
		message<-input
	}

}

//广播(从管道中读取数据把数据发送给所有客户端的管道中,这部分数据所有人可见,包含所有客户端的数据)
func broadcast() {
	info := <-message
	for _, user := range AllUsers {
		user.msg <- info
	}
}

//客户端监听各自的管道中的数据(这部分数据只包含自己管道中的数据,仅自己可见)
func ClientMsg(user *User, conn net.Conn) {
	//user.msg应为有缓冲,否则在此处会阻塞
	for data := range user.msg {
		//把数据写回客户端
		_, _ = conn.Write([]byte(data))
	}
}

原理图
socket

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值