Golang实现即时通讯系统

即时通讯系统

1.基础server构建

  1. 创建一个Server的结构体,结构体应该包含服务端的IP和端口
  2. 写一个创建Server的方法
  3. 创建一个启动Server函数
  4. 创建一个业务链接函数

server.go

package main
import (
	"fmt"
	"net"
)

type Server struct{
    Ip string
   Port int
}
//创建Server对象
func NewServer(ip string,port int)*Server{
    server:=&Server{
        Ip:ip,
        Port:port,
    }
    return server
}
//启动Server服务函数
func(this *Server)Start(){
    listener,err:=net.Listen("tcp",fmt.Sprintf("%s:%d",this.Ip,this.Port))
    if err!=nil{
        fmt.Plrintln("Accept Error:",err)
        return
    }
    for{
         conn,err:=listener.Accept()
        if err!=nil{
            fmt.Println("Listener Accept err:",err)
            continue
        }
        go this.Handler(conn)
    }
  //Handler函数 业务函数
    func (this *Server)Handler(conn net.Conn){
        fmt.Println("业务链接成功!")
    }
}

main.go

package main

func main(){
    server:=NewServer("127.0.0.1",8888)
    server.Start()
}

2.用户上线及广播功能

  • user.go
    1. 创建User结构体
    2. 创建User对象
    3. 监听User对应channel消息
  • server.go
    1. Server结构体新增在线用户表和Message管道属性
    2. 在处理客户端上线的Handler函数中根据端口创建用户并添加到在线用户表中
    3. Handler函数中添加广播消息函数,广播用户上线的消息
    4. 添加广播消息的函数
    5. 添加广播消息channel方法
    6. 用一个goroutine单独监听Message管道

user.go

package main
import "net"
type User struct{
    Name string 
    Addr string
    C chan string
    conn net.Conn
}

//创建User对象
func NewUser(conn net.Conn)*User{
    userAddr:=conn.RemoteAddr().String()
    user:=&User{
        Name:userAddr,
        Addr:userAddr,
        C :make(chan string),
        conn:conn
    }
    
    //监听当前user的channel消息
    go user.ListenMessage()
    return user
}
//监听当前用户channel管道消息函数,一旦有消息,就直接发送给对端客户端
func (this *User)ListenMessage(){
    for {
        msg:=<-this.C
        this.conn.Write([]byte(msg+"\n"))
    }
}

server.go

package main

type Server struct {
    Ip string
    Port int
    Message chan string 
    OnlineMap map[string]*User
    mapLock sync.RWMutex
}
//创建一个Server对象
func NewServer(ip string ,port int)*Server{
    server:=&Server{
        Ip:ip,
        Port:port,
        Message:make(chan string),
        OnlineMap:make(map[string]*User)
    }
    return server
}
//创建启动函数
func (this *Server)Start(){
    listener,err:=net.Listen("tcp",fmt.Sprintf("%s:%d",this.IP,this.Port))
    if err!=nil{
        fmt.Println("net.Listen err:",err)
        return
    }
    defer listener.Close()
    go this.ListenMessager()
    for {
        conn,err:=listener.Accept()
         if err!=nil{
        fmt.Println("Listenner accept err:",err)
        continue
    	}
        go this.Handler(conn)
    }
}
//创建Handler函数
func (this *Server)Handler(conn net.Conn){
    user:=NewUser(conn)
    this.mapLock.Lock()
    this.OnlineMap[user.Name]=user
    this.mapLock.Unlock()
    //广播当前用户上线的消息
    this.BroadCast(user,"已上线")
    //阻塞当前handler
    select{}
}
//监听Message广播消息channel的goroutine,一旦有消息就发送给全部的在线User
func (this *Server)ListenMessager(){
    msg:=<-this.Message
    this.mapLock.Lock()
    for _,cli:=range OnlineMap{
        cli.C<-msg
    }
    this.mapLock.Unlock()
}
//广播函数
func (this *Server)BroadCast(user *User,msg string){
    sendMsg:="["+user.Addr+"]"+user.name+":"+msg
    this.Message<-sendMsg
}

