2022-7-2 --> 2022-7-4 Go学习 (学习资源不好,未完成)

2022-7-2 --> 2022-7-4 Go学习

Go的编译

  1. 编译指令 : 二进制码文件
go build -o 文件名.exe 文件名.go   
  1. 编译的好处:
    • 让程序运行更快
    • 在没有go的环境里依然可以运行
    • 帮助排错
  2. 编译的坏处:
    • 使文件变得更大
  3. tree查看文件结构

GO常用转意字符

  1. \t :一个制表位

  2. \n :换行

  3. \ " :"

  4. \ \ :\

  5. \r :回车

package main
import "fmt"
func main(){
	fmt.Println("tom\tjack")	tom     jack
	fmt.Println("tom\rjack")	jack			//tom被覆盖了
	fmt.Println("tom\njack")	tom 
    							jack  
	fmt.Println("tom\\jack")	tom\jack
	fmt.Println("tom\"jack")	tom"jack
}
  • 示例
package main
import "fmt"
func main(){
	fmt.Print("姓名\t年龄\t籍贯\t住址\njack\t12\t河北\t北京")
}
姓名    年龄    籍贯    住址
jack    12     河北    北京

GO的书写规范

  1. 官方推荐使用行注释
  2. 有正确的缩进和空白
  3. 可以使用命令来规范代码
gofmt -w w

GO变量定义的四种方式

//1.无初始值
var a int 
//2.有初始值
var a int = 1
//3.无类型自动匹配
var a = 1
//4.局部可用,屈居不可用
a := 1

Go常量定义方式

//1.单个常量定义方式 
const A = 1
//2.多个常量定义方式
const(
	A = 1
    B = 2
)

iota用法

  • iota 必须配合常量使用,作用是可以在单个常量或多个常量内定义一个自增长的值,初始为0
//1. 单个
const A = iota    //0
const B = iota*2 + 1  //1   单个iota始终为0,故为一
//2.多个
const(
	A = iota    //0
    B			//1
    C = iota*2	//2*2
    D 			//3
)    

Go方法定义 --返回值的个数自选

  1. 返回值匿名
func foo1 (a string,b int)(int, string){
    fmt.Println(a)
    fmt.Println(b)
    return a,"d"
}
  1. 返回值有名
func foo1 (a string,b int) (c int,d int) {
    fmt.Println(a)
    fmt.Println(b)
    c = 100
	d = 100
    return  
}
  1. 返回值同一类型可简写
func foo1 (a string,b int) (c,d int) {
    fmt.Println(a)
    fmt.Println(b)
    c = 100
	d = 100
    return  
}
  1. 无返回值
func foo1 (a string,b int) {
    fmt.Println(a)
    fmt.Println(b)
}
  • 方法名大写是public
  • 方法名小写是private

GO init()

  • init()函数运行在main()函数之前

GO 导包

  1. go导包要注意路径
  2. go正常情况下导包就必须使用,不然报错
  3. 导包又不使用,可以如下操作
import _ "包路径/包名"
  1. 如果包名过长,可起别名
import myLib "包路径/包名"
  1. 如果方法名不重复的情况下,可如下操作
import . "包路径/包名"  //调用方法时可以不写包名,直接调用方法

GO指针

  • 未使用指针
package main 
import "fmt"
func changeValue(p int){
    p = 10
}
func main(){
    var a = 1 
    changeValue(a)
    fmt.Println("a=",a) //1
}

  • 使用指针
package main
import "fmt"
func changeValue(p *int){
    *p = 10
}
func main(){
    var a int = 1
    changeValue(&a)
    fmt.Println("a=",a)
}
  • 个人理解:

    1. 未使用指针传的是变量的内存地址投影,是假身,没有在物理上修改那个值,只是镜中花,水中月
    2. 使用指针传的是真实的内存地址,修改的是真实存在的内存上的值
    3. *代表存储的是地址 &代表取得是地址
  • 一级指针交换值

