【Go-自学版】03-即时通信系统1

1. 基础 server 构建

main.go | server.go

go 复制代码
// main.go
package main

func main() {
	// http://127.0.0.1:8888/ 
	server := NewServer("127.0.0.1", 8888)
	server.Start()
}
go 复制代码
// 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
}

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

// 启动服务器接口
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()
	for {
		// accept
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println("listener.Accept err:", err)
			continue
		}

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

	// close listen socket
}

2. 用户上线及广播功能

main.go | server.go | user.go

go 复制代码
// server.go
type Server struct {
	IP   string
	Port int

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

	// 消息广播
	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
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) {
	// "[user.Addr] user.Name: msg"
	sendMsg := "[" + user.Addr + "] " + user.Name + ": " + msg
	this.Message <- sendMsg
}

// 当前链接的业务
func (this *Server) Handler(conn net.Conn) {
	user := NewUser(conn)

	// 用户上线, 添加到OnlineMap中
	this.mapLock.Lock()
	this.OnlineMap[user.Name] = user
	this.mapLock.Unlock()

	// 广播当前用户上线消息
	this.BroadCast(user, "online now")

	// 接收客户端发送的消息
	go func() {
		buf := make([]byte, 4096)
		for {
			n, err := conn.Read(buf)
			// n = 0 表示对端关闭
			if n == 0 {
				this.BroadCast(user, "offline now")
				return
			}

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

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

			// 将消息进行广播
			this.BroadCast(user, msg)
		}
	}()

}

// 启动服务器接口
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)
	}

	// close listen socket
}
go 复制代码
// user.go
type User struct {
	Name string
	Addr string
	C    chan string
	conn net.Conn
}

// 创建一个用户的API
func NewUser(conn net.Conn) *User {
	userAddr := conn.RemoteAddr().String()

	user := &User{
		Name: userAddr,
		Addr: userAddr,
		C:    make(chan string),
		conn: conn,
	}

	// 启动监听当前user channel消息的 goroutine
	go user.ListenMessage()

	return user
}

// 监听当前User channel,一有消息就发送给对应客户端
func (this *User) ListenMessage() {
	for {
		msg := <-this.C

		this.conn.Write([]byte(msg + "\n"))
	}
}

3. 用户业务封装

main.go | server.go | user.go

go 复制代码
// server.go
type Server struct {
	IP   string
	Port int

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

	// 消息广播
	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
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) {
	// "[user.Addr] user.Name: msg"
	sendMsg := "[" + user.Addr + "] " + user.Name + ": " + msg
	this.Message <- sendMsg
}

// 当前链接的业务
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)
			// n = 0 表示对端关闭
			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])

			// 将消息进行广播
			user.DoMessage(msg)
		}
	}()

}

// 启动服务器接口
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)
	}

	// close listen socket
}
go 复制代码
// user.go
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
}

// 监听当前User channel,一有消息就发送给对应客户端
func (this *User) ListenMessage() {
	for {
		msg := <-this.C

		n, err := this.conn.Write([]byte(msg + "\n"))
		if n == 0 {
			fmt.Println("conn close")
			return
		}
		if err != nil {
			fmt.Println("conn Write error:", err)
			return
		}
	}
}

// 用户上线业务
func (user *User) Online() {
	// 用户上线, 添加到OnlineMap中
	user.server.mapLock.Lock()
	user.server.OnlineMap[user.Name] = user
	user.server.mapLock.Unlock()

	// 广播当前用户上线消息
	user.server.BroadCast(user, "online now")
}

// 用户下线业务
func (user *User) Offline() {
	// 用户下线, 从OnlineMap中删除
	user.server.mapLock.Lock()
	delete(user.server.OnlineMap, user.Name)
	user.server.mapLock.Unlock()

	// 广播当前用户下线消息
	user.server.BroadCast(user, "offline now")
}

// 用户处理消息业务
func (user *User) DoMessage(msg string) {
	user.server.BroadCast(user, msg)
}
相关推荐
玩电脑的辣条哥2 小时前
Python如何播放本地音乐并在web页面播放
开发语言·前端·python
ll7788114 小时前
LeetCode每日精进:20.有效的括号
c语言·开发语言·算法·leetcode·职场和发展
Jackson@ML6 小时前
Python数据可视化简介
开发语言·python·数据可视化
赵琳琅6 小时前
Java语言的云计算
开发语言·后端·golang
lly2024066 小时前
jQuery 杂项方法
开发语言
赵琳琅6 小时前
MDX语言的安全开发
开发语言·后端·golang
开开又心心的学嵌入式7 小时前
C语言——指针进阶应用
c语言·开发语言
开开又心心的学嵌入式7 小时前
C语言——指针基础知识
c语言·开发语言
lonelyhiker7 小时前
javascript的原型链
开发语言·javascript·原型模式
夏梓蕙8 小时前
Elixir语言的软件开发工具
开发语言·后端·golang