3.用户消息广播机制

  • server.go
    1. 完善handle处理业务⽅法,启动 ⼀个针对当前客户端的读gorutine

server.go

package main
 
type Server struct {
    Ip string 
    Port int 
    OnlineMap map[string]*User
    Message chan string
    mapLock sync.RWMutex
}

func NewServer(ip string ,port int)*Server{
    server:=&Sever{
        Ip:ip,
        Port:port,
        OlineMap:make(map[string]*User),
        Message:make(chan string),
    }
    return server
}

func(this *Sever)Handler(conn net.Conn){
    user:=NewUser(conn)
    this.mapLock.Lock()
    OlineMap[user.Name]=user
    this.mapLock.Unlock()
  	this.BroadCast(user,"已上线")
    go func(){
        buf:=make([]byte,4096)
        for{
            n,err:=conn.Read(buf)
            if n==0{
                this.BroadCast(user,"已下线")
                return
            }
            if err!=nil&&err!=io.EOF{
                fmt.Println("Conn Read Error:",err)
                return
            }
            msg:=string(buf[:n-1])
            this.BroadCast(user,msg)
        }
    }()
    select{}
}
func (this *Server)Start(){
    listener,err:=net.Listen()
    if err!=nil{
        fmt.Println(net.Listen err:",err)
    }
    defer listener.Close()
    go this.ListenMessager()
    for{
        conn,err:=listener.Accept()
        if err!=nil{
            fmt.Println("Listener Accept err:",err)
            continue
        }
        go this.Handler(conn)
        
    } 
}
func (this *Server)ListenMessager(){
    msg:=<-this.Message
    this.mapLock.Lock()
    for _,cli:=range OnlineMap{
        cli.C<-msg
    }
    this.mapLock.Unlock()
}
func(this *Server)BroadCast(user *User,msg string){
    sendMsg:="["+user.Addr+"]"+user.Name+":"+msg
    this.Message<-sendMsg
}

4.用户业务层封装

  • user.go
    1. 新增和server关联
    2. 新增online方法
    3. 新增offline方法
    4. 新增 doMessage方法
  • server.go
    1. 将之前的user业务进行替换

user.go

package main

import "net"

type User struct {
	Name   string
	Addr   string
	C      chan string
	conn   net.Conn
	server *Server
}

func NewUser(conn net.Conn, server *Server) *User {
	userAddr := conn.RemoteAddr().String()
	user := &User{
		Name:   userAddr,
		Addr:   userAddr,
		C:      make(chan string),
		conn:   conn,
		server: server,
	}

	return user
}

func (this *User) Online() {
	this.server.mapLock.Lock()
	this.server.OnlineMap[this.Name] = this
	this.server.mapLock.Unlock()
	this.server.BroadCast(this, "已上线")
}

func (this *User) Offline() {
	this.server.mapLock.Lock()
	delete(this.server.OnlineMap, this.Name)
	this.server.mapLock.Unlock()
	this.server.BroadCast(this, "已下线")
}
func (this *User) DoMessage(msg string) {
	this.server.BroadCast(this, msg)
}

func (this *User) LitenMessage() {
	for {
		msg := <-this.C
		this.conn.Write([]byte(msg + "\n"))
	}
}

server.go

package main

import (
	"fmt"
	"io"
	"net"
	"sync"
)

type Server struct {
	Ip        string
	Port      int
	Message   chan string
	OnlineMap map[string]*User
	mapLock   sync.RWMutex
}

func NewServer(ip string, port int) *Server {
	server := &Server{
		Ip:        ip,
		Port:      port,
		Message:   make(chan string),
		OnlineMap: make(map[string]*User),
	}
	return server
}
func (this *Server) Handler(conn net.Conn) {
	user := NewUser(conn, this)
	user.Online()
	go func() {
		buf := make([]byte, 4096)
		for {
			n, err := conn.Read(buf)
			if n == 0 {
				user.Offline()
				return
			}
			if err != nil && err != io.EOF {
				fmt.Println("Conn Read err:", err)
				return
			}
			msg := string(buf[:n-1])
			user.DoMessage(msg)
		}
	}()
	select {}
}
func (this *Server) Start() {
	listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", this.Ip, this.Port))
	if err != nil {
		fmt.Println("net.Listen err:", err)
		return
	}
	defer listener.Close()
	go this.ListenMessage()
	for {
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println("listener accept err:", err)
			continue
		}
		go this.Handler(conn)

	}
}
func (this *Server) ListenMessage() {
	for {
		msg := <-this.Message
		this.mapLock.Lock()
		for _, cli := range this.OnlineMap {
			cli.C <- msg
		}
		this.mapLock.Unlock()
	}
}
func (this *Server) BroadCast(user *User, msg string) {
	sendMsg := "[" + user.Addr + "]" + user.Name + ":" + msg
	this.Message <- sendMsg
}

