反射

反射使用的库函数为 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)
}