8小时快速入门,golang安装学习,有语言基础的快来学习

8小时快速入门,golang安装学习,有语言基础的快来学习,第1张


安装golang(Linux环境)
# 下载linux源码包中文官网地址
https://studygolang.com/dl
# 下载命令直接下载
wget https://studygolang.com/dl/golang/go1.17.2.linux-amd64.tar.gz
# 解压到指定目录
tar -zxf go1.17.2.linux-amd64.tar.gz -C /usr/local
# 配置go的相关路径,在末尾加入以下内容
vim ~/.bashrc

# 设置go语言路径
export GOROOT=/usr/local/go
# 设置工作路径
export GOPATH=$HOME/go
# 设置系统环境变量
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin

# 刷新配置
source ~/.bashrc
# 检查是否安装go成功
go version 或者 go --help

Golang的优势







Golang适合用来做什么(强项)

Go明星作品(成就)

Golang的不足(缺点)

1 - firstGolang
package main    // 程序包名

// 导入包
//import "fmt"
import (
        "fmt"
        "time"
)

func main(){    // { 跟函数名必须同行,否则编译错误
        // 结尾 ; 分号可加可不加,建议不加
        fmt.Println("hello Go!")
        time.Sleep(1 * time.Second)
}

2 - 声明 var
package main

import "fmt"

/*
        四种变量的声明方式
        注意:变量声明必须使用,否则错误
*/

// 声明全局变量 方法一,二,三是可以的
var gA int
var gB int = 10
var gC = 100

// 声明全局变量 方法四,错误:syntax error: non-declaration statement outside function body
// 只能用在函数体内
// gD := 1000
func main(){
        // 方法一:声明一个变量,默认值是0
        var a int
        fmt.Println("a = ",a)
        fmt.Printf("type of a = %T\n",a)

        // 方法二:声明一个变量,初始化一个值
        var b int = 10
        fmt.Println("b = ",b)
        fmt.Printf("type of b = %T\n",b)

        // 方法三:在初始化的时候,可以省去数据类型,通过自动匹配当前的变量的数据类型
        var c = 100
        fmt.Println("c = ",c)
        fmt.Printf("type of c = %T\n",c)

        // 方法四:(常用的方法)省去var关键字,直接自动匹配
        d := 1000
        fmt.Println("d = ",d)
        fmt.Printf("type of d = %T\n",d)

        e := "abcd"
        fmt.Println("e = ",e)
        fmt.Printf("type of e = %T\n",e)

        // 全局变量
        fmt.Println("gA = ",gA)
        fmt.Println("gB = ",gB)
        fmt.Println("gC = ",gC)
        //fmt.Println("gD = ",gD)
        
		// 多个变量声明方式
        var aa,bb int = 10,20
        fmt.Println("aa = ",aa,",bb =",bb)

        var cc,dd = 100,"acds"
        fmt.Println("cc = ",cc,",dd = ",dd)

        var (
                ee int = 100
                ff bool = true
        )
        fmt.Println("ee = ",ee,",ff = ",ff)
}
3 - 常量const.iota
package main

import "fmt"

// 定义枚举类
const (
        // iota 每行会累加1,第一行默认值是0
        BEIJING = iota          // iota = 0, BEIJINGJ = 0
        SHANGHAI                // iota = 1, SHANGHAI = 1
        SHENZHEN                // iota = 2, SHENZHEN = 2
)

const (
        a,b = iota + 1, iota + 2        // iota = 0, a = iota + 1, b = iota + 2, a = 1, b = 2
        c,d                             // iota = 1, c = iota + 1, d = iota + 2, c = 2, d = 3
        e,f                             // iota = 2, e = iota + 1, f = iota + 2, e = 3, f = 4
)

func main(){
        // 常量(只读属性)
        const length = 10
        fmt.Println("length = ",length)

        // length = 100         // 常量是不允许修改的 报错:cannot assign to length

        fmt.Println("BEIJING = ",BEIJING)
        fmt.Println("SHANGHAI = ",SHANGHAI)
        fmt.Println("SHENZHEN = ",SHENZHEN)
        
        fmt.Println("a = ",a,",b = ",b)
        fmt.Println("c = ",c,",d = ",d)
        fmt.Println("e = ",e,",f = ",f)

        // iota 只允许const()使用 报错:undefined: iota
        // var aa = iota
}
4 - 函数func
package main

import "fmt"

// 返回单个返回值,匿名
func f1(a int, b int) int {
        fmt.Println("=====f1=====") 
        fmt.Println("a = ",a,", b = ",b)
        
        //c := 10
        
        return 10
}

// 返回多个返回值,无形参名称
func f2(a int,b string) (int, string) {
        fmt.Println("====f2====")
        fmt.Println("a = ",a,",b = ",b)
        
        return 100, "abc"
}

// 返回多个返回值,有形参名称
func f3(a int,b bool) (r1 int, r2 bool) {
        fmt.Println("====f3====") 
        fmt.Println("a = ",a,",b = ",b)
        
        // 给有名称的返回值赋值 不赋值默认0 false
        r1 = 100 
        r2 = true
        return
}       

// 返回多个返回值,类型相同
func f4(a int, b string) (r1,r2 int) {
        fmt.Println("====f4====")
        fmt.Println("a = ",a,",b = ",b)
        return r1,r2
}

func main(){
        f1 := f1(1,2)
        fmt.Println("f1 = ",f1)
        
        ret1, ret2 := f2(3,"abc")
        fmt.Println("ret1 = ",ret1,",ret2 = ",ret2);
        
        ret3, ret4 := f3(100,false)
        fmt.Println("ret3 = ",ret3,",ret4 = ",ret4)
        
        //前面已定义的变量,重新赋值,需要类型一致
        // 报错:cannot assign int to ret4 (type bool) in multiple assignment
        //ret3,ret4 = f4(101,"aaa")
        
        ret5,ret6 := f4(101,"bbb")
        fmt.Println("ret5 = ",ret5,",ret6 = ",ret6)
} 
5 - init初始化函数和导包方式
# lib1/lib1.go
package lib1    // 包名和文件夹名称一致

import "fmt"

// 首字母大写代表对外开放api 函数
func Lib1test(){
    fmt.Println("lib1==Lib1test==")
}

func init(){
    fmt.Println("lib1====init===")
}

# lib2/lib2.go
package lib2

import "fmt"

func Lib2test(){
    fmt.Println("lib2====Lib2test====")
}