5.查询在线用户

  • user.go
    1. 输入消息格式: “who”
    2. 新增SendMsg方法向对象客户端广播消息
    3. 在DoMessage()方法中,加上对“who”指令的处理,返回在线用户消息

user.go

package main

import "net"

type User struct {
	Name   string
	Addr   string
	C      chan string
	conn   net.Conn
	server *Server
}

func NewUser(conn net.Conn, server *Server) *User {
	userAddr := conn.RemoteAddr().String()
	user := &User{
		Name:   userAddr,
		Addr:   userAddr,
		C:      make(chan string),
		conn:   conn,
		server: server,
	}

	return user
}

func (this *User) Online() {
	this.server.mapLock.Lock()
	this.server.OnlineMap[this.Name] = this
	this.server.mapLock.Unlock()
	this.server.BroadCast(this, "已上线")
}

func (this *User) Offline() {
	this.server.mapLock.Lock()
	delete(this.server.OnlineMap, this.Name)
	this.server.mapLock.Unlock()
	this.server.BroadCast(this, "已下线")
}
func (this *User) SendMsg(msg string) {
	this.conn.Write([]byte(msg))
}
func (this *User) DoMessage(msg string) {

	if msg == "who" {
		this.server.mapLock.Lock()
		for _, cli := range (this.server.OnlineMap) {
			onlineMsg := "[" + cli.Addr + "]" + cli.Name + "在线...\n"
			this.SendMsg(onlineMsg)
		}
		this.server.mapLock.Unlock()
	} else {
		this.server.BroadCast(this, msg)
	}
}

func (this *User) LitenMessage() {
	for {
		msg := <-this.C
		this.conn.Write([]byte(msg + "\n"))
	}
}

6.修改用户名

  • user.go
    1. 消息格式“rename|张三”
    2. 在DoMessage()⽅法中,加上对“rename|张三”指令的处理,返回在线⽤户信息
package main

import (
	"net"
	"strings"
)

type User struct {
	Name   string
	Addr   string
	C      chan string
	conn   net.Conn
	server *Server
}

func NewUser(conn net.Conn, server *Server) *User {
	userAddr := conn.RemoteAddr().String()
	user := &User{
		Name:   userAddr,
		Addr:   userAddr,
		C:      make(chan string),
		conn:   conn,
		server: server,
	}

	return user
}

func (this *User) Online() {
	this.server.mapLock.Lock()
	this.server.OnlineMap[this.Name] = this
	this.server.mapLock.Unlock()
	this.server.BroadCast(this, "已上线")
}

