IMv9.0版本总结[服务端+客户端],最终版本

编程入门 行业动态 更新时间:2024-10-09 08:26:52

IMv9.0<a href=https://www.elefans.com/category/jswz/34/1771446.html style=版本总结[服务端+客户端],最终版本"/>

IMv9.0版本总结[服务端+客户端],最终版本

 一、经历的版本

经历了多个版本,基础内容在前面,可以使用之前的基础环境:

v1:

v2:

v3:

v4:

v5:

v6:

v7:


v8:

————————————————
 

 二、简要说明

前面都是服务端的8个版本+msys2[netcat,nc]的方式进行的测试,下面就简单的客户端+最终的服务端进行。

三、客户端

package mainimport ("flag""fmt""io""net""os"
)type Client struct {ServerIp   stringServerPort intName       stringconn       net.Connflag       int //当前client的模式
}func NewClient(serverIp string, serverPort int) *Client {//创建客户端对象client := &Client{ServerIp:   serverIp,ServerPort: serverPort,flag:       999,}//链接serverconn, 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
}// 处理server回应的消息, 直接显示到标准输出即可
func (client *Client) DealResponse() {//一旦client.conn有数据,就直接copy到stdout标准输出上, 永久阻塞监听io.Copy(os.Stdout, client.conn)
}func (client *Client) menu() bool {var flag intfmt.Println("1.公聊模式")fmt.Println("2.私聊模式")fmt.Println("3.更新用户名")fmt.Println("0.退出")fmt.Scanln(&flag)if flag >= 0 && flag <= 3 {client.flag = flagreturn true} else {fmt.Println(">>>>请输入合法范围内的数字<<<<")return false}}// 查询在线用户
func (client *Client) SelectUsers() {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 stringvar chatMsg stringclient.SelectUsers()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.SelectUsers()fmt.Println(">>>>请输入聊天对象[用户名], exit退出:")fmt.Scanln(&remoteName)}
}func (client *Client) PublicChat() {//提示用户输入消息var chatMsg stringfmt.Println(">>>>请输入聊天内容,exit退出.")fmt.Scanln(&chatMsg)for chatMsg != "exit" {//发给服务器//消息不为空则发送if len(chatMsg) != 0 {sendMsg := chatMsg + "\n"_, err := client.conn.Write([]byte(sendMsg))if err != nil {fmt.Println("conn Write err:", err)break}}chatMsg = ""fmt.Println(">>>>请输入聊天内容,exit退出.")fmt.Scanln(&chatMsg)}}func (client *Client) UpdateName() bool {fmt.Println(">>>>请输入用户名:")fmt.Scanln(&client.Name)sendMsg := "rename|" + client.Name + "\n"_, err := client.conn.Write([]byte(sendMsg))if err != nil {fmt.Println("conn.Write err:", err)return false}return true
}func (client *Client) Run() {for client.flag != 0 {for client.menu() != true {}//根据不同的模式处理不同的业务switch client.flag {case 1://公聊模式client.PublicChat()breakcase 2://私聊模式client.PrivateChat()breakcase 3://更新用户名client.UpdateName()break}}
}var serverIp string
var serverPort int// ./client -ip 127.0.0.1 -port 8888
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}//单独开启一个goroutine去处理server的回执消息go client.DealResponse()fmt.Println(">>>>>链接服务器成功...")//启动客户端的业务client.Run()
}

四、服务端

4.1user.go

package mainimport ("net""strings"
)type User struct {Name stringAddr stringC    chan stringconn net.Connserver *Server
}//创建一个用户的API
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,}//启动监听当前user channel消息的goroutinego user.ListenMessage()return user
}//用户的上线业务
func (this *User) Online() {//用户上线,将用户加入到onlineMap中this.server.mapLock.Lock()this.server.OnlineMap[this.Name] = thisthis.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, "下线")}//给当前User对应的客户端发送消息
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] = thisthis.server.mapLock.Unlock()this.Name = newNamethis.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)}
}//监听当前User channel的 方法,一旦有消息,就直接发送给对端客户端
func (this *User) ListenMessage() {for {msg := <-this.Cthis.conn.Write([]byte(msg + "\n"))}
}

4.2server.go

package mainimport ("fmt""io""net""sync""time"
)type Server struct {Ip   stringPort int//在线用户的列表OnlineMap map[string]*UsermapLock   sync.RWMutex//消息广播的channelMessage 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发送给全部的在线Userthis.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 + ":" + msgthis.Message <- sendMsg
}func (this *Server) Handler(conn net.Conn) {//...当前链接的业务//fmt.Println("链接建立成功")user := NewUser(conn, this)user.Online()//监听用户是否活跃的channelisLive := 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 * 300)://已经超时//将当前的User强制的关闭user.SendMsg("你被踢了")//销毁用的资源close(user.C)//关闭连接conn.Close()//退出当前Handlerreturn //runtime.Goexit()}}
}//启动服务器的接口
func (this *Server) Start() {//socket listenlistener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", this.Ip, this.Port))if err != nil {fmt.Println("net.Listen err:", err)return}//close listen socketdefer listener.Close()//启动监听Message的goroutinego this.ListenMessager()for {//acceptconn, err := listener.Accept()if err != nil {fmt.Println("listener accept err:", err)continue}//do handlergo this.Handler(conn)}
}

4.3main.go

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

更多推荐

IMv9.0版本总结[服务端+客户端],最终版本

本文发布于:2024-02-06 10:10:17,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1747971.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:版本   服务端   客户端

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!