package main 
import "fmt"
func swap(a *int , b *int){
	var temp int
	temp = *a
	 *a = *b
	 *b = temp
 }
func main(){
	var a int = 10
	var b int = 20
	swap(&a,&b)
	fmt.Println("a=",a," b=",b)
}

defer

  • 延期执行 类似压栈的方式(栈就类似一个有底的细管,内容就想一个个小球,最先进去的,就只能最后出来)
package main
import "fmt"
func main(){
	defer fmt.Println("main end")
	defer fmt.Println("main end1")
	fmt.Println("main:hello go1")
	fmt.Println("main:hello go2")
}
//main:hello go1
//main:hello go2
//main end1
//main end
  • return and defer 测试
package main
import "fmt"
func deferFunc() {
	fmt.Println("defer")
}
func returnFunc() int {
	fmt.Println("return")
	return 1
}
func returnAndDefer() int{
	defer deferFunc()
	return returnFunc()
}
func main(){
	returnAndDefer()
}
//return 
//defer

数组

  • 数组声明方式
package main 
import "fmt"
func main(){
	var myArray [10]int
	for i,j:=0,len(myArray);i<j;i++{
		myArray[i]=i
	}
	fmt.Println(myArray)
}
package main
import "fmt"
func main(){
   myArray1 := [10]int{1,2,3,4,}
	for index,value := range myArray1{
		fmt.Println(index, value)
	}
}

slice

  • 动态数组里的内容是地址,可以修改值,
  1. 定义方式一
package main
import "fmt"
func main(){
	myArray := []int{1,2,3,4,5}
	for index,value := range myArray{
		fmt.Println(index , value)
	}
	fmt.Printf("%T",myArray)   //[]int
    fmt.Printf("len = %d,slice = %v\n" ,len(myArray),myArray)//5 selice =[1 2 3 4 5]
}
package main
import "fmt"
func main(){
	myArray := []int{1,2,3,4,5}
	for _,value := range myArray{  //不用可以下划线匿名
		fmt.Println(value)
	}
	fmt.Printf("%T",myArray)
}
  1. 定义方式二
var selice1 []int  //无值无空间
slice1 = make([]int,3) //开辟空间 0 0 0
slice1[0] = 1   // 1 0 0
  1. 定义方式三
var slice1 []int = make([]int,3) //声明并开辟空间
  1. 定义方式四
slice1 := make([]int,3)
  • 判断是否为空切片
if slice1 == null{
    fmt.Println("空")
}else{
    fmt.Println("不为空")
}

切片追加

var numbers = make([]int, 3 ,5)  //有效元素为 0 0 0 容量为5
fmt.Printf("len = %d, cap = %d,slice = %v\n",len(numbers),cap(numbers),numbers) // 3    5   0 0 0
numbers = append(numbers,1)   //在后面追加一个1 ,0 0 0 1
//如果append追加超出容量,会动态的在创建一个cap容量,cap += cap

切片截取

s := []int{1,2,3} // len 3 cap 3
s1 := s[0:2]  //s1 = 1,2   左闭右开的区间 [0:2) --> 0,1
copy(s2,s)  //拷贝

map

  1. 定义方式一
var myMap map[string]string  //map[key]value
if myMap == null{            //true
    fmt.Println("null")
}
//使用前分配空间
make(map[string]string,10)  //容量为十,满了在开辟,类似slice
myMap["one"] = "Java"
myMap["two"] = "c"
myMap["three"] = "python"
  1. 定义方式二
myMap := make(map[int]string)   //容量可写可不写,看需要
myMap[1] = "Java"
myMap[2] = "c"
myMap[3] = "python"
  1. 定义方式三
myMap := map [string]string {
    "one" : "php"
    "two" : "c++"
    "three": "python"
}

map 的使用方式