func (this *User) Offline() {
	this.server.mapLock.Lock()
	delete(this.server.OnlineMap, this.Name)
	this.server.mapLock.Unlock()
	this.server.BroadCast(this, "已下线")
}
func (this *User) SendMsg(msg string) {
	this.conn.Write([]byte(msg))
}
func (this *User) DoMessage(msg string) {

	if msg == "who" {
		this.server.mapLock.Lock()
		for _, cli := range (this.server.OnlineMap) {
			onlineMsg := "[" + cli.Addr + "]" + cli.Name + "在线...\n"
			this.SendMsg(onlineMsg)
		}
		this.server.mapLock.Unlock()
	}else if len(msg)>7&&msg[:7]=="rename|" {
		newName:=strings.Split(msg,"|")[1]
		_,ok:=this.server.OnlineMap[newName]
		if ok{
			this.SendMsg("当前用户名被使用\n") 
		}else {
			this.server.mapLock.Lock()
			delete(this.server.OnlineMap,this.Name)
			this.server.OnlineMap[newName]=this
			this.server.mapLock.Unlock()
			this.Name=newName
			this.SendMsg("您已经更新用户名:"+this.Name+"\n")
		}
	} else {
		this.server.BroadCast(this, msg)
	}
}

func (this *User) LitenMessage() {
	for {
		msg := <-this.C
		this.conn.Write([]byte(msg + "\n"))
	}
}

7.超时强踢功能

  • server.go
    1. ⽤户的任意消息表示⽤户为活跃,⻓时间不发消息认为超时,就要强制关闭⽤户连接。
    2. 在⽤户的Hander() goroutine 中,添加⽤户活跃channel,⼀旦有消息,就向该channel发送数据
    3. 在⽤户的Hander() goroutine 中,添加定时器功能,超时则强踢

server.go

package main

import (
	"fmt"
	"io"
	"net"
	"sync"
	"time"
)

type Server struct {
	Ip   string
	Port int

	//在线用户的列表
	OnlineMap map[string]*User
	mapLock   sync.RWMutex

	//消息广播的channel
	Message chan string
}

//创建一个server的接口
func NewServer(ip string, port int) *Server {
	server := &Server{
		Ip:        ip,
		Port:      port,
		OnlineMap: make(map[string]*User),
		Message:   make(chan string),
	}

	return server
}

//监听Message广播消息channel的goroutine,一旦有消息就发送给全部的在线User
func (this *Server) ListenMessager() {
	for {
		msg := <-this.Message

		//将msg发送给全部的在线User
		this.mapLock.Lock()
		for _, cli := range this.OnlineMap {
			cli.C <- msg
		}
		this.mapLock.Unlock()
	}
}

//广播消息的方法
func (this *Server) BroadCast(user *User, msg string) {
	sendMsg := "[" + user.Addr + "]" + user.Name + ":" + msg

	this.Message <- sendMsg
}

func (this *Server) Handler(conn net.Conn) {
	//...当前链接的业务
	//fmt.Println("链接建立成功")

	user := NewUser(conn, this)

	user.Online()

	//监听用户是否活跃的channel
	isLive := make(chan bool)

	//接受客户端发送的消息
	go func() {
		buf := make([]byte, 4096)
		for {
			n, err := conn.Read(buf)
			if n == 0 {
				user.Offline()
				return
			}

			if err != nil && err != io.EOF {
				fmt.Println("Conn Read err:", err)
				return
			}

			//提取用户的消息(去除'\n')
			msg := string(buf[:n-1])

			//用户针对msg进行消息处理
			user.DoMessage(msg)

			//用户的任意消息,代表当前用户是一个活跃的
			isLive <- true
		}
	}()

	//当前handler阻塞
	for {
		select {
		case <-isLive:
			//当前用户是活跃的,应该重置定时器
			//不做任何事情,为了激活select,更新下面的定时器

		case <-time.After(time.Second * 10):
			//已经超时
			//将当前的User强制的关闭

			user.SendMsg("你被踢了")

			//销毁用的资源
			close(user.C)

			//关闭连接
			conn.Close()

			//退出当前Handler
			return //runtime.Goexit()
		}
	}
}