func init(){
    fmt.Println("lib2====init===")
}

# main.go文件
package main

// 注意导包路径是根据工作路径决定,也就是一开始配的 $GOPATH/src/GolangStudy/5-init/lib1
import (
    "GolangStudy/5-init/lib1"
    "GolangStudy/5-init/lib2"
)

func main(){
    lib1.Lib1test()
    lib2.Lib2test()
}


package main

import (
    // 匿名导入
    _ "GolangStudy/5-init/lib1"
    // 别名导入
    //mylib2 "GolangStudy/5-init/lib2"
    // 导入到当前包,慎用,可能会导致方法名冲突
    . "GolangStudy/5-init/lib2"
)

func main(){
    // 当导入包只使用init时,可以采用匿名导入
    // lib1.Lib1test()

    //lib2.Lib2test()

    // 可以使用别名调用
    //mylib2.Lib2test()

    // 导入当前包可直接使用
    Lib2test()
}
6 - 指针pointer
package main

import "fmt"

/*func swap(a int, b int){
    var c int
    c = a
    a = b
    b = c
}*/

func swap(a *int, b *int){
    var c int
    c = *a
    *a = *b
    *b = c
}

func main(){
    a := 10
    b := 20

    //swap(a, b)

    swap(&a, &b)

    fmt.Println("a = ",a,",b = ",b);

    // 一级指针
    var c *int
    c = &a

    fmt.Println(&a);
    fmt.Println(c);

    // 二级指针
    var d **int
    d = &c

    fmt.Println(&c);
    fmt.Println(d);
}

7 - defer关键字
package main

import "fmt"

func deferA(){
    fmt.Println("--A--")
}

func deferB(){
    fmt.Println("--B--")
}

func deferC(){
    fmt.Println("--C--")
}

func main(){
    // 输出: --C--  --B--   --A--
    defer deferA()
    defer deferB()
    defer deferC()

    // 输出: --returnFunc--    --deferFunc--
    deferAndReturn()
}

func deferAndReturn() int {
    defer deferFunc()
    return returnFunc()
}

func deferFunc() int {
    fmt.Println("--deferFunc--")
    return 0
}

func returnFunc() int {
    fmt.Println("--returnFunc--")
    return 0
}

8 - 数组array(slice动态数组)

1、定义数组

package main

import "fmt"

func printArr1(arr [4]int){
    for index,value := range arr {
        fmt.Println("index = ",index,",value = ",value)
    }
    
    arr[1] = 100
}

func printArr2(arr []int){
    for index,value := range arr {
        fmt.Println("index = ",index,",value = ",value)
    }

    arr[1] = 100
}

func main(){
    // 固定长度数组
    var arr1 [10]int
    arr2 := [4]int{1,2,3,4}

    //for i := 0; i < 10; i++{
    for i := 0; i < len(arr1); i++ {
        fmt.Println(arr1[i])
    }

    for index, value := range arr2 {
        fmt.Println("index = ",index,",value = ",value)
    }

    // 查看数据类型 注意:Printf,不是 Println
    fmt.Printf("type of arr1 = %T\n",arr1)
    fmt.Printf("type of arr2 = %T\n",arr2)

    // 不能传长度不一致的数组
    // 报错:cannot use arr1 (type [10]int) as type [4]int in argument to printArr
    //printArr(arr1)

    // 传递的是值
    printArr1(arr2)
    fmt.Println("arr2[1] = ",arr2[1])

    // 不给任何长度,表示动态数组
    arr3 := []int{11,22,33,44}

    // 指针传递
    printArr2(arr3)
    fmt.Println("arr3[1] = ",arr3[1])
}


2、slice声明的几种方式

package main

import "fmt"

func main(){

    //声明slice是一个切片,并且初始化,默认值1,2,3,长度len是3
    //var slice = []int{1,2,3}
    //slice := []int{1,2,3}

    //声明slice是一个切片,没有分配空间
    var slice1 []int
    //此时赋值报错
    //slice1[0] = 100
    //开辟空间再赋值
    //slice1 = make([]int,3)
    //slice1[0] = 100

    //var slice2 []int = make([]int,3)
    //var slice2 = make([]int,3)
    //slice2 := make([]int,3)

    // %v 表示打印详细信息
    fmt.Printf("len = %d, slice = %v\n",len(slice1),slice1)

    //判断slice是否为0
    if slice1 == nil {
        fmt.Println("slice1 是一个空切片")
    }else{
        fmt.Println("slice1 是有空间的")
    }
}


3、追加append

package main

import "fmt"

func main(){
    // 3:长度,5:容量
    var number = make([]int,3,5)

    fmt.Printf("len = %d, cap = %d, slice = %v\n",len(number),cap(number),number)

    //追加元素1
    number = append(number,1)
    fmt.Printf("len = %d, cap = %d, slice = %v\n",len(number),cap(number),number)

    //追加元素2,此时容量已满
    number = append(number,2)
    fmt.Printf("len = %d, cap = %d, slice = %v\n",len(number),cap(number),number)

    //向容量cap已满的slice追加元素3
    number = append(number,3)
    fmt.Printf("len = %d, cap = %d, slice = %v\n",len(number),cap(number),number)

    fmt.Println("=========")

    //切片的扩容机制,append的时候,如果长度增加后超过容量,则将容量增加2倍
    var number2 = make([]int,3)
    fmt.Printf("len = %d, cap = %d, slice = %v\n",len(number2),cap(number2),number2)
    number2 = append(number2,1)
    fmt.Printf("len = %d, cap = %d, slice = %v\n",len(number2),cap(number2),number2)
}



4、截取、拷贝copy

package main

import "fmt"

func main(){
    s := []int{1,2,3}

    // 截取s[1],s[2]不包含s[3],所以s1 = []int{2,3}
    s1 := s[1:3]
    fmt.Printf("s1 = %v\n",s1)  //s1 = [2 3]

    // 截取s[0],s[1],所以s1 = []int{1,2}
    s2 := s[:2]
    fmt.Printf("s2 = %v\n",s2)  //s2 = [1 2]

    // 截取s[0],s[1],s[1],所以s1 = []int{1,2,3}
    s3 := s[0:]
    fmt.Printf("s3 = %v\n",s3)  //s3 = [1 2,3]

    //因为截取是浅拷贝,也就是只拷贝指针地址,所以值都会改变,简称浅拷贝
    s3[1] = 100

    fmt.Printf("s1 = %v\n",s1)
    fmt.Printf("s2 = %v\n",s2)
    fmt.Printf("s3 = %v\n",s3)

    //copy 可以将底层数据的slice一起进行拷贝,简称深拷贝
    s4 := make([]int,3)
    copy(s4,s)
    fmt.Printf("s4 = %v\n",s4)
}

