go基础开发的聊天室(二)

前言

前一篇说了聊天室客户端的实现,这一篇就接着上篇讲服务端,与很多聊天室不同,我在服务端里加入了管理员,管理员与普通用户的功能相比多了屏蔽发言,以及踢出群聊的功能

数据的接收与发送
和客户端的没什么不同

func recv(recvData chan string, conn net.Conn) {

	for {

		buf := make([]byte, 1024)

		n, err := conn.Read(buf)

		if err != nil {

			connection <- conn

			return
		}

		recvData <- string(buf[:n])

	}

}

/**
 * 数据发送
 * sentData  接收数据Channel
 *  conn      TCP连接对象
 **/
func sent(sentData chan string, conn net.Conn) {

	for {

		data := <-sentData

		_, err := conn.Write([]byte(data))

		if err != nil {

			connection <- conn

			return
		}

	}
}

客户端的连接管理

func connManager(connection chan net.Conn) {
	for {
		conn := <-connection
		username := ipToUname[conn.RemoteAddr().String()]
		notesInfo(messages, "用户:"+username+"已退出聊天室!")
		conn.Close()
		delete(conns, username)
		delete(ipToUname, conn.RemoteAddr().String())
	}
}

数据解析与封装

func dataSent(conns *map[string]clientInfo, messages chan string) {

	for {
		msg := <-messages

		fmt.Println(msg)

		data := strings.Split(msg, "-") // 聊天数据分析:
		length := len(data)

		if length == 2 { // 管理员单个用户发送控制命令

			(*conns)[data[0]].sentData <- data[1]

		} else if length == 3 { // 用户列表

			(*conns)[data[1]].sentData <- data[2]

		} else if length == 4 { // 向单个用户发送数据

			msg = data[1] + " say to you : " + data[3]

			(*conns)[data[2]].sentData <- msg

		} else {
			// 群发
			for _, value := range *conns {
				value.sentData <- msg
			}
		}

	}
}

管理员注册处理

/**
 * 获取全部已注册用户的数据,用于登录注册时验证
 
 */
func getAllUser(filename string) map[string]userData {
	buf := make([]byte, dataSize)
	udata := make(map[string]userData)
	file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0766) //打开保存用户的文件
	defer file.Close()                                              //保证文件关闭
	errorExit(err, "getAllUser")
	n, _ := file.Read(buf)
	json.Unmarshal(buf[:n], &udata) //将流文件转成json并赋值给udata这个map
	return udata
}

/**
 * 添加新注册用户数据
 
 */
func insertNewUser(filename, username, password string) {
	file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0766)
	defer file.Close()
	checkError(err, "insertNewUser")
	uData[username] = userData{password, 1}
	data, _ := json.MarshalIndent(uData, "", "  ")
	file.WriteString(string(data))
}
func regProcess(username, password string) (status bool, info string) {
	if len(uData) == maxReg {
		return false, "注册人数已满!"
	}
	if _, ok := uData[username]; ok {
		return false, "用户已存在,请更换注册名!"
	} else {
		insertNewUser(dataFileName, username, password)
		return true, success
	}
}

管理员登录处理

func logProcess(username, password string) (status bool, info string) {

	if len(conns) == maxLog {
		return false, "当前登录人数已满,请稍后登录"
	}
	if user, ok := uData[username]; !ok {
		return false, "用户名或密码错误!"
	} else {
		if user.Password == password {
			if _, ok := conns[username]; ok {
				return false, "用户已登录!"
			} else {
				return true, success
			}
		} else {
			return false, "用户名或密码错误!"
		}
	}
}

用户进入聊天室资格认证

func userAuth(conn *net.Conn, recvData, sentData chan string) {

	for {

		data := strings.Split(<-recvData, "-") // 等待用户发送登录或注册数据
		flag, username, password := data[0], data[1], data[2]

		if flag == "Reg" {

			_, info := regProcess(username, password)
			sentData <- info

		} else {

			status, info := logProcess(username, password)
			sentData <- info

			if status == true {
				messages <- ("用户:" + username + "进入聊天室")            // 用户登录成功,发送系统通知
				conns[username] = clientInfo{*conn, sentData}       // 记录用户登录状态信息
				ipToUname[(*conn).RemoteAddr().String()] = username // 记录IP与用户名对应信息
				messages <- "List-" + username + "-" + userList()   // 向用户发送已在线用户列表
				go dataRec(&conns, username, recvData, messages)    // 开启服务器对该客户端数据接收线程
				break
			}
		}
	}
}