//启动服务器的接口
func (this *Server) Start() {
	//socket listen
	listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", this.Ip, this.Port))
	if err != nil {
		fmt.Println("net.Listen err:", err)
		return
	}
	//close listen socket
	defer listener.Close()

	//启动监听Message的goroutine
	go this.ListenMessager()

	for {
		//accept
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println("listener accept err:", err)
			continue
		}

		//do handler
		go this.Handler(conn)
	}
}

8.私聊功能

  • user.go
    1. 消息格式: “To|张三|你好啊,我是。。”
    2. 在DoMessage()⽅法中,加上对“to|张三|你好啊,我是...”指令的处理,返回在线⽤户信息

user.go

package main
type User struct{
    Name string
    Addr string
    C chan string
    conn net.Conn
    server *Server
}
func NewUser(conn net.Conn,server *Server){
    userAddr :=conn.RemoteAddr().String()
    user:=&User{
        Name:userAddr,
        Addr:userAddr,
        C:make (chan string),
        conn:conn,
        server:server,
    }
    go user.ListenMessage()
    return user
}
func (this *User)Online(){
    this.server.MapLock.Lock()
    this.server.OnlineMap[this.Name]=this
    this.server.MapLock.Unlock()
    this.server.BroadCast(this+"已上线!")
}
func (this *User)Offline(){
	//用户下线,将用户从onlineMap中删除
	this.server.mapLock.Lock()
	delete(this.server.OnlineMap, this.Name)
	this.server.mapLock.Unlock()

	//广播当前用户上线消息
	this.server.BroadCast(this, "下线")
}
func (this *User)SendMsg(msg string){
    this.conn.Write([]byte(msg))
}
func (this *User)DoMessage(msg string){
    	if msg == "who" {
		//查询当前在线用户都有哪些

		this.server.mapLock.Lock()
		for _, user := range this.server.OnlineMap {
			onlineMsg := "[" + user.Addr + "]" + user.Name + ":" + "在线...\n"
			this.SendMsg(onlineMsg)
		}
		this.server.mapLock.Unlock()

	} else if len(msg) > 7 && msg[:7] == "rename|" {
		//消息格式: rename|张三
		newName := strings.Split(msg, "|")[1]

		//判断name是否存在
		_, ok := this.server.OnlineMap[newName]
		if ok {
			this.SendMsg("当前用户名被使用\n")
		} else {
			this.server.mapLock.Lock()
			delete(this.server.OnlineMap, this.Name)
			this.server.OnlineMap[newName] = this
			this.server.mapLock.Unlock()

			this.Name = newName
			this.SendMsg("您已经更新用户名:" + this.Name + "\n")
		}

	} else if len(msg) > 4 && msg[:3] == "to|" {
		//消息格式:  to|张三|消息内容

		//1 获取对方的用户名
		remoteName := strings.Split(msg, "|")[1]
		if remoteName == "" {
			this.SendMsg("消息格式不正确,请使用 \"to|张三|你好啊\"格式。\n")
			return
		}

		//2 根据用户名 得到对方User对象
		remoteUser, ok := this.server.OnlineMap[remoteName]
		if !ok {
			this.SendMsg("该用户名不不存在\n")
			return
		}

		//3 获取消息内容,通过对方的User对象将消息内容发送过去
		content := strings.Split(msg, "|")[2]
		if content == "" {
			this.SendMsg("无消息内容,请重发\n")
			return
		}
		remoteUser.SendMsg(this.Name + "对您说:" + content)

	} else {
		this.server.BroadCast(this, msg)
	}
}
func (this *User)ListenMessage(){
    for {
        msg:=<-this.C
        this.conn.Write([byte(msg+"\n")])
    }
}

9.客户端实现(建立连接)

  • Client.go
    1. 创建client结构体,结构体包括ServerIp,ServerPort,Name和conn
    2. 创建NewClient方法