9 - map

1、三种声明方式

package main

import "fmt"

func main(){
    //第一种声明方式

    //声明map1是一种map类型,key是string,value是string
    var map1 map[string]string
    if map1 == nil{
        fmt.Println("map1 是一个空map")
    }

    //分配数据空间
    map1 = make(map[string]string,5)
    map1["name"] = "JackyChen"
    map1["age"] = "18"
    map1["sex"] = "男"
    fmt.Println(map1)

    //第二种声明方式
    map2 := make(map[int]string)
    map2[0] = "JackyChen"
    map2[1] = "18"
    map2[2] = "男"
    fmt.Println(map2)

    //第三种声明方式
    map3 := map[string]string{
        "name" : "JackyChen",
        "age" : "18",
        "sex" : "男",
    }
    fmt.Println(map3)
}


2、添加,修改,删除

package main

import "fmt"

func main(){
    personMap := make(map[string]string)

    fmt.Println("=========添加")
    //添加
    personMap["name"] = "JackyChen"
    personMap["age"] = "18"
    personMap["sex"] = "男"
    printMap(personMap)

    fmt.Println("=========修改")
    //修改
    //personMap["age"] = "25"
    changeMap(personMap)
    printMap(personMap)

    fmt.Println("=========删除")
    //删除
    delete(personMap,"age")
    printMap(personMap)
}

//personMap是一个指针引用传递
func printMap(personMap map[string]string){
    for key,value := range personMap{
        fmt.Println("key = ",key,", value = ",value)
    }
}

func changeMap(personMap map[string]string){
    personMap["sex"] = "女"
}

10 - OOP

1、struct结构体

package main

import "fmt"

//定义结构体
type Book struct{
    name string
    age int
}

func main(){
    var book Book
    book.name = "JackyChen"
    book.age = 11

    fmt.Printf("book = %v\n",book)

    changeBook(&book)
    fmt.Printf("book = %v\n",book)
}

func changeBook(book *Book){
    book.age = 25
}

2、封装

package main

import "fmt"

// 类和属性名首字母大写,可以其它包访问
// 也就是大写是共有,小写是私有
type Hero struct{
    Name string
    Age int
    Level int
}

func (this Hero) Show(){
    fmt.Println("Name = ",this.Name)
}

// 需要加引用才能进行改变
func (this *Hero) SetName(name string){
    this.Name = name
}

func (this Hero) GetName() string{
    return this.Name
}

func main(){
    //hero := Hero{Name:"JackyChen",Age:18,Level:1}
    hero := Hero{"JackyChen",18,1}
    hero.Show()

    hero.SetName("cxhblog")
    hero.Show()
}

3、继承

package main

import "fmt"

//父类
type Human struct{
    Name string
    Age int
}

func (this Human) Eat(){
    fmt.Println("Human Eat...")
}

func (this Human) Walk(){
    fmt.Println("Human Walk...")
}

//子类
type Man struct{
    Human       // 表示Man继承Human
    Level int
}

//覆盖父类方法
func (this Man) Eat(){
    fmt.Println("Man Eat...")
}

// 子类新方法
func (this Man) Fly(){
    fmt.Println("Man Fly...")
}

func (this Man) Print(){
    fmt.Println("name = ",this.Name)
    fmt.Println("age = ",this.Age)
    fmt.Println("Level = ",this.Level)
}

func main(){
    h := Human{"JackyChen",18}
    fmt.Printf("Human = %v\n",h)

    //m := Man{Human{"cxhblog",25},2}
    var m Man
    m.Name = "cxhblog"
    m.Age = 25
    m.Level = 3
    fmt.Printf("Man = %v\n",m)

    m.Eat()
    m.Fly()
    m.Print()
}

4、多态interface

package main

import "fmt"

type Animal 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 Sleep...")
}

func (this Dog) GetColor() string {
    return this.Color
}

func (this Dog) GetType() string {
    return "Dog"
}

func showAnimal(animal Animal){
    animal.Sleep()
    fmt.Println("color = ",animal.GetColor())
    fmt.Println("type = ",animal.GetType())
}

func main(){
    /*
    var animal Animal   //接口的数据类型,父类指针
    animal = &Cat{"Green"}  //调用的是Cat的Sleep方法,多态现象
    animal.Sleep()

    animal = &Dog{"Yellow"} //调用的是Cat的Sleep方法,多态现象
    animal.Sleep()
    */

    cat := Cat{"Green"}
    showAnimal(&cat)

    dog := Dog{"Green"}
    showAnimal(&dog)
}

5、interface空接口万能类型与类型断言机制

package main

import "fmt"

//interface{}是万能数据类型
func myFunc(arg interface{}){
    fmt.Println(arg)

    // 给interface{} 提供 "类型断言" 机制
    value,ok := arg.(string)
    if ok {
        fmt.Println("arg is string type, value = ",value)
        fmt.Printf("value type is %T\n",value)
    }else{
        fmt.Println("arg is not string type")
    }
}

type Book struct{
    author string
}

func main(){
    book := Book{"JackyChen"}
    myFunc(book)
    fmt.Println("=========")
    myFunc(100)
    fmt.Println("=========")
    myFunc("abc")
}
11 - reflect

1、变量的内置pair结构详细说明

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("write a Book")
}

func main(){
    var a string
    //pair
    a = "abcd"

    //任何一个类型都有pair 
    var allType interface{}
    allType = a

    str,_ := allType.(string)

    fmt.Println(str)

    //b:pair
    b := &Book{}

    var r Reader
    //r:pair
    r = b
    r.ReadBook()

    var w Writer
    //r:pair
    w = r.(Writer)  //为什么断言成功,因为w r type是一致
    w.WriteBook()
}

2、反射reflect机制用法

package main

import (
    "fmt"
    "reflect"
)

type User struct {
    Id int
    Name string
    Age int
}

//注意不要加 *User,否则NumMethod获取不到个数
func (this User) Call() {
    fmt.Println("user is a Called..")
    fmt.Printf("%v\n",this)
}