package main
import "fmt"
func printMap(cityMap map[string]string){
	for index,value := range cityMap{
		fmt.Println(index,value)
	}
}
func changeMap(cityMap map[string]string){
	cityMap["England"] = "London"
}
func main(){
	cityMap := make(map[string]string)
	//添加
	cityMap["China"] = "beijing"
	cityMap["Japan"] = "Tokyo"
	cityMap["USA"] = "NewYork"
	//遍历
	printMap(cityMap)
	//删除
	delete(cityMap,"China")
	//修改
	cityMap["USA"] = "DC"
	//遍历
	printMap(cityMap)
	//添加
	changeMap(cityMap)
	//遍历
	printMap(cityMap)
}

面向对象

  • 结构
package main
import "fmt"
//声明一种新的数据类型,myint 是int的一个别名
type myint int
//定义一个结构体
type Book struct{
	title string 
	auth  string
}
func changeBook(book Book){
	//传递一个book的副本
	book.title = "666"
}
func changeBooks(book *Book){
	//传递一个book的指针
	book.title = "777"
}
func main(){
	var a myint = 10
	fmt.Println("a=",a)  //a= 10
	fmt.Printf("a is %T \n",a)  //a is main.myint 
	var book Book 
	book.title = "Golang"
	book.auth = "a3"
	fmt.Println(book)  //{Golang a3}
	changeBook(book)
	fmt.Println(book)
	changeBooks(&book) //{777 a3}
	fmt.Println(book)
}

封装

package main 
import "fmt"
//如果类名首字母大写,表示其他包也能够访问
type Hero struct{
	//如果说类的属性首字母大写,表示该属性对外能够访问,否则的话只能够类的内部访问
	Name string 
	Ad int
	Level int
}
func (this Hero) Show(){
	fmt.Println("hero= ",this)
}
func (this Hero) GetName() string{
	return this.Name
}
//无指针无法修改
func (this *Hero) SetName(name string) {
     this.Name = name
}
func main(){
	//创建一个对象
	hero := Hero{Name : "a3" ,Ad : 100 , Level : 12 }
	hero.Show()
	hero.SetName("lisi")
	hero.Show()
}

继承

package main
import "fmt"
type Human struct{
	name string 
	sex string 
}
func (this Human) Eat() {
	fmt.Println("Human.Eat()")
}
func (this Human) Waik(){
	fmt.Println("Human.Waik()")
}
type SuperMan struct{
	Human //SuperMan类继承了Human
	Level int8
}
//重定义父类的方法
func (this SuperMan) Eat(){
	fmt.Println("SuperMan.Eat()")
}
//子类的新方法
func (this SuperMan) Fly() {
	fmt.Println("SuperMan.Fly")
}
func main(){
	h := Human{
		"a3",
		"female",
	}
	h.Eat()
	h.Waik()
	//定义一个子类对象
	s := SuperMan{
		Human{"leisi","nv"},
		12,
	}
	s.Waik()//父类的方法
	s.Eat() //子类的方法
	s.Fly() //子类的方法
}

多态

package main
import "fmt"
//本质是一个指针
type AnimalIF interface{
	Sleep()				//获取动物的
	GetColor()  string  //获取动物的颜色
	GetType()  string  //获取动物的种类
}
//定义一个具体的类
type Cat struct{
	color  string //猫的颜色
}
func (this Cat)Sleep(){
	fmt.Println("Cat is Sleep")
}
func (this Cat) GetColor()string{
	return this.color
}
func (this Cat) GetType()string{
	return  "Cat"
}
//具体的类
type Dog struct{
	color string 
}
func (this Dog)Sleep(){
	fmt.Println("Dog is Sleep")
}
func (this Dog) GetColor()string{
	return this.color
}
func (this Dog) GetType()string{
	return  "Dog"
} 
func showAnimal(animalIF AnimalIF){
	animalIF.Sleep() //多态
	fmt.Println(animalIF.GetColor())
	fmt.Println(animalIF.GetType())
}
func main(){
	var animalIF AnimalIF //接口的数据类型,父类指针
	animalIF = Cat{"Green"}
	animalIF.Sleep() //调用的是Cat的sleep方法 多态的现象
	animalIF = Dog{"Yello"} 
	animalIF.Sleep() //调用Dog的sleep方法  多态的现象
	cat := Cat{"Green"}
	dog := Dog{"Yello"}
	showAnimal(cat)
	showAnimal(dog)
}