package main
import (
	"fmt"
	"net"
)
type Client struct {
    ServerIp string
    ServerPort int
    Name string
    conn net.Conn
}
func NewClient(serverIp string ,ServerPort int)*Client{
    client:=&Client{
        ServerIp:serverIp,
        ServerPort :serverPort,
    }
    conn,err:=net.Dial("tcp",fmt.Sprintf("%s:%d",serverIp,serverPort))
    if err!=nil{
        fmt.Println("net.Dial Error:",err)
        return nil
    }
    client.conn=conn
    return client
}
func main(){
    client:=NewClient("127.0.0.1",8888)
    if client==nil{
        fmt.Println(">>>>Server Connect Failed")
    }else{
        fmt.Println(">>>>Server Connect Success")
    }
    select{}
}

10.客户端实现(命令行解析)

  • client.go
    1. 添加init函数,使用flag

client.go

package main

type Client struct{
    ServerIp string
    ServerPort int
    Name string
    conn net.Conn
    flag int
}

func NewClient(serverIp string,serverPort int)*Client{
    client:=&Client{
        ServerIp :serverIp,
        ServerPort:serverPort,
        flag:999,
    }
    conn,err:=net.Dial("tcp",fmt.Sprintf("%s:%d",serverIp,serverPort))
    if err!=nil{
        fmt.Println("net.Dial Error:",err)
        return nil
    }
    client.conn=conn
    return client
    
}
var serverIp string
var serverPort int

func init(){
    flag.StringVar(&serverIp,"ip","127.0.0.1","设置服务器IP地址(默认是127.0.0.1)")
    flag.IntVar(&serverPort,"port",8888,"设置服务器端口(默认是8888)")
    
}
func main(){
    flag.Parse()
    client:=NewClient(serverIp,serverPort)
    if client ==nil{
        fmt.Println(">>>>链接服务器失败...")
        return
    }
    fmt.Println(">>>>>链接服务器成功...")
}

11.客户端实现(菜单显示)

  • client.go
    1. 新增menu函数

clien.go

package main

type Client struct {
   	ServerIp string
    ServerPort int
    Name string
    conn net.Conn
    flag int
}
func NewClient(serverIp string ,serverPort int)*Client{
    client:=Client{
        ServerIp:serverIp,
        ServerPort :serverPort,
        flag:999,
    }
    conn,err:=net.Dial("tcp",fmt.Sprintf("%s:%d",serverIp,serverPort))
    if err!=nil{
        fmt.Println("net.Dial error:",err)
        return nil
    }
    client.conn=conn
    return client
}
var serverIp string
var serverPort int
func init(){
    flag.StringVar(&serverIp,"ip","127.0.0.1","设置服务器IP地址(默认127.0.0.1)")
    flag.IntVar(&serverPort,"port",8888,"设置服务器端口(默认8888)")
}
func main(){
    flag.Parse()
    client:=NewClient(serverIp,serverPort)
    if client==nil{
        fmt.Println(">>>>链接服务器失败...")
        return
    }
    fmt.Println(">>>>>链接服务器成功...")
}

12.客户端实现(查修在线用户功能、私聊功能、公聊功能、更新用户名实现)

  • client.go
    1. 添加DealResponse函数将server返回的信息打印输出
    2. 添加SelectUser函数,给server发送消息“who”,server返回在线用户列表
    3. 添加PrivateChat函数
    4. 添加PublicChat函数
    5. 添加UpdateName函数
    6. 添加Run函数,使用case语句让用户选择使用模式

client.go

package main

import (
	"flag"
	"fmt"
	"io"
	"net"
	"os"
)

type Client struct {
	ServerIp   string
	ServerPort int
	Name       string
	conn       net.Conn
	flag       int //当前Client的模式
}