服务端的tcp连接创建

func createTCP(tcpaddr string) *net.TCPListener {

	tcpAddr, err := net.ResolveTCPAddr("tcp4", tcpaddr)

	errorExit(err, "ResolveTCPAddr")

	l, err := net.ListenTCP("tcp", tcpAddr)

	errorExit(err, "DialTCP")

	return l
}

管理员的登录注册

func intoManager() {
	fmt.Println("请输入将要进行操作:1、管理员注册 2、管理员登录")
	var input string
LOOP:
	{
		fmt.Scanln(&input)
		switch input {
		case "1":
			adminReg()
		case "2":
			adminLog()
		default:
			goto LOOP
		}
	}
	admimManager(messages)
}

/**
 * 管理员登录程序
 **/
func adminLog() {

	for {
		var adminname, password string
		fmt.Println("请输入管理员用户名:")
		fmt.Scanln(&adminname)
		fmt.Println("请输入管理员密码:")
		fmt.Scanln(&password)
		if pwd, ok := adminList[adminname]; !ok {
			fmt.Println("用户名或者密码错误")
		} else {
			if pwd != password {
				fmt.Println("用户名或者密码错误!")
			} else {
				fmt.Println("登录成功!")
				break
			}
		}
	}

}

/**
 * 管理员注册程序
 **/
func adminReg() {
	var adminname, password string
	fmt.Println("请输入管理员用户名:")
	fmt.Scanln(&adminname)
	fmt.Println("请输入管理员密码:")
	fmt.Scanln(&password)
	adminList[adminname] = password //将注册的管理员姓名密码保存到adminList中,单次启动有效
	fmt.Println("注册成功!请登录")
	adminLog() //跳转到登录
}

管理员一系列功能实现

func admimManager(messages chan string) {

	for {
		var input, objUser string
		fmt.Scanln(&input)
		switch input {
		case "/to":
			fmt.Println(userList())
			fmt.Println("请输入聊天对象:")
			fmt.Scanln(&objUser)
			if _, ok := conns[objUser]; !ok {
				fmt.Println("不存在此用户!")
			} else {
				fmt.Println("请输入消息:")
				fmt.Scanln(&input)
				notesInfo(messages, "To-Manager-"+objUser+"-"+input)
			}

		case "/all":
			fmt.Println("请输入消息:")
			fmt.Scanln(&input)
			notesInfo(messages, "Manager say : "+input)

		case "/shield":
			fmt.Println(userList())
			fmt.Println("请输入屏蔽用户名:")
			fmt.Scanln(&objUser)
			notesInfo(messages, objUser+"-/shield")
			notesInfo(messages, "用户:"+objUser+"已被管理员禁言!")

		case "/remove":
			fmt.Println(userList())
			fmt.Println("请输入踢出用户名:")
			fmt.Scanln(&objUser)
			notesInfo(messages, "用户:"+objUser+"已被管理员踢出聊天室!")
			if _, ok := conns[objUser]; !ok {
				fmt.Println("不存在此用户!")
			} else {
				conns[objUser].conn.Close() // 删除该用户的连接
				delete(conns, objUser)      // 从已登录的列表中删除该用户
			}
		}

	}
}

服务端启动程序

func StartServer(port string) {

	l := createTCP(":" + port)

	fmt.Println("服务端启动成功,正在监听端口!")

	go dataSent(&conns, messages) // 启动服务器广播线程
	go intoManager()              // 启动管理模块
	go connManager(connection)    // 启动连接管理线程

	for {

		conn, err := l.Accept()
		if checkError(err, "Accept") == false {
			continue
		}
		fmt.Println("客户端:", conn.RemoteAddr().String(), "连接服务器成功!")
		var recvData = make(chan string)
		var sentData = make(chan string)
		go recv(recvData, conn)                // 开启对客户端的接受数据线程
		go sent(sentData, conn)                // 开启对客户端的发送数据线程
		go userAuth(&conn, recvData, sentData) // 用户资格认证
	}

}

服务端主程序

func main() {

	if len(os.Args) == 2 {
		uData = getAllUser(dataFileName) // 用户登录、注册数据初始化

		StartServer(os.Args[1]) // 启动客户端

	} else {

		fmt.Println("输入错误!")
	}

}

用到的标准库

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

总结
终于写完了,啊啊啊!
源码地址
https://gitee.com/qian-xiawudi/test1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值