func main(){
    user := User{1,"JackyChen",18}
    DoFieldAndMethod(user)
}

func DoFieldAndMethod(input interface{}){
    //获取input的type
    inputType := reflect.TypeOf(input)
    fmt.Println("inputType = ",inputType.Name())

    //获取input的value
    inputValue := reflect.ValueOf(input)
    fmt.Println("inputValue = ",inputValue)

    //通过type 获取里面的字段
    //1.获取interface的reflect.Type,通过Type得到NumField,进行遍历
    //2.得到每个field,数据类型
    //3.通过field有个Interface()方法得到对应的value
    for i := 0; i < inputType.NumField(); i++{
        field := inputType.Field(i)
        value := inputValue.Field(i).Interface()

        //属性名 类型 value
        fmt.Printf("%s: %v = %v\n",field.Name,field.Type,value)
    }

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

3、反射解析结构体tag标签

package main

import (
    "fmt"
    "reflect"
)

type resume struct{
    Name string `info:"name" doc:"你的名字"`
    Age int `info:"sex" doc:"JackyCHen"`
}

func findTag(str interface{}){
    tag := reflect.TypeOf(str)
    for i := 0; i < tag.NumField(); i++{
        tagInfo := tag.Field(i).Tag.Get("info")
        tagDoc := tag.Field(i).Tag.Get("doc")
        fmt.Println("info: ",tagInfo,", doc:",tagDoc)
    }
}

func main(){
    var r resume

    findTag(r)
}

4、结构体标签在json的应用

package main

import (
    "fmt"
    "encoding/json"
)

type Movie struct{
    Title string    `json:"title"`
    Year int        `json:"year"`
    Price int       `json:"rmb"`
    Actors []string `json:"actors"`
}

func main(){
    movie := Movie{"喜剧之王",1998,2,[]string{"周星驰","张柏芝"}}
    //编码 结构体 =====> json
    jsonStr,err := json.Marshal(movie)
    if err != nil {
        fmt.Println("json marshal error",err)
        return
    }
    fmt.Printf("jsonStr = %s\n",jsonStr)

    //解码 json ======> 结构体
    //jsonStr = {"title":"喜剧之王","year":1998,"rmb":2,"actors":["周星驰","张柏芝"]}
    myMovie := Movie{}
    err = json.Unmarshal(jsonStr,&myMovie)
    if err != nil {
        fmt.Println("json unmarshal error",err)
        return
    }

    fmt.Printf("%v\n",myMovie)
}
12 - 协程goroutine

1、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协程
    go newTask()

    //主进程退出,整个进程退出
    fmt.Println("main goroutine exit")

    /*
    i := 0
    for {
        i++
        fmt.Printf("main goroutine : i = %d\n",i)
        time.Sleep(1 * time.Second)
    }*/
}

2、退出协程go exit

package main

import (
    "fmt"
    "time"
    "runtime"
)

func main(){
    //用go创建承载一个形参和返回值为空的函数
    go func(){
        defer fmt.Println("A.defer")

        func(){
            defer fmt.Println("B.defer")

            //退出协程
            runtime.Goexit()

            fmt.Println("B")
        }()

        fmt.Println("A")
    }()

    //有参和返回值
    //因为go是异步的,所以得不到返回值,需要使用管道channel
    go func(a int, b int) bool {
        fmt.Println("a = ",a,", b = ",b)
        return true
    }(10,20)

    for{
        time.Sleep(1 * time.Second)
    }
}
13 - channel

1、channel定义使用

package main

import "fmt"

func main(){
    c := make(chan int)

    go func() {
        defer fmt.Println("goroutine结束")

        fmt.Println("goroutine 正在运行")
        //发送到channel
        c <- 666
    }()

    //接收数据,<-c 不能有空格
    num := <-c
    fmt.Println("num = ",num)
    fmt.Println("main goroutine 接收...")
}

2、channel(有缓冲)

package main

import (
    "fmt"
    "time"
)

func main(){
    //带有缓冲的channel
    c := make(chan int,3)

    go func(){
        defer fmt.Println("goroutine结束")

        //到channel存满会进行阻塞,等待空间
        for i := 0; i < 5; i++{
            c <- i
            fmt.Println("元素=",i,"len(c) = ",len(c),", cap(c) = ",cap(c))
        }
    }()

    time.Sleep(2 * time.Second)

    for i := 0; i < 5; i++{
        num := <-c
        fmt.Println("num = ",num)
    }

    fmt.Println("main goroutine 结束")
}

3、channel关闭

package main

import (
    "fmt"
)

func main(){
    c := make(chan int)

    go func(){
        for i := 0; i < 5; i++{
            c <- i
            //向一个关闭的channel发送数据,会包panic错误
            //close(c)
        }
        
        //关闭channel
        close(c)
    }()

    //如果没有关闭造成deadlock死锁
    for {
        //ok判断close是否关闭
        if data,ok := <-c; ok {
            fmt.Println("data = ",data)
        }else{
            break
        }
    }

    fmt.Println("main Finished..")
}

4、channel-range

package main

import (
    "fmt"
)

func main(){
    c := make(chan int)

    go func(){
        for i := 0; i < 5; i++{
            c <- i
        }

        //关闭channel
        close(c)
    }()

    //使用range迭代不断 *** 作channel
    for data := range c {
        fmt.Println("data =",data)
    }

    fmt.Println("main Finished..")
}

5、channel-select

package main

import "fmt"

func fibonacli(c, quit chan int){
    x, y := 1, 1
	//select具备多路channel监控状态功能
    for{
        select{
            //如果c可写
            case c <- x:
                x = y
                y = x +y
            //quit可读
            case <-quit:
                fmt.Println("quit")
                return
        }
    }

}

func main(){
    c := make(chan int)
    quit := make(chan int)

    go func(){
        for i := 0; i < 4; i++{
            fmt.Println(<-c)
        }

        quit <- 0
    }()

    fibonacli(c,quit)
}
Go Modules模式

1、go mod 命令

2、go mod环境变量

go env -w GO111MODULE=on

go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/


GO MODULE初始化项目
# 1:开启GO Modules
go env -w GO111MODULE=on	或者	修改~/.bashrc文件: export GO111MODULE=on		刷新配置:source ~/.bashrc
# 2:创建项目目录
mkdir -p $HOME/JackyChen/modules_test && cd $HOME/JackyChen/modules_test
# 3:初始化,指定模块导入路径:github.com/JackyChen/modules_test
go mod init github.com/JackyChen/modules_test
// go: creating new go.mod: module github.com/JackyChen/modules_test
# 4:新建main.go文件,加入代码,github地址:https://github.com/aceld/zinx