func NewClient(serverIp string, serverPort int) *Client {
	client := &Client{
		ServerIp:   serverIp,
		ServerPort: serverPort,
		flag:       999,
	}
	//	链接server
	conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", serverIp, serverPort))
	if err != nil {
		fmt.Println("net.Dial error:", err)
		return nil
	}
	client.conn = conn
	return client
}
func (client *Client) DealResponse() {
	io.Copy(os.Stdout, client.conn)
}
func (client *Client) menu() bool {
	var flag int
	fmt.Println("1.公聊模式")
	fmt.Println("2.私聊模式")
	fmt.Println("3.更新用户名")
	fmt.Println("0.退出")
	fmt.Scanln(&flag)
	if flag >= 0 && flag <= 3 {
		client.flag = flag
		return true
	} else {
		fmt.Println(">>>>请输入合法范围内的数字<<<<<")
		return false
	}
}

// 查询在线用户
func (client *Client) SelectUser() {
	sendMsg := "who\n"
	_, err := client.conn.Write([]byte(sendMsg))
	if err != nil {
		fmt.Println("conn write err:", err)
		return
	}
}

// 私聊模式
func (client *Client) PrivateChat() {
	var remoteName string
	var chatMsg string
	client.SelectUser()
	fmt.Println(">>>>请输入聊天对象[用户名],exit退出:")
	fmt.Scanln(&remoteName)
	for remoteName != "exit" {
		fmt.Println(">>>>>请输入聊天消息内容,exit退出:")
		fmt.Scanln(&chatMsg)
		for chatMsg != "exit" {
			//	消息不为空则发送
			if len(chatMsg) != 0 {
				sendMsg := "to|" + remoteName + "|" + chatMsg + "\n\n"
				_, err := client.conn.Write([]byte(sendMsg))
				if err != nil {
					fmt.Println("conn write err:", err)
					break
				}
			}
			chatMsg = ""
			fmt.Println(">>>>>请输入消息内容,exit退出:")
			fmt.Scanln(&chatMsg)
		}
		client.SelectUser()
		fmt.Println(">>>>请输入聊天对象[用户名],exit退出:")
		fmt.Scanln(&remoteName)
	}
}

// 公聊模式
func (client *Client) PublicChat() {
	var chatMsg string
	fmt.Println("请输入消息内容,exit退出:")
	fmt.Scanln(&chatMsg)
	for chatMsg != "exit" {
		if len(chatMsg) != 0 {
			_, err := client.conn.Write([]byte(chatMsg + "\n"))
			if err != nil {
				fmt.Println("conn write err:", err)
				break
			}
		}
		fmt.Println(">>>>>>请输入消息内容,exit退出:")
		fmt.Scanln(&chatMsg)
	}
}

// 更新用户名
func (client *Client) UpdateName() bool {
	fmt.Println(">>>>请输入新的用户名,exit退出:")
	fmt.Scanln(&client.Name)
	sendMsg := "rename|" + client.Name + "\n"
	if client.Name != "exit" {
		_, err := client.conn.Write([]byte(sendMsg))
		if err != nil {
			fmt.Println("conn write err:", err)
			return false
		}
	}
	return true
}

var serverIp string
var serverPort int

func (client *Client) Run() {
	for client.flag != 0 {
		for client.menu() != true {
		}
		switch client.flag {
		case 1:
			client.PublicChat()
			break
		case 2:
			client.PrivateChat()
			break
		case 3:
			client.UpdateName()
			break
		}
	}
}
func init() {
	flag.StringVar(&serverIp, "ip", "127.0.0.1", "设置服务器IP地址(默认是127.0.0.1)")
	flag.IntVar(&serverPort, "port", 8888, "设置服务器端口(默认是8888)")
}
func main() {
	flag.Parse()
	client := NewClient(serverIp, serverPort)
	if client == nil {
		fmt.Println(">>>>>链接服务器失败...")
		return
	}
	go client.DealResponse()
	fmt.Println(">>>>>链接服务器成功...")
	client.Run()
}

posted @ 2023-03-25 16:10  Weber·Bon  阅读(141)  评论(0编辑  收藏  举报