基于Go写一个极简的通讯demo
window环境
客户端构建:go build -o client.exe .\client.go
服务端构建:go build - go build -o server.exe .\main.go .\server.go .\User.go
定义服务端
Server具体实现
结构体
type Server struct { //服务端的IP地址 Ip string //服务端的IP端口 Port int // 在线用户 OnlineUserMap map[string]*User // 在线用户map的同步锁 mapLock sync.RWMutex // 消息广播channel Message chan string}创建Server接口
func NewServer(ip string, port int) *Server { server := &Server{ Ip: ip, Port: port, OnlineUserMap: make(map[string]*User), Message: make(chan string), } return server}监听用户上线
// BroadCast 服务端发送广播消息func (s *Server) BroadCast(user *User, msg string) { sendMsg := "[" + user.Addr + "]" + user.Name + ":" + msg s.Message <- sendMsg}
// ListenMessage 在线用户监听器func (s *Server) ListenMessage() { for { msg := <-s.Message s.mapLock.Lock() for _, cli := range s.OnlineUserMap { cli.C <- msg } s.mapLock.Unlock() }}服务端对用户消息的处理器
// Handler handler处理func (s *Server) Handler(conn net.Conn) { fmt.Print("链接建立成功") // 加入在线用户集 user := NewUser(conn, s) user.Online() fmt.Println("[" + user.Addr + "]" + user.Name + ": online...") isLive := make(chan bool) // 广播用户发送信息 go func() { buf := make([]byte, 4096) for { n, err := conn.Read(buf) if n == 0 { user.Offline() fmt.Println("[" + user.Addr + "]" + user.Name + ": offline...") return } if err != nil && err != io.EOF { fmt.Println("conn Read err:", err) return } fmt.Println("[" + user.Addr + "]" + user.Name + ":" + string(buf)) // 提取用户信息 去除 '\n' msg := string(buf[:n-1]) user.DoMessage(msg) } }() // 超时强制退出监听检测处理 for { select { case <-isLive: case <-time.After(time.Second * 60): user.DoMessage("You have been forced offline\n") fmt.Println("[" + user.Addr + "]" + user.Name + ": have been forced offline") close(user.C) err := conn.Close() if err != nil { return } } }}服务端启动器
func (s *Server) Start() { listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", s.Ip, s.Port)) if err != nil { fmt.Println("net.listener err:", err) return } defer func(listener net.Listener) { err := listener.Close() if err != nil { fmt.Println("net.listener err:", err) } }(listener) // 启动在线用户监听 go s.ListenMessage() fmt.Println("start success....") for { // accept conn, err := listener.Accept() if err != nil { fmt.Println("listener Accept err:", err) continue } // handler go s.Handler(conn) }}主函数启动
package main
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 // 在线用户 OnlineUserMap map[string]*User mapLock sync.RWMutex // 消息广播channel Message chan string}
// NewServer 创建Server接口func NewServer(ip string, port int) *Server { server := &Server{ Ip: ip, Port: port, OnlineUserMap: make(map[string]*User), Message: make(chan string), } return server}
func (s *Server) BroadCast(user *User, msg string) { sendMsg := "[" + user.Addr + "]" + user.Name + ":" + msg s.Message <- sendMsg}
func (s *Server) ListenMessage() { for { msg := <-s.Message s.mapLock.Lock() for _, cli := range s.OnlineUserMap { cli.C <- msg } s.mapLock.Unlock() }
}
// Handler handler处理func (s *Server) Handler(conn net.Conn) { fmt.Print("链接建立成功") // 加入在线用户集 user := NewUser(conn, s) user.Online() fmt.Println("[" + user.Addr + "]" + user.Name + ": online...") isLive := make(chan bool) // 广播用户发送信息 go func() { buf := make([]byte, 4096) for { n, err := conn.Read(buf) if n == 0 { user.Offline() fmt.Println("[" + user.Addr + "]" + user.Name + ": offline...") return } if err != nil && err != io.EOF { fmt.Println("conn Read err:", err) return } fmt.Println("[" + user.Addr + "]" + user.Name + ":" + string(buf)) // 提取用户信息 去除 '\n' msg := string(buf[:n-1]) user.DoMessage(msg) } }() // 超时强制退出监听检测处理 for { select { case <-isLive: case <-time.After(time.Second * 60): user.DoMessage("You have been forced offline\n") fmt.Println("[" + user.Addr + "]" + user.Name + ": have been forced offline") close(user.C) err := conn.Close() if err != nil { return } } }}
// Start 启动服务器接口func (s *Server) Start() { listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", s.Ip, s.Port)) if err != nil { fmt.Println("net.listener err:", err) return } defer func(listener net.Listener) { err := listener.Close() if err != nil { fmt.Println("net.listener err:", err) } }(listener) // 启动在线用户监听 go s.ListenMessage() fmt.Println("start success....") for { // accept conn, err := listener.Accept() if err != nil { fmt.Println("listener Accept err:", err) continue } // handler go s.Handler(conn) }}User具体实现
用户结构体
type User struct { // 用户名 Name string // 用户IP地址 Addr string // chan C chan string // 链接 conn net.Conn // 所属的服务 server *Server}创建一个User
func NewUser(c net.Conn, server *Server) *User { userAddr := c.RemoteAddr().String() user := &User{ Name: userAddr, Addr: userAddr, C: make(chan string), conn: c,
server: server, } go user.ListenMessage() return user}监听用户状态
func (u *User) ListenMessage() { for { msg := <-u.C _, err := u.conn.Write([]byte(msg + "\n")) if err != nil { fmt.Println("write err:", err) return } }}
// SendMsg 给客户端发送消息func (u *User) SendMsg(msg string) { _, err := u.conn.Write([]byte(msg)) if err != nil { fmt.Println("send Msg err:", err) }}具体实现用户的上下线
// Online 广播用户上线通知func (u *User) Online() { u.server.mapLock.Lock() u.server.OnlineUserMap[u.Name] = u u.server.mapLock.Unlock() u.server.BroadCast(u, "online...")}
// Offline 广播用户下线通知func (u *User) Offline() { u.server.mapLock.Lock() delete(u.server.OnlineUserMap, u.Name) u.server.mapLock.Unlock() u.server.BroadCast(u, "offline...")}解析用户的消息
func (u *User) DoMessage(message string) { if len(message) > 0 { msg := strings.Split(message, "|") if len(msg) > 1 { switch msg[0] { case "rename": newName := msg[1] _, ok := u.server.OnlineUserMap[newName] if ok { u.SendMsg("this name is already used\n") return } u.server.mapLock.Lock() u.server.OnlineUserMap[newName] = u delete(u.server.OnlineUserMap, u.Name) u.server.mapLock.Unlock() u.Name = newName u.SendMsg("update newName success\n") break case "to": // to|用户名|消息 if len(msg) < 3 { u.server.BroadCast(u, "Command format error\n") return } remoteUer, ok := u.server.OnlineUserMap[msg[1]] if !ok { u.server.BroadCast(u, msg[1]+" not found\n") return } remoteUer.SendMsg(msg[2]) } } else { switch message { case "who": u.server.mapLock.Lock() for _, user := range u.server.OnlineUserMap { onlineMsg := "[" + user.Addr + "]" + user.Name + ":online\n" u.SendMsg(onlineMsg) } u.server.mapLock.Unlock() break default: u.server.BroadCast(u, message) } } }}完整代码
package main
import ( "fmt" "net" "strings")
type User struct { Name string Addr string C chan string conn net.Conn
server *Server}
func NewUser(c net.Conn, server *Server) *User { userAddr := c.RemoteAddr().String() user := &User{ Name: userAddr, Addr: userAddr, C: make(chan string), conn: c,
server: server, } go user.ListenMessage() return user}
// Online 广播用户上线通知func (u *User) Online() { u.server.mapLock.Lock() u.server.OnlineUserMap[u.Name] = u u.server.mapLock.Unlock() u.server.BroadCast(u, "online...")}
// Offline 广播用户下线通知func (u *User) Offline() { u.server.mapLock.Lock() delete(u.server.OnlineUserMap, u.Name) u.server.mapLock.Unlock() u.server.BroadCast(u, "offline...")}
func (u *User) DoMessage(message string) { if len(message) > 0 { msg := strings.Split(message, "|") if len(msg) > 1 { switch msg[0] { case "rename": newName := msg[1] _, ok := u.server.OnlineUserMap[newName] if ok { u.SendMsg("this name is already used\n") return } u.server.mapLock.Lock() u.server.OnlineUserMap[newName] = u delete(u.server.OnlineUserMap, u.Name) u.server.mapLock.Unlock() u.Name = newName u.SendMsg("update newName success\n") break case "to": // to|用户名|消息 if len(msg) < 3 { u.server.BroadCast(u, "Command format error\n") return } remoteUer, ok := u.server.OnlineUserMap[msg[1]] if !ok { u.server.BroadCast(u, msg[1]+" not found\n") return } remoteUer.SendMsg(msg[2]) } } else { switch message { case "who": u.server.mapLock.Lock() for _, user := range u.server.OnlineUserMap { onlineMsg := "[" + user.Addr + "]" + user.Name + ":online\n" u.SendMsg(onlineMsg) } u.server.mapLock.Unlock() break default: u.server.BroadCast(u, message) } } }}
func (u *User) ListenMessage() { for { msg := <-u.C _, err := u.conn.Write([]byte(msg + "\n")) if err != nil { fmt.Println("write err:", err) return } }}
// SendMsg 给客户端发送消息func (u *User) SendMsg(msg string) { _, err := u.conn.Write([]byte(msg)) if err != nil { fmt.Println("send Msg err:", err) }}定义客户端
初始化客户端
客户端结构体
type Client struct { // 服务端ip ServerIp string // 服务端端口 ServerPort int // 客户端名 Name string // 客户端管道 conn net.Conn // 客户端模式 flag int}// NewClient 创建一个监听func NewClient(serverIp string, serverPort int) *Client { client := &Client{ ServerIp: serverIp, ServerPort: serverPort, flag: 999, } // 建立tcp长链接 conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", client.ServerIp, client.ServerPort)) if err != nil { fmt.Println("dial err:", err) return nil } client.conn = conn return client}通过参数命令来定义并启动客户端
func init() { // 初始化配置,通过 -ip设置IP地址 flag.StringVar(&serverIp, "i", "127.0.0.1", "设置服务器的IP地址(默认127.0.0.1)") // 初始化配置,通过 -p设置IP端口 flag.IntVar(&serverPort, "p", 8888, "设置服务器的端口(默认8888)")}
// DealResponse 监听服务端返回的消息func (client *Client) DealResponse() { _, err := io.Copy(os.Stdout, client.conn) if err != nil { return }}
// 具体业务的实现func (client *Client) Run() {
}
func main() { // 命令行解析 flag.Parse() client := NewClient(serverIp, serverPort) if client == nil { fmt.Println(">>> 链接服务器失败") return } go client.DealResponse() fmt.Println(">>> 链接服务器成功") client.Run()
}客户端封装功能
封装修改用户名
// 包装更新用户名请求func (client *Client) updateUserName() bool { fmt.Println(">>> 请输入用户名") _, err := fmt.Scanln(&client.Name) if err != nil { return false } 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) listUser() bool { sendMsg := "who\n" _, err := client.conn.Write([]byte(sendMsg)) if err != nil { fmt.Println(">>> conn write err:", err) return false } return true}封装广播聊天
// PublicChat 公聊模式func (client *Client) publicChat() { var chatMsg string fmt.Println(">>> 请输入发送内容,输入exit退出") _, err := fmt.Scanln(&chatMsg) if err != nil { return } 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 } } // 继续监听下一个发送信息,直到exit chatMsg = "" fmt.Println(">>> 请输入发送内容,输入exit退出") _, err := fmt.Scanln(&chatMsg) if err != nil { return } }}封装私聊
// PrivateChat 私聊模式func (client *Client) privateChat() { var remote, chatMsg string client.listUser() for remote != "exit" { fmt.Println(">>> 请输入发送对象,输入exit退出") _, err := fmt.Scanln(&remote) if err != nil { return } for chatMsg != "exit" { if len(chatMsg) != 0 { // 发送消息到服务端 sendMsg := "to|" + remote + "|" + chatMsg + "\n" _, err := client.conn.Write([]byte(sendMsg)) if err != nil { fmt.Println(">>> conn write err:", err) break } } // 继续监听下一个发送信息,直到exit chatMsg = "" fmt.Println(">>> 请输入发送内容,输入exit退出") _, err := fmt.Scanln(&chatMsg) if err != nil { return } } // 内循环退出,继续监听输入内容选择用户,知道exit remote = "" fmt.Println(">>> 请输入发送对象,输入exit退出") _, err = fmt.Scanln(&remote) if err != nil { return } }}封装客户端菜单命令
// Menu 命令菜单func (client *Client) Menu() bool { var code int fmt.Println("1: 公聊模式") fmt.Println("2: 私聊模式") fmt.Println("3: 更新用户名") fmt.Println("0: 退出") _, err := fmt.Scanln(&code) if err != nil { return false } if code >= 0 && code <= 3 { client.flag = code return true } return false}调整业务函数
func (client *Client) Run() { for client.flag != 0 { // 过滤非法的菜单code for !client.Menu() { } // 根据菜单code进行功能调度 switch client.flag { case 1: client.publicChat() break case 2: client.privateChat() break case 3: client.updateUserName() break } }}完整代码
package main
import ( "flag" "fmt" "io" "net" "os")
var serverIp stringvar serverPort int
type Client struct { ServerIp string ServerPort int Name string conn net.Conn
flag int}
// NewClient 创建一个监听func NewClient(serverIp string, serverPort int) *Client { client := &Client{ ServerIp: serverIp, ServerPort: serverPort, flag: 999, } // 建立tcp长链接 conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", client.ServerIp, client.ServerPort)) if err != nil { fmt.Println("dial err:", err) return nil } client.conn = conn return client}
// Menu 命令菜单func (client *Client) Menu() bool { var code int fmt.Println("1: 公聊模式") fmt.Println("2: 私聊模式") fmt.Println("3: 更新用户名") fmt.Println("0: 退出") _, err := fmt.Scanln(&code) if err != nil { return false } if code >= 0 && code <= 3 { client.flag = code return true } return false}
// 包装更新用户名请求func (client *Client) updateUserName() bool { fmt.Println(">>> 请输入用户名") _, err := fmt.Scanln(&client.Name) if err != nil { return false } 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) listUser() bool { sendMsg := "who\n" _, err := client.conn.Write([]byte(sendMsg)) if err != nil { fmt.Println(">>> conn write err:", err) return false } return true}
// PrivateChat 私聊模式func (client *Client) privateChat() { var remote, chatMsg string client.listUser() for remote != "exit" { fmt.Println(">>> 请输入发送对象,输入exit退出") _, err := fmt.Scanln(&remote) if err != nil { return } for chatMsg != "exit" { if len(chatMsg) != 0 { // 发送消息到服务端 sendMsg := "to|" + remote + "|" + chatMsg + "\n" _, err := client.conn.Write([]byte(sendMsg)) if err != nil { fmt.Println(">>> conn write err:", err) break } } // 继续监听下一个发送信息,直到exit chatMsg = "" fmt.Println(">>> 请输入发送内容,输入exit退出") _, err := fmt.Scanln(&chatMsg) if err != nil { return } } // 内循环退出,继续监听输入内容选择用户,知道exit remote = "" fmt.Println(">>> 请输入发送对象,输入exit退出") _, err = fmt.Scanln(&remote) if err != nil { return } }}
// PublicChat 公聊模式func (client *Client) publicChat() { var chatMsg string fmt.Println(">>> 请输入发送内容,输入exit退出") _, err := fmt.Scanln(&chatMsg) if err != nil { return } 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 } } // 继续监听下一个发送信息,直到exit chatMsg = "" fmt.Println(">>> 请输入发送内容,输入exit退出") _, err := fmt.Scanln(&chatMsg) if err != nil { return } }}
func (client *Client) Run() { for client.flag != 0 { // 过滤非法的菜单code for !client.Menu() { } // 根据菜单code进行功能调度 switch client.flag { case 1: client.publicChat() break case 2: client.privateChat() break case 3: client.updateUserName() break } }}
func init() { // 初始化配置,通过 -ip设置IP地址 flag.StringVar(&serverIp, "i", "127.0.0.1", "设置服务器的IP地址(默认127.0.0.1)") // 初始化配置,通过 -p设置IP端口 flag.IntVar(&serverPort, "p", 8888, "设置服务器的端口(默认8888)")}
func (client *Client) DealResponse() { _, err := io.Copy(os.Stdout, client.conn) if err != nil { return }}
func main() { // 命令行解析 flag.Parse() client := NewClient(serverIp, serverPort) if client == nil { fmt.Println(">>> 链接服务器失败") return } go client.DealResponse() fmt.Println(">>> 链接服务器成功") client.Run()
}