interface 万能类型

package main
import "fmt"
//interface{}是万能类型
func myFunc(arg interface{}){
	fmt.Println("myFunc is called")
	fmt.Println(arg)
	//interface{} 该如何区分 此时引用的底层数据类型到底是什么?
	//interface{} 提供 "断言机制"
	value,ok := arg.(string)
	if !ok {
		fmt.Println("arg is not string type")
	}else {
		fmt.Println("arg is string type value = ",value)
		fmt.Printf("value type is %T \n",value)
	}
}
type Book struct{
	auth string 
}
func main(){
	book := Book{"Golang"}
	myFunc(book)
	myFunc(100)
	myFunc("abc")
	myFunc(3.14)
}

pari

package main 
import "fmt"
func main(){
	var a string 
	//pari<typr : string,value : "aceld">
	a = "aceld"
	//pari<type : string,value : "aceld">
	var allType interface{}
	allType = a 
	value ,ok := allType.(string)
	fmt.Println(value,ok) //aceld true
}
package main
import (
	"fmt"
	"io"
	"os"
)
func main() {
	//tty : pari<type : *os.File, "dev/tty">
	tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
	if err != nil {
		fmt.Println("open file error", err)
		return
	}
	//r:pari<type , value>
	var r io.Reader
	//r:pari<type : *os.File,value:"/dev/tty"文件描述符>
	r = tty
	//r:pari<type , value>
	var w io.Writer
	//r:pari<type : *os.File,value:"/dev/tty"文件描述符>
	w = r.(io.Writer)
	w.Write([]byte("HELLO THIS IS A TEST!!!\n"))
}
package main
import "fmt"
type Reader interface {
	ReadBook()
}
type Writer interface {
	WriteBooK()
}
//具体类型
type Book struct {
}
func (this Book) ReadBook() {
	fmt.Println("Read A Book")
}
func (this Book) WriteBooK() {
	fmt.Println("Writer A Book")
}
func main() {
	//b: pari<type : Book ,value : book{}地址>
	b := Book{}
	//r:pari <type,value>
	var r Reader
	//b: pari<type : Book ,value : book{}地址>
	r = b
	r.ReadBook()
	var w Writer
	//b: pari<type : Book ,value : book{}地址>
	w = r.(Writer) //此处的断言为什么会成功?因为w r 具体的type是一致的

	w.WriteBooK()
}

反射

  • reflect
package main
import (
	"fmt"
	"reflect"
)
func reflectNum(arg interface{}) {
	fmt.Println("type :", reflect.TypeOf(arg))   //float64
	fmt.Println("value:", reflect.ValueOf(arg))	 //1.2345
}
func main() {
	var num float64 = 1.2345
	reflectNum(num)
}
package main
import (
	"fmt"
	"reflect"
)
type User struct {
	Id   int
	Name string
	Age  int
}
func (this User) Call() {
	fmt.Println("User is called ..")
	fmt.Printf("%v\n", this)
}
func main() {
	user := User{
		1,
		"Aceld",
		18,
	}
	DoFiledAndMethod(user)
}
func DoFiledAndMethod(input interface{}) {
	//获取input的type
	inputType := reflect.TypeOf(input)
	fmt.Println("inputType= ", inputType.Name()) //inputType=  User
	//获取input的value
	inputValue := reflect.ValueOf(input)
	fmt.Println("inputValue= ", inputValue) //inputValue=  {1 Aceld 18}
	//分别通过type获取里面的字段
	//1.获取interface的reflect.Type,通过Type得到NumField,进行遍历
	//2.得到每个filed,数据类型
	//3.通过filed有一个Interface()方法得到对应的value
	for i := 0; i < inputType.NumField(); i++ {
		field := inputType.Field(i)
		value := inputValue.Field(i).Interface()

		fmt.Printf("%s : %v =%v\n", field.Name, field.Type, value)
	}
	//通过type获取里面的方法调用
	for i := 0 ; i < inputType.NumMethod();i++{
		m := inputType.Method(i)
		fmt.Printf("%s : %v\n",m.Name,m.Type)
	}
}

