反射 反射使用的库函数为 reflect
其中reflect 中定义了多个类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 const ( Invalid Kind = iota Bool Int Int8 Int16 Int32 Int64 Uint Uint8 Uint16 Uint32 Uint64 Uintptr Float32 Float64 Complex64 Complex128 Array Chan Func Interface Map Pointer Slice String Struct UnsafePointer )
获取变量的值和类型 主要使用2个函数
1 func TypeOf(i interface{}) Type
可以通过 reflect.TypeOf 获取任意类型的 Type 类型.
通过 reflect.ValueOf 得到一个值的类型,
1 func ValueOf(i interface{}) Value
Value 可以通过调用 Int() float() 等方法获取其对应的类型的值。
Type 和Value 都可以通过 Kind 函数获取到一个值的类型常量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 func TestReflect(t *testing.T) { var a int = 3 varType := reflect.TypeOf(a) t.Log(varType.Kind() == reflect.Int) //valueOf v := reflect.ValueOf(a) t.Log(v) //获取类型 t.Log(v.Type()) t.Log(v.Kind()) t.Log(v.Int()) t.Log(v.Interface()) } hello_test.go:32: true hello_test.go:36: 3 hello_test.go:38: int hello_test.go:39: int hello_test.go:40: 3 hello_test.go:41: 3
反射修改值 通过 reflect.ValueOf(*a) 获取对应的Value 对象可以对原始的传入的引用进行修改。传入的值必须为指针类型。
1 2 3 4 5 6 7 8 9 10 func TestModifyValue(t *testing.T) { var a int = 5 v := reflect.ValueOf(&a) canset := v.CanSet() t.Logf("canset %t",canset) typeOfe := v.Elem() t.Logf("canset %t",typeOfe.CanSet()) typeOfe.SetInt(6) t.Logf("a = %d",a) }
通过CanSet() 方法可以得到值是否可修改,通过Elem() 函数可以获取指针指向的元素类型。
根据不同的类型来调用不同的set 函数为原始的元素设置值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 func TestUser(t *testing.T) { u := User{name:"张三",Age:10} t.Log(u) v := reflect.ValueOf(&u) ve := v.Elem() //获取指定字段 ageField := ve.FieldByName("Age") t.Log(ageField.Kind()) t.Logf("age can set %t",ageField.CanSet()) //true ageField.SetInt(100) nameField := ve.FieldByName("name") t.Log(nameField.Kind()) t.Logf("age can set %t",nameField.CanSet()) //false t.Log(u) t.Log(ve.NumField()) //遍历所有字段 for i := 0; i < ve.NumField(); i++ { f := ve.Field(i) t.Logf("f== %s",f) } t.Log("==method===") t.Log(ve.NumMethod()) //遍历所有方法 并调用 for i := 0; i < ve.NumMethod(); i++ { me := ve.Method(i) t.Logf("m== %s",me) inputs := make([]reflect.Value,1) inputs[0] = reflect.ValueOf("李四") me.Call(inputs) } t.Log(u) //获取 struct tag 信息 ut := reflect.TypeOf(&u) uType := ut.Elem() if ageTypeField,ok := uType.FieldByName("Age");ok{ jvalue := ageTypeField.Tag.Get("json") t.Logf("jvalue = %s",jvalue) }else{ t.Log("获取失败") } }
reflect.ValueOf 和 reflect.TypeOf 得到的指针对象需要再调用 Elem 获取指针指向的元素类型 FieldByName 只能设置 首字母大写 的属性信息,首字母小写的 CanSet 返回false NumField() 和 NumMethod() 用来获取全部的字段和方法数量可用来遍历所有的字段和方法 获取到Method 后调用call 方法执行对应的方法 reflect.TypeOf 的 FieldByName 方法是有2个返回值的,并在这个能够获取结构字段的 Tag 信息。 通过反射比较map 和切片 map 和切片不能直接使用 == 来做比较,但是反射中提供了一个 reflect.DeepEqual 函数可以用来做比较。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 func TestDeepEquals(t *testing.T) { //map 比较 a := map[string]string{"name":"xx","age":"10"} b := map[string]string{"name":"xx","age":"10"} c := map[string]string{"name":"xx1","age":"10"} t.Logf("map DeepEqual1 %t",reflect.DeepEqual(a,b)) t.Logf("map DeepEqual2 %t",reflect.DeepEqual(a,c)) //切片比较 s1 := []int{1,2} s2 := []int{1,2} s3 := []int{1,2,3} t.Logf("slice DeepEqual1 %t",reflect.DeepEqual(s1,s2)) t.Logf("slice DeepEqual2 %t",reflect.DeepEqual(s1,s3)) }
比较map 和切片直接调用DeepEqual 函数即可。
2个切片的每个元素的值和顺序一样就相等。
2个map的key 和value 的数量和值一致就相等。
网络编程 监听tcp请求模拟网络聊天 使用go的net 库可以监听tcp 或udp端口 接收网络请求。
服务端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 import ( "fmt" "net" "reflect" "unsafe" ) func main() { fmt.Println("网络监听开始-------") listern,_err := net.Listen("tcp","127.0.0.1:5001") if(_err != nil){ fmt.Errorf("错误",_err) return } //循环获取连接请求 connMap := make(map[string]net.Conn) for{ accept,accept_error := listern.Accept() if(accept_error == nil){ address := accept.RemoteAddr().String() fmt.Println(address + "执行了连接请求==>") connMap[address] = accept go doConnect(accept,connMap) }else{ fmt.Println("网络异常==>") } } } func doConnect(conn net.Conn,connectMap map[string]net.Conn) { for{ buf := make([]byte,512) len,err := conn.Read(buf) if(err == nil){ curAddress := conn.RemoteAddr().String() fmt.Println("服务器接收到数据--->"+curAddress+":"+string(buf[:len])) fmt.Println(connectMap) //遍历所有其他的连接请求 for key,value := range connectMap { if key != curAddress { fmt.Println("转发请求-->"+key) info := curAddress + ":" + string(buf[:len]) value.Write(string2BytesSlicePlus2(info)) } } } } } func string2BytesSlicePlus2(str string) []byte { strSliceHeader := *(*reflect.StringHeader)(unsafe.Pointer(&str)) byteSlice := *(*[]byte)(unsafe.Pointer(&strSliceHeader)) return byteSlice }
客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 import ( "bufio" "fmt" "net" "os" "time" ) func main() { arg := os.Args fmt.Println(arg) host := arg[1] port:= arg[2] fmt.Println("host="+host) fmt.Println("port="+port) conn,err := net.Dial("tcp",host + ":" + port) if(err != nil){ fmt.Errorf("连接失败了",err) return } //监听服务端 go func() { for{ buf := make([]byte,512) len,err := conn.Read(buf) if err != nil{ fmt.Println("读取服务端信息出现错误.....") time.Sleep(time.Second * 1) continue } fmt.Println(string(buf[:len])); } }() //监听控制台输入 inputReader := bufio.NewReader(os.Stdin) for{ data,_,err := inputReader.ReadLine() if(err != nil){ fmt.Println("发生了错误") return } conn.Write(data) } }
HTTP服务器 使用 net/http 包下的相关api 可以简单的创建一个web http 服务。
核心方法
http.ListenAndServe 监听端口和服务 http.HandleFunc 处理特定的url处理器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import ( "fmt" "net/http" ) func main() { http.HandleFunc("/", func(responseWriter http.ResponseWriter, request *http.Request) { method := request.Method fmt.Println("method->"+method) url := request.URL fmt.Println(url) aValue := request.FormValue("a") fmt.Println("a="+aValue) responseWriter.WriteHeader(200) str := "好的" responseWriter.Write([]byte(str)) }) http.HandleFunc("/index", func(responseWriter http.ResponseWriter, request *http.Request) { fmt.Println("请求了/index 路径") responseWriter.WriteHeader(500) }) http.ListenAndServe("127.0.0.1:6001",nil) }