# 下载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的优势
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/
# 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
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()
}
生态拓展和成长方向
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)