package main
import (
        "fmt"
        "github.com/aceld/zinx/ziface"
        "github.com/aceld/zinx/znet"
)

//ping test 自定义路由
type PingRouter struct {
        znet.BaseRouter
}

//Ping Handle
func (this *PingRouter) Handle(request ziface.IRequest) {
        //先读取客户端的数据
        fmt.Println("recv from client : msgId=", request.GetMsgID(), ", data=", string(request.GetData()))

    //再回写ping...ping...ping
        err := request.GetConnection().SendBuffMsg(0, []byte("ping...ping...ping"))
        if err != nil {
                fmt.Println(err)
        }
}

func main() {
        //1 创建一个server句柄
        s := znet.NewServer()

        //2 配置路由
        s.AddRouter(0, &PingRouter{})

        //3 开启服务
        s.Serve()
}

# 手动下载包,保存路径:$GOPATH/pkg/mod/github.com
go get github.com/aceld/zinx/znet
go get github.com/aceld/zinx/ziface
# 执行main.go,如果没有手动下载包,执行命令会自动下载
go run main.go

# 补充:

# 1:下载包会多出go.sum文件
github.com/aceld/zinx v1.0.0 h1:w1rfb84AsiR/5NRKL8HDBpOB3nJwzwzZyouUjS825q0=
github.com/aceld/zinx v1.0.0/go.mod h1:bMiERrPdR8FzpBOo86nhWWmeHJ1cCaqVvWKCGcDVJ5M=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
// 直接或间接依赖的所有模块版本,保证今后项目依赖的版本不会被篡改
// h1:hash	表示整体项目zip文件打开后的全部文件的校验和生成的hash,如果不存在,可能表示依赖的库不存在
// xxx/go.mod h1:hash	go.mod文件做的hash
# 2:go.mod,会多一行代码
require github.com/aceld/zinx v1.0.0 	// indirect
require github.com/aceld/zinx			// 当前依赖
v1.0.0									// 依赖版本
indirect								// 间接依赖

# 修改项目模块版本依赖关系
go mod edit -replace=zinx@v1.0.0=zinx@v1.1.1


14 - 即时通信系统 golang-IM-System

1、基础server构建

# 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) Hanlder(conn net.Conn){
    fmt.Println("连接建立成功")
}

//启动服务器接口
func (this *Server) Start(){
    //socket listen 127.0.0.1:8888
    listener,err := net.Listen("tcp",fmt.Sprintf("%s:%d",this.Ip,this.Port))
    if err != nil{
        fmt.Println("net.listen err:",err)
        return
    }

    //socket close
    defer listener.Close()

    for {
        //accept
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("listener.Accept err:",err)
            return
        }

        //do hanlder
        go this.Hanlder(conn)
    }
}

# main go

package main

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



2、用户上线功能

# server.go

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
}

//连接成功的业务
func (this *Server) Hanlder(conn net.Conn){
    //fmt.Println("连接建立成功")
    user := NewUser(conn)

    //用户上线,将用户放入OnlineMap
    this.mapLock.Lock()
    this.OnlineMap[user.Name] = user
    this.mapLock.Unlock()

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

    //当前hanlder阻塞
    select{}
}

//广播消息
func (this *Server)BroadCast(user *User, msg string){
    sendMsg := "[" + user.Addr + "]" + user.Name + "]" + msg
    this.Message <- sendMsg
}

//监听Message广播消息channel,有消息就发给在线User
func (this *Server) ListenerMessager(){
    for {
        msg := <-this.Message

        this.mapLock.Lock()
        for _,cli := range this.OnlineMap{
            cli.C <- msg
        }
        this.mapLock.Unlock()
    }
}

//启动服务器接口
func (this *Server) Start(){
    //socket listen 127.0.0.1:8888
    listener,err := net.Listen("tcp",fmt.Sprintf("%s:%d",this.Ip,this.Port))
    if err != nil{
        fmt.Println("net.listen err:",err)
        return
    }

    //socket close
    defer listener.Close()

    //启动监听Message
    go this.ListenerMessager()

    for {
        //accept
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("listener.Accept err:",err)
            return
        }

        //do hanlder
        go this.Hanlder(conn)
    }
}
# user.go

package main

import "net"

type User struct{
    Name string
    Addr string
    C    chan string
    conn net.Conn
}

//创建用户接口
func NewUser(conn net.Conn) *User {
    addr := conn.RemoteAddr().String()
    user := &User{
        Name:addr,
        Addr:addr,
        C   : make(chan string),
        conn:conn,
    }

    //监听当前user channel消息的goroutine
    go user.ListenerMessage()

    return user
}

//监听User channel,有消息就发给客户端
func (this *User) ListenerMessage(){
    for{
        msg := <-this.C
        this.conn.Write([]byte(msg + "\n"))
    }
}



3、用户消息广播

package main

import (
    "fmt"
    "net"
    "sync"
    "io"
)

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
}

//连接成功的业务
func (this *Server) Hanlder(conn net.Conn){
    //fmt.Println("连接建立成功")
    user := NewUser(conn)

    //用户上线,将用户放入OnlineMap
    this.mapLock.Lock()
    this.OnlineMap[user.Name] = user
    this.mapLock.Unlock()

    //广播当前用户上线消息
    this.BroadCast(user,"已上线")
	
	//接收用户消息
    go func(){
         buf := make([]byte,4096)
         for {
            n, err := conn.Read(buf)
            if n == 0 {
             this.BroadCast(user,"下线")
             return
            }

            if err != nil && err != io.EOF {
             fmt.Println("Conn Read err:",err)
             return
            }
            //提取用户的消息(去除"\n"),截取0至n-1
            msg := string(buf[:n-1])

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

    //当前hanlder阻塞
    select{}
}

//广播消息
func (this *Server)BroadCast(user *User, msg string){
    sendMsg := "[" + user.Addr + "]" + user.Name + "]" + msg
    this.Message <- sendMsg
}

//监听Message广播消息channel,有消息就发给在线User
func (this *Server) ListenerMessager(){
    for {
        msg := <-this.Message

        this.mapLock.Lock()
        for _,cli := range this.OnlineMap{
            cli.C <- msg
        }
        this.mapLock.Unlock()
    }
}

//启动服务器接口
func (this *Server) Start(){
    //socket listen 127.0.0.1:8888
    listener,err := net.Listen("tcp",fmt.Sprintf("%s:%d",this.Ip,this.Port))
    if err != nil{
        fmt.Println("net.listen err:",err)
        return
    }

    //socket close
    defer listener.Close()

    //启动监听Message
    go this.ListenerMessager()

    for {
        //accept
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("listener.Accept err:",err)
            return
        }

        //do hanlder
        go this.Hanlder(conn)
    }
}


4、用户业务封装

# server.go

package main

import (
    "fmt"
    "net"
    "sync"
    "io"
)

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
}

