简单聊天室功能实现,没用框架,纯go语言,教程https://www.bilibili.com/video/BV1ZU4y1W72B
mac安装nc,使用nc命令模拟客户端,如 nc -v 172.0.0.1 8080
package main
import (
"fmt"
"net"
"strings"
"time"
)
type User struct {
id string
name string
msg chan string
}
//搞一个key-value变量(map结构),并make出来空间
var allUsers = make(map[string]User)
//全局管道,进行广播
var message = make(chan string, 10)
func main() {
listener, err := net.Listen("tcp", "172.0.0.1:8080")
if err != nil {
fmt.Println("服务器端提示:listen err:", err)
return
}
//监听广播管道
go broadcast()
//持续监听新用户连接请求
for {
fmt.Println("服务器端提示:监听中...")
conn, err := listener.Accept()
if err != nil {
fmt.Println("conn err:", err)
}
fmt.Println("服务器端提示:建立连接成功...")
//握手成功
go handler(conn)
}
}
//连接完成,处理业务
func handler(conn net.Conn) {
fmt.Println("服务器端提示:handle...")
//把所有user写进map
clientAddr := conn.RemoteAddr().String()
newuser := User{
id: clientAddr,
name: clientAddr,
msg: make(chan string),
}
allUsers[newuser.id] = newuser
//写回客户端
go writebackToClient(&newuser, conn)
//退出账户
var isQuit = make(chan bool)
var timeReset = make(chan bool)
go quit(isQuit, timeReset, &newuser, conn)
//上线广播
msginfo := fmt.Sprintf("id:%s,name:%s 上线了...\n", newuser.id, newuser.name)
message <- msginfo
//---------- 处理具体业务 ------------
//持续接收客户端数据
for {
buf := make([]byte, 1024)
//读取客户端数据到buf
count, err := conn.Read(buf)
//用户crtl+c退出
if count == 0 {
isQuit <- true
return
}
if err != nil {
fmt.Printf("服务器端提示:conn.Read err:%s,count:%d", err, count)
return
}
//所有用户信息整理成一个字符串,并用回车换行
var whoUser []string
for _, user := range allUsers {
userinfo := fmt.Sprintf("id:%s,name:%s", user.id, user.name)
whoUser = append(whoUser, userinfo)
}
r := strings.Join(whoUser, "\n")
//查询所有用户业务
userInput := string(buf[:count-1])
if len(userInput) == 5 && (userInput == "[who]" || userInput == "[WHO]") {
newuser.msg <- (r + "\n")
//修改名字
} else if len(userInput) > 8 && (userInput[:8] == "[rename]") {
newuser.name = strings.Split(userInput, "]")[1]
allUsers[newuser.id] = newuser
newuser.msg <- "[rename] successfully!\n"
} else {
message <- (userInput + "\n")
}
//重置计时器
timeReset <- true
}
//---------- 处理具体业务 ------------
}
//广播消息
func broadcast() {
fmt.Println("服务器端提示:广播启动成功...")
defer fmt.Println("服务器端提示:广播异常退出...")
for {
info := <-message
for _, user := range allUsers {
user.msg <- info
}
}
}
//写回客户端
func writebackToClient(user *User, conn net.Conn) {
fmt.Println("服务器端提示:writebackToClient启动...")
for {
data := <-user.msg //必须加上 <-
conn.Write([]byte(data))
}
// for data := range user.msg { //range下不用 <-
// conn.Write([]byte(data))
// }
}
//退出账户
func quit(isQuit, timeReset <-chan bool, user *User, conn net.Conn) {
fmt.Println("服务器端提示:等待执行退出账户...")
defer fmt.Println("服务器端提示:用户退出成功!")
// if <-isQuit {
// message <- fmt.Sprintf("username:%s退出\n", user.name)
// delete(allUsers, user.id)
// conn.Close()
// return
// }
for {
select {
case <-isQuit:
message <- fmt.Sprintf("username:%s主动退出", user.name)
delete(allUsers, user.id)
conn.Close()
return
case <-time.After(10 * time.Second):
message <- fmt.Sprintf("username:%s超时退出", user.name)
delete(allUsers, user.id)
conn.Close()
return
case <-timeReset:
fmt.Printf("客户端提示:uesrname:%s重置计时器成功\n", user.name)
}
}
}