结构体标签

package main
import (
	"fmt"
	"reflect"
)
type resume struct {
	Name string `info:"name" doc:"我的名字"` //类似comment?
	Sex  string `info:"sex"`
}
func findTag(str interface{}) {
	t := reflect.TypeOf(str).Elem()
	for i := 0; i < t.NumField(); i++ {
		tagstring := t.Field(i).Tag.Get("info")
		tagDoc := t.Field(i).Tag.Get("doc")
		fmt.Println("info", tagstring, "doc", tagDoc)
	}
}
func main() {
	var re resume
	findTag(&re)
}
  • json 编码解码
package main
import (
	"encoding/json"
	"fmt"
)
type Movie struct {
	Title  string   `json:"title"`
	Year   int      `json:"Year"`
	Price  int      `json:"rmb"`
	Actors []string `json:actors`
}
func main() {
	movie := Movie{"喜剧之王", 2000, 10, []string{"星星", "吱吱"}}
	//编码的过程 结构体 ---> json
	jsonStr, err := json.Marshal(movie)
	if err != nil {
		fmt.Println("json marshal error", err)
		return
	}
	//jsonStr = {"title":"喜剧之王","Year":2000,"rmb":10,"Actors":["星星","吱吱"]}
	fmt.Printf("jsonStr = %s\n", jsonStr)
	//解码过程 jsonStr --->结构体
	myMovie := Movie{}
	err = json.Unmarshal(jsonStr,&myMovie)
	if err !=nil{
		fmt.Println("json unmarshal error",err)
		return
	}
	fmt.Printf("%v\n",myMovie)
}

goroutine

package main
import (
	"fmt"
	"time"
)
//子goroutine
func newTask() {
	i := 0
	for {
		i++
		fmt.Printf("new Goroutine : i = %d\n", i)
		time.Sleep(1 * time.Second)
	}
}
//主goroutine
func main() {
	//创建一个go程,去执行newTask()进程
	go newTask()
	i := 0
	for {
		i++
		fmt.Printf("main goroutine: i = %d\n", i)
		time.Sleep(1 * time.Second)
	}
}
package main
import (
	"fmt"
	"time"
)
func main() {
	//用go创建一个形参为空,返回值为空的一个函数
	go func() {
		defer fmt.Println("A.defer")
		//return
		func() {
			defer fmt.Println("B defer")
			//退出当前goroutine
			//runtime.Goexit()
			fmt.Println("B")
		}()
		fmt.Println("A")
	}()
	go func(a int, b int) bool {
		fmt.Println("a = ", a, "b = ", b)
		return true
	}(10, 20)
	//死循环
	for {
		time.Sleep(1 * time.Second)
	}
}

channel

package main
import "fmt"
func main() {
	//定义一个channel
	c := make(chan int)
	go func() {
		defer fmt.Println("goroutine结束")
		fmt.Println("goroutine 正在运行")
		c <- 666 //将666 发送给c
	}()
	num := <-c //从c中接收数据,并赋值给num
	fmt.Println("num = ", num)
	fmt.Println("main goroutine 结束")
}
//goroutine 正在运行
// goroutine结束
// num =  666
// main goroutine 结束
  • 当channel已经满了,在向里面写数据,就会阻塞,当channel为空,从里面取数据也会阻塞