//连接成功的业务
func (this *Server) Hanlder(conn net.Conn){
    //fmt.Println("连接建立成功")
    user := NewUser(conn,this)

    //用户上线
    user.Online()

    //接收用户消息
    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"),截取0至n-1
            msg := string(buf[:n-1])

            //用户处理消息
            user.DoMessage(msg)
         }
     }()

    //当前hanlder阻塞
    select{}
}

//广播消息
func (this *Server)BroadCast(user *User, msg string){
    sendMsg := "[" + user.Addr + "]" + user.Name + "]" + msg
    this.Message <- sendMsg
}

//监听Message广播消息channel,有消息就发给在线User
func (this *Server) ListenerMessager(){
    for {
        msg := <-this.Message

        this.mapLock.Lock()
        for _,cli := range this.OnlineMap{
            cli.C <- msg
        }
        this.mapLock.Unlock()
    }
}

//启动服务器接口
func (this *Server) Start(){
    //socket listen 127.0.0.1:8888
    listener,err := net.Listen("tcp",fmt.Sprintf("%s:%d",this.Ip,this.Port))
    if err != nil{
        fmt.Println("net.listen err:",err)
        return
    }

    //socket close
    defer listener.Close()

    //启动监听Message
    go this.ListenerMessager()

    for {
        //accept
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("listener.Accept err:",err)
            return
        }

        //do hanlder
        go this.Hanlder(conn)
    }
}
# user.go

package main

import "net"

type User struct{
    Name string
    Addr string
    C    chan string
    conn net.Conn
    server *Server
}

//创建用户接口
func NewUser(conn net.Conn, server *Server) *User {
    addr := conn.RemoteAddr().String()
    user := &User{
        Name:addr,
        Addr:addr,
        C   : make(chan string),
        conn:conn,
        server:server,
    }

    //监听当前user channel消息的goroutine
    go user.ListenerMessage()

    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,"下线")
}

//将得到的消息进行广播
func (this *User) DoMessage(msg string){
    this.server.BroadCast(this,msg)
}

//监听User channel,有消息就发给客户端
func (this *User) ListenerMessage(){
    for{
        msg := <-this.C
        this.conn.Write([]byte(msg + "\n"))
    }
}

5、在线用户查询

# user.go

package main

import "net"

type User struct{
    Name string
    Addr string
    C    chan string
    conn net.Conn
    server *Server
}

//创建用户接口
func NewUser(conn net.Conn, server *Server) *User {
    addr := conn.RemoteAddr().String()
    user := &User{
        Name:addr,
        Addr:addr,
        C   : make(chan string),
        conn:conn,
        server:server,
    }

    //监听当前user channel消息的goroutine
    go user.ListenerMessage()

    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,"下线")
}

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 {
            if(user != this){
                onlineMsg := "[" + user.Addr + "]" + user.Name + ":在线...\n"
                this.SendMsg(onlineMsg)
            }
        }
        this.server.mapLock.Unlock()
    }else{
        this.server.BroadCast(this,msg)
    }
}

//监听User channel,有消息就发给客户端
func (this *User) ListenerMessage(){
    for{
        msg := <-this.C
        this.conn.Write([]byte(msg + "\n"))
    }
}


6、修改用户名

# user.go

package main

import (
    "net"
    "strings"
)

type User struct{
    Name string
    Addr string
    C    chan string
    conn net.Conn
    server *Server
}

//创建用户接口
func NewUser(conn net.Conn, server *Server) *User {
    addr := conn.RemoteAddr().String()
    user := &User{
        Name:addr,
        Addr:addr,
        C   : make(chan string),
        conn:conn,
        server:server,
    }

    //监听当前user channel消息的goroutine
    go user.ListenerMessage()

    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,"下线")
}

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 {
            if(user != this){
                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]
        //判断用户名是否存在
        _, ok := this.server.OnlineMap[newName]
        if ok {
            this.SendMsg("用户名已存在\n")
        }else{
            this.server.mapLock.Lock()

            delete(this.server.OnlineMap,this.Name)
            this.Name = newName
            this.server.OnlineMap[newName] = this

            this.server.mapLock.Unlock()
            this.SendMsg("更新用户名:" + this.Name + ",成功\n")
        }
    }else{
        this.server.BroadCast(this,msg)
    }
}

//监听User channel,有消息就发给客户端
func (this *User) ListenerMessage(){
    for{
        msg := <-this.C
        this.conn.Write([]byte(msg + "\n"))
    }
}


7、超时强踢

# server.go

package main

import (
    "fmt"
    "net"
    "sync"
    "io"
    "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
}

//连接成功的业务
func (this *Server) Hanlder(conn net.Conn){
    //fmt.Println("连接建立成功")
    user := NewUser(conn,this)

    //用户上线
    user.Online()

    //监听用户消息活跃channel
    isActive := 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"),截取0至n-1
            msg := string(buf[:n-1])

            //用户处理消息
            user.DoMessage(msg)

            //用户发消息,代表活跃
            isActive <- true
         }
     }()

    //当前hanlder阻塞
    for {
        select{
            case <-isActive:
                //用户活跃,激活select,重置定时器

            case <-time.After(time.Second * 15):
                //超时不活跃,踢出下线
                user.SendMsg("长时间不 *** 作,强制下线\n")

                //销毁用户资源
                close(user.C)

                //关闭连接
                conn.Close()

                //退出当前hanlder
                return  //或者runtime.Goexit()
        }
    }
}

//广播消息
func (this *Server)BroadCast(user *User, msg string){
    sendMsg := "[" + user.Addr + "]" + user.Name + "]" + msg
    this.Message <- sendMsg
}

