golang适合做即时通讯吗?可以的哦!
main.go
packagemain func main() { server := NewServer("127.0.0.1", 8888) server.Start() }
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 * 300): //已经超时 //将当前的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) } }
user.go
package main import ( "net" "strings" ) type User struct { Name string Addr string C chan string conn net.Conn server *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消息的goroutine go user.ListenMessage() return user } //用户的上线业务 func (this *User) Online() { //用户上线,将用户加入到onlineMap中 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, "下线") } //给当前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] = 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) } } //监听当前User channel的 方法,一旦有消息,就直接发送给对端客户端 func (this *User) ListenMessage() { for { msg := <-this.C this.conn.Write([]byte(msg + "\n")) } }
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 } //处理server回应的消息, 直接显示到标准输出即可 func (client *Client) DealResponse() { //一旦client.conn有数据,就直接copy到stdout标准输出上, 永久阻塞监听 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) 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 string var chatMsg string client.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 string fmt.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() break case 2: //私聊模式 client.PrivateChat() break case 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() }
更多相关技术内容咨询欢迎前往并持续关注六星社区了解详情。
如果你想用Python开辟副业赚钱,但不熟悉爬虫与反爬虫技术,没有接单途径,也缺乏兼职经验
关注下方微信公众号:Python编程学习圈,获取价值999元全套Python入门到进阶的学习资料以及教程,还有Python技术交流群一起交流学习哦。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!