package main
import (
	"fmt"
	"time"
)
func main() {
	c := make(chan int, 3) // 3 代表有缓冲的   
	fmt.Println("len(c) = ", len(c), "cap(c) = ", cap(c))
	go func() {
		defer fmt.Println("子go称结束")
		for i := 0; i < 3; i++ {
			c <- i
			fmt.Println("子go程正在运行,发送的元素= ", i, "len(c) = ", len(c), " cap(c)= ", cap(c))
		}
	}()
	time.Sleep(2 * time.Second)
	for i := 0; i < 3; i++ {
		num := <-c //从c中接收数据,并赋值给num "<-c" 不能有空格
		fmt.Println("num = ", num)
	}
	fmt.Println("main 结束")
}
// len(c) =  0 cap(c) =  3
// 子go程正在运行,发送的元素=  0 len(c) =  0  cap(c)=  3
// 子go程正在运行,发送的元素=  1 len(c) =  1  cap(c)=  3
// 子go程正在运行,发送的元素=  2 len(c) =  2  cap(c)=  3
// 子go称结束
// num =  0
// num =  1
// num =  2
// main 结束

关闭channel

  1. channel不像文件一样需要经常去关闭,只有当你确实没有任何发送数据了,或者你想显示的结束range循环之类的,才去关闭channel
  2. 关闭channel后,无法向channel在发送数据(引发 panic 错误后导致接收立即返回零值)
  3. 关闭channel后,可以继续从channel接收数据
  4. 对于 nil channel,无论收发都会被阻塞
package main
import "fmt"
func main() {
	c := make(chan int)
	go func() {
		for i := 0; i < 5; i++ {
			c <- i
		}
		//close 可以关闭一个channel
		close(c)
	}()
	for {
		//ok如果为true表示channel没有关闭,如果为false表示channel已经关闭
		if data, ok := <-c; ok {
			fmt.Println(data)
		}else{
			break
		}
	}
	fmt.Println("main Finished..")
}
// 0
// 1
// 2
// 3
// 4
// main Finished..

channel and range

package main
import "fmt"
func main() {
	c := make(chan int)
	go func() {
		for i := 0; i < 5; i++ {
			c <- i
		}
		//close关闭一个channel
		close(c)
	}()
	//可以使用range来迭代不断操作channel,简写上一个案例 
	for data := range c {
		fmt.Println(data)
	}
	fmt.Println("main Finished..")
}

channel and select

  • select 具备多路channel的监控状态功能
package main
import "fmt"
func fibonacii(c, quit chan int) {
	x, y := 1, 1
	for {
		select {
		case c <-x:
			//如果c可写,则该case就会进来
			x = y
			y = x + y
		case <-quit:
			fmt.Println(quit)
			return
		}
	}
}
func main() {
	c := make(chan int)
	quit := make(chan int)
	//sub go
	go func() {
		for i := 0; i < 10; i++ {
			fmt.Println(<-c)
		}
		quit <- 0
	}()
	//main go
	fibonacii(c, quit)
}
//1
//1
//2
//4
//8
//16
//32
//64
//128
//256
//0xc00001c120

即时通信系统

1.0 基础server构建

package main
//telnet IP 端口 #连接server
//CTRL+] #进入输入界面
//send 内容 #发送字符串
func main() {
	server := NewServer("127.0.0.1", 8888)
	server.Start()
}
package main
import (
	"fmt"
	"net"
)
//ip 和 端口
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)
	}
}

2.0 用户上线及广播功能

package main
import "net"
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"))
	}
}
package main
import (
	"fmt"
	"net"
	"sync"
)
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)
	//用户上线,将用户加入到onlineMap中
	this.mapLock.Lock()
	this.OnlineMap[user.Name] = user
	this.mapLock.Unlock()
	//广播当前用户上线消息
	this.BroadCast(user, "已上线")
	//当前handler阻塞
	select {}
}
//启动服务器的接口
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)
	}
}

用户消息广播机制

posted @   李光宇  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
· 零经验选手,Compose 一天开发一款小游戏!
点击右上角即可分享
微信分享提示