//监听Message广播消息channel,有消息就发给在线User
func (this *Server) ListenerMessager(){
    for {
        msg := <-this.Message

        this.mapLock.Lock()
        for _,cli := range this.OnlineMap{
            cli.C <- msg
        }
        this.mapLock.Unlock()
    }
}

//启动服务器接口
func (this *Server) Start(){
    //socket listen 127.0.0.1:8888
    listener,err := net.Listen("tcp",fmt.Sprintf("%s:%d",this.Ip,this.Port))
    if err != nil{
        fmt.Println("net.listen err:",err)
        return
    }

    //socket close
    defer listener.Close()

    //启动监听Message
    go this.ListenerMessager()

    for {
        //accept
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("listener.Accept err:",err)
            return
        }

        //do hanlder
        go this.Hanlder(conn)
    }
}


8、私聊消息功能

# user.go

package main

import (
    "net"
    "strings"
)

type User struct{
    Name string
    Addr string
    C    chan string
    conn net.Conn
    server *Server
}

//创建用户接口
func NewUser(conn net.Conn, server *Server) *User {
    addr := conn.RemoteAddr().String()
    user := &User{
        Name:addr,
        Addr:addr,
        C   : make(chan string),
        conn:conn,
        server:server,
    }

    //监听当前user channel消息的goroutine
    go user.ListenerMessage()

    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,"下线")
}

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 {
            if(user != this){
                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]
        //判断用户名是否存在
        _, ok := this.server.OnlineMap[newName]
        if ok {
            this.SendMsg("用户名已存在\n")
        }else{
            this.server.mapLock.Lock()

            delete(this.server.OnlineMap,this.Name)
            this.Name = newName
            this.server.OnlineMap[newName] = this

            this.server.mapLock.Unlock()
            this.SendMsg("更新用户名:" + this.Name + ",成功\n")
        }
    }else if len(msg) > 4 && msg[:3] == "to|" {
        //消息格式 to|cxhblog|Hello World!
        remoteName := strings.Split(msg,"|")[1]
        if remoteName == "" {
            this.SendMsg("消息格式错误\n")
            return
        }

        //根据用户名,得到User对象
        remoteUser, ok := this.server.OnlineMap[remoteName]
        if !ok {
            this.SendMsg("用户不存在\n")
            return
        }

        //获取消息内容,发送消息
        text := strings.Split(msg,"|")[2]
        if text == "" {
            this.SendMsg("消息内容为空,请重新发送\n")
            return
        }

        remoteUser.SendMsg(this.Name + ",对你说:" + text + "\n")
    }else{
        this.server.BroadCast(this,msg)
    }
}

//监听User channel,有消息就发给客户端
func (this *User) ListenerMessage(){
    for{
        msg := <-this.C
        this.conn.Write([]byte(msg + "\n"))
    }
}


9、客户端实现 - 建立连接

# client.go

package main

import (
    "fmt"
    "net"
)

type Client struct{
    ServerIp string
    ServerPort int
    Name string
    conn net.Conn
}

func NewClient(serverIp string, serverPort int) *Client {
    //创建客户端对象
    client := &Client{
        ServerIp:serverIp,
        ServerPort:serverPort,
    }

    //链接server
    conn,err := net.Dial("tcp",fmt.Sprintf("%s:%d",serverIp,serverPort))
    if err != nil {
        fmt.Println("net.Dial err:",err)
        return nil
    }

    client.conn = conn

    return client
}

func main(){
    client := NewClient("127.0.0.1",8888)
    if client == nil {
        fmt.Println("连接失败...\n")
        return
    }

    fmt.Println("连接成功")

    //启动客户端业务
    select{}
}


10、客户端实现-命令行解析

# client.go

package main

import (
    "fmt"
    "net"
    "flag"
)

type Client struct{
    ServerIp string
    ServerPort int
    Name string
    conn net.Conn
}

func NewClient(serverIp string, serverPort int) *Client {
    //创建客户端对象
    client := &Client{
        ServerIp:serverIp,
        ServerPort:serverPort,
    }

    //链接server
    conn,err := net.Dial("tcp",fmt.Sprintf("%s:%d",serverIp,serverPort))
    if err != nil {
        fmt.Println("net.Dial err:",err)
        return nil
    }

    client.conn = conn

    return client
}

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("连接失败...\n")
        return
    }

    fmt.Println("连接成功")

    //启动客户端业务
    select{}
}


11、客户端实现-菜单显示

# client.go

package main

import (
    "fmt"
    "net"
    "flag"
)

type Client struct{
    ServerIp string
    ServerPort int
    Name string
    conn net.Conn
    flag int
}

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 err:",err)
        return nil
    }

    client.conn = conn

    return client
}

func (this *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 {
        this.flag = flag
        return true
    }else{
        fmt.Println("====请输入合法数字====")
        return false
    }
}

func (this *Client) Run(){
    for this.flag != 0 {
        for this.menu() != true {
        }

        //根据不同模式处理不同业务
        switch this.flag {
            case 1:
                //公聊模式
                fmt.Println("选择公聊模式...")
                break
            case 2:
                //私聊模式
                fmt.Println("选择私聊模式...")
                break
            case 3:
                //更新用户名
                fmt.Println("选择更新用户名...")
                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("连接失败...\n")
        return
    }

    fmt.Println("连接成功")

    //启动客户端业务
    client.Run()
}


12、更新用户名

# client.go

package main

import (
    "fmt"
    "net"
    "flag"
    "io"
    "os"
)

type Client struct{
    ServerIp string
    ServerPort int
    Name string
    conn net.Conn
    flag int
}

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 err:",err)
        return nil
    }

    client.conn = conn

    return client
}

func (this *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 {
        this.flag = flag
        return true
    }else{
        fmt.Println("====请输入合法数字====")
        return false
    }
}

//更新用户名
func (this *Client) UpdateName() bool {
    fmt.Println("====请输入用户名====")
    fmt.Scanln(&this.Name)

    sendMsg := "rename|" + this.Name + "\n"
    _, err := this.conn.Write([]byte(sendMsg))
    if err != nil {
        fmt.Println("conn.Write err:",err)
        return false
    }

    return true
}

func (this *Client) Run(){
    for this.flag != 0 {
        for this.menu() != true {
        }

        //根据不同模式处理不同业务
        switch this.flag {
            case 1:
                //公聊模式
                fmt.Println("选择公聊模式...")
                break
            case 2:
                //私聊模式
                fmt.Println("选择私聊模式...")
                break
            case 3:
                //更新用户名
                this.UpdateName()
                break
        }
    }
}

