package main
import (
"fmt"
"net"
"strconv"
"strings"
)
type user struct {
conn net.Conn
name string
} //定义用户结构体
var online map[string]*user //该map储存在线用户
var msg chan string //定义全局的消息channel,接受到的消息会储存在这里,然后通过manager读取消息,做出相应处理
func manager() {
online = make(map[string]*user)
msg = make(chan string)
//初始化online和msg
for { //从msg中不断地取数据
m := <-msg
var name = ""
for _, ss := range m {
if ss != ' ' {
name += string(ss)
} else {
break
}
}
fmt.Println(name)
if name == "online" {
use := strings.Split(m, " ")[1]
for _, u := range online {
if u.name == use {
var joint string = "当前在线人数为:"
joint += strconv.Itoa(len(online)) + "人\n"
u.conn.Write([]byte(joint))
}
//u.conn.Write([]byte("当前在线人数为:" + string(rune(len(online))) + "人"))
}
continue
}
for _, u := range online {
if u.name != name {
u.conn.Write([]byte(m)) //将数据群发给每个在线用户
}
}
}
}
func userHander(conn net.Conn) {
defer conn.Close()
addr := conn.RemoteAddr().String() + "login" + "\n"
msg <- addr //将用户登录的消息发到全局msg里
var uu = user{conn, conn.RemoteAddr().String()} //初始化结构体
online[conn.RemoteAddr().String()] = &uu //把该用户结构体加到在线用户map中
for { //接受用户发来的消息
by := make([]byte, 1024)
n, _ := conn.Read(by)
line := string(by[:n])
//处理online关键字,展示在线人数
if line == "online\r\n" {
msg <- "online " + uu.name
continue
}
//处理exit关键字,用户退出
if line == "exit\r\n" {
delete(online, conn.RemoteAddr().String())
msg <- uu.name + " 已成功退出"
return
}
//处理change关键字,修改用户姓名
var name = ""
for _, ss := range line {
if ss != ' ' {
name += string(ss)
} else {
break
}
}
if name == "change" {
newName := strings.Split(line, " ")
oldName := uu.name
uu.name = newName[1]
online[conn.RemoteAddr().String()].name = newName[1]
msg <- "用户" + oldName + "已更名为" + newName[1] + "\n"
continue
}
msg <- uu.name + ": " + string(by[:n]) //将消息存到全局的msg
}
}
func main() {
listener, err := net.Listen("tcp", "127.0.0.1:9000") //实现tcp连接监听本机的9000端口
if err != nil {
fmt.Println("err:", err)
}
go manager() //该go程负责从全局的消息channel中取出数据并发送数据
for { //利用for循环处理连接上来的用户
conn, err := listener.Accept()
if err != nil {
fmt.Println("err", err)
}
go userHander(conn) //把连接上来的用户交给该go程
}
}
client端
package main
import (
"bufio"
"fmt"
"net"
"os"
)
var msg = make(chan string)
func read() {
for { //从msg里读出信息并将信息写到控制台
m := <-msg
fmt.Print(m)
}
}
func write(conn net.Conn) {
for { //读取server端发来地信息,并将信息写到全局的msg里
s := make([]byte, 10, 10)
n, err := conn.Read(s)
if err != nil {
fmt.Println("err:",err)
}
msg <- string(s[:n])
}
}
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:9000") //发送连接请求
if err != nil {
fmt.Println("err: ",err)
}
defer conn.Close()
go read()
go write(conn)
for { //读取用户从控制台输入地信息
reader := bufio.NewReader(os.Stdin)
s, err := reader.ReadString('\n')
if err != nil {
fmt.Println("err:",err)
}
if s == "exit\r\n" { //用户退出
conn.Write([]byte(s))
break
} else if s == "change\r\n" { //用户更名
fmt.Println("请输入要更改的名字")
s, _ := reader.ReadString('\n')
s2 := []byte(s[0 : len(s)-2])
s3 := append([]byte("change "), s2...)
conn.Write([]byte(s3))
continue
}
conn.Write([]byte(s)) //将消息发送给server端
}
}
聊天室的大致思路图
1.运行server代码
2.运行client代码
发消息:输入消息内容+回车
更名.输入change+回车,然后输入新名+回车
查询在线人数:输入online回车
退出:输入exit+回车