//处理server回应的消息,直接显示到标准输出
func (this *Client) DealResponse(){
    //永久阻塞监听,一旦client.conn有数据,就直接copy到stdout标准输出上
    io.Copy(os.Stdout,this.conn)

    /*
    //等价于
    buf := make([]byte,4096)
    for {
        client.conn.Read(buf)
        fmt.Println(buf)
    }
    */
}

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("连接失败...\n")
        return
    }

    //单独开启goroutine处理server回应的消息
    go client.DealResponse()

    fmt.Println("连接成功")

    //启动客户端业务
    client.Run()
}


13、公聊模式

package main

import (
    "fmt"
    "net"
    "flag"
    "io"
    "os"
)

type Client struct{
    ServerIp string
    ServerPort int
    Name string
    conn net.Conn
    flag int
}

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 err:",err)
        return nil
    }

    client.conn = conn

    return client
}

func (this *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 {
        this.flag = flag
        return true
    }else{
        fmt.Println("====请输入合法数字====")
        return false
    }
}

//更新用户名
func (this *Client) UpdateName() bool {
    fmt.Println("====请输入用户名====")
    fmt.Scanln(&this.Name)

    sendMsg := "rename|" + this.Name + "\n"
    _, err := this.conn.Write([]byte(sendMsg))
    if err != nil {
        fmt.Println("conn.Write err:",err)
        return false
    }

    return true
}

//公聊模式
func (this *Client) PublicChan(){
    var chanMsg string
    fmt.Println("请输入消息内容,exit 退出")
    fmt.Scanln(&chanMsg)

    for chanMsg != "exit" {

        if len(chanMsg) != 0 {
            sendMsg := chanMsg + "\n"
            _, err := this.conn.Write([]byte(sendMsg))
            if err != nil {
                fmt.Println("PucblicChan err:",err)
                break
            }
        }

        chanMsg = ""
        fmt.Println("请输入消息内容,exit 退出")
        fmt.Scanln(&chanMsg)
    }
}

func (this *Client) Run(){
    for this.flag != 0 {
        for this.menu() != true {
        }

        //根据不同模式处理不同业务
        switch this.flag {
            case 1:
                //公聊模式
                this.PublicChan()
                break
            case 2:
                //私聊模式
                fmt.Println("选择私聊模式...")
                break
            case 3:
                //更新用户名
                this.UpdateName()
                break
        }
    }
}

//处理server回应的消息,直接显示到标准输出
func (this *Client) DealResponse(){
    //永久阻塞监听,一旦client.conn有数据,就直接copy到stdout标准输出上
    io.Copy(os.Stdout,this.conn)

    /*
    //等价于
    buf := make([]byte,4096)
    for {
        client.conn.Read(buf)
        fmt.Println(buf)
    }
    */
}

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("连接失败...\n")
        return
    }

    //单独开启goroutine处理server回应的消息
    go client.DealResponse()

    fmt.Println("连接成功")

    //启动客户端业务
    client.Run()
}


14、私聊模式

package main

import (
    "fmt"
    "net"
    "flag"
    "io"
    "os"
)

type Client struct{
    ServerIp string
    ServerPort int
    Name string
    conn net.Conn
    flag int
}

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 err:",err)
        return nil
    }

    client.conn = conn

    return client
}

func (this *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 {
        this.flag = flag
        return true
    }else{
        fmt.Println("====请输入合法数字====")
        return false
    }
}

//更新用户名
func (this *Client) UpdateName() bool {
    fmt.Println("====请输入用户名====")
    fmt.Scanln(&this.Name)

    sendMsg := "rename|" + this.Name + "\n"
    _, err := this.conn.Write([]byte(sendMsg))
    if err != nil {
        fmt.Println("conn.Write err:",err)
        return false
    }

    return true
}

//公聊模式
func (this *Client) PublicChan(){
    var chanMsg string
    fmt.Println("请输入消息内容,exit 退出")
    fmt.Scanln(&chanMsg)

    for chanMsg != "exit" {

        if len(chanMsg) != 0 {
            sendMsg := chanMsg + "\n"
            _, err := this.conn.Write([]byte(sendMsg))
            if err != nil {
                fmt.Println("PucblicChan err:",err)
                break
            }
        }

        chanMsg = ""
        fmt.Println("请输入消息内容,exit 退出")
        fmt.Scanln(&chanMsg)
    }
}

//查询在线用户
func (this *Client) SelectUsers(){
    sendMsg := "who\n"
    _, err := this.conn.Write([]byte(sendMsg))
    if err != nil {
        fmt.Println("SelectUsers err:",err)
        return
    }
}

//私聊模式
func (this *Client) PrivateChat(){
    var remoteName string
    var chatMsg string
    this.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"
                _, err := this.conn.Write([]byte(sendMsg))
                if err != nil {
                    fmt.Println("PucblicChan err:",err)
                    break
                }
            }

            chatMsg = ""
            fmt.Println("请输入消息内容,exit 退出")
            fmt.Scanln(&chatMsg)
        }

        this.SelectUsers()
        remoteName = ""
        fmt.Println("请输入聊天对象[用户名], exit退出")
        fmt.Scanln(&remoteName)
    }

}

func (this *Client) Run(){
    for this.flag != 0 {
        for this.menu() != true {
        }

        //根据不同模式处理不同业务
        switch this.flag {
            case 1:
                //公聊模式
                this.PublicChan()
                break
            case 2:
                //私聊模式
                this.PrivateChat()
                break
            case 3:
                //更新用户名
                this.UpdateName()
                break
        }
    }
}

//处理server回应的消息,直接显示到标准输出
func (this *Client) DealResponse(){
    //永久阻塞监听,一旦client.conn有数据,就直接copy到stdout标准输出上
    io.Copy(os.Stdout,this.conn)

    /*
    //等价于
    buf := make([]byte,4096)
    for {
        client.conn.Read(buf)
        fmt.Println(buf)
    }
    */
}

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("连接失败...\n")
        return
    }

    //单独开启goroutine处理server回应的消息
    go client.DealResponse()

    fmt.Println("连接成功")

    //启动客户端业务
    client.Run()
}

生态拓展和成长方向





码云源码地址:https://gitee.com/cxhblog/go.git 推荐学习网站:https://www.topgoer.com/

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/995102.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存