【启程Golang之旅】网络编程与反射

欢迎来到Golang的世界!在当今快节奏的软件开发领域,选择一种高效、简洁的编程语言至关重要。而在这方面,Golang(又称Go)无疑是一个备受瞩目的选择。在本文中,带领您探索Golang的世界,一步步地了解这门语言的基础知识和实用技巧。

目录

初识网络编程

初识反射


初识网络编程

把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大、功能强的网络系统,从而使众多的计算机可以方便地互相传递信息、共享硬件、软件、数据信息等资源。设备之间在网络中进行数据的传输,发送/接收数据。

网络:两台或者两台以上计算机就可以构成网络,如下图所示:

设备之间进行传输数据的时候,必须遵循一定的通讯协议规则,具体如下所示:

接下来我们就网络通信的流程写一下网络编程实现的过程,如下:

创建客户端:调用Dial函数实现创建客户端,示例代码如下所示:

func main() {
	// 打印
	fmt.Println("客户端启动。。。")
	// 调用Dial函数,参数需要指定tcp协议,需要指定服务器端的IP+PORT
	con, err := net.Dial("tcp", "127.0.0.1:8888")
	if err != nil {
		fmt.Println("连接失败, err = ", err)
	} else {
		// 打印连接成功
		fmt.Println("连接成功, con = ", con)
	}
}

创建服务器端:调用Listen函数实现创建服务器端,示例代码如下所示:

func main() {
	// 打印
	fmt.Println("服务器端启动。。。")
	// 进行监听:需要指定服务器端TCP协议,服务器端的IP+PORT
	listen, err := net.Listen("tcp", "127.0.0.1:8888")
	if err != nil {
		fmt.Println("监听失败, err = ", err)
		return
	}
	// 监听成功,等待客户端的连接
	// 循环等待客户端的连接
	for {
		con, err1 := listen.Accept()
		if err1 != nil {
			fmt.Println("等待客户端连接失败, err = ", err1)
			return
		} else {
			// 连接成功
			fmt.Printf("等待连接成功,con = %v, 接收到的客户端信息:%v \n", con, con.RemoteAddr().String())
		}
	}
}

连接测试:接下来我们开始启动我们的客户端和服务器端,这里我们首先启动服务器端,然后启动客户端对服务器端进行访问,服务器端就等待客户端的访问连接即可,画面如下所示:

发送数据:上面演示了客户端与服务器端的连接,接下来我们开始在客户端设置一些数据然后发送给服务器端,让服务器端去接收到客户端发送过来的数据,示例代码如下所示,由于客户端发送一次数据就关闭了,所以服务器端报出客户端的连接意外关闭的err,这个忽视就行。

具体的代码如下所示:

// 客户端
package main

import (
	"bufio"
	"fmt"
	"net"
	"os"
)

func main() {
	// 打印
	fmt.Println("客户端启动。。。")
	// 调用Dial函数,参数需要指定tcp协议,需要指定服务器端的IP+PORT
	con, err := net.Dial("tcp", "127.0.0.1:8000")
	if err != nil {
		fmt.Println("连接失败, err = ", err)
	} else {
		// 打印连接成功
		fmt.Println("连接成功, con = ", con)
	}
	// 通过客户端发送单行数据,然后退出
	reader := bufio.NewReader(os.Stdin) // os.Stdin代表终端标准输入
	// 从终端读取一行用户输入的信息
	str, err := reader.ReadString('\n')
	if err != nil {
		fmt.Println("终端输入失败, err = ", err)
	}
	// 将用户输入的信息发送给服务器端
	con1, err1 := con.Write([]byte(str)) // []byte将字符串转换为字节数组
	if err1 != nil {
		fmt.Println("连接失败, err = ", err1)
	}
	// 打印发送成功
	fmt.Printf("发送成功,发送%d个字节\n", con1)
}

// 服务器端
package main

import (
	"fmt"
	"net"
)

func process(con net.Conn) {
	// 连接数据完要进行关闭
	defer con.Close()
	for {
		// 创建一个切片,用于接收客户端的数据
		buf := make([]byte, 1024)
		// 接收客户端的数据
		n, err := con.Read(buf)
		if err != nil {
			fmt.Println("接收客户端数据失败, err = ", err)
			return
		}
		// 打印接收到的数据
		fmt.Printf("接收到客户端数据:%v \n", string(buf[0:n]))
	}
}

func main() {
	// 打印
	fmt.Println("服务器端启动。。。")
	// 进行监听:需要指定服务器端TCP协议,服务器端的IP+PORT
	listen, err := net.Listen("tcp", "127.0.0.1:8000")
	if err != nil {
		fmt.Println("监听失败, err = ", err)
		return
	}
	// 监听成功,等待客户端的连接
	// 循环等待客户端的连接
	for {
		con, err1 := listen.Accept()
		if err1 != nil {
			fmt.Println("等待客户端连接失败, err = ", err1)
			return
		} else {
			// 连接成功
			fmt.Printf("等待连接成功,con = %v, 接收到的客户端信息:%v \n", con, con.RemoteAddr().String())
		}
		// 准备协程,协程内容不处理客户端服务请求
		go process(con)
	}
}

初识反射

在go语言中,反射(Reflection)是指程序在运行时检查变量的类型和值,并且可以修改这些变量的值、调用其方法以及获取其字段的信息的能力。反射是一种强大的元编程工具,可以让程序在运行时动态地获取和操作类型信息,而不需要在编译时确定。在go语言中,反射是通过reflect包来实现的,以下是反射具体能做的一些事:

1)反射可以在运行时动态获取变量的各种信息,比如变量的类型,类别等信息

2)如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法)

3)通过反射,可以修改变量的值,可以调用关联的方法。

4)使用反射,需要import("reflect")

反射相关的使用函数可以通过如下的函数进行操作:

1)reflect.TypeOf(变量名),获取变量的类型,返回reflect.Type类型

2)reflect.ValueOf(变量名),获取变量的值,返回reflect.Value类型(reflect.Value是一个结构体类型),通过reflect.Value,可以获取到关于该变量的很多信息。

对基本类型反射

package main

import (
	"fmt"
	"reflect"
)

// 利用一个函数,函数的参数定义为空接口
func testReflect(i interface{}) {
	// 1.调用TypeOf()函数,返回reflect.Type类型数据
	reType := reflect.TypeOf(i)
	fmt.Println("reType: ", reType)           // reType:  int
	fmt.Printf("reType的具体类型是: %T \n", reType) // reType的具体类型是: *reflect.rtype
	// 2.调用ValueOf()函数,返回reflect.Value类型数据
	reValue := reflect.ValueOf(i)
	fmt.Println("reValue: ", reValue)        // reValue:  100
	fmt.Printf("reValue的具体类型是: %T", reValue) // reValue的具体类型是: reflect.Value

	// reValue转成空接口
	i2 := reValue.Interface()
	// 类型断言
	n := i2.(int)
	n1 := n + 30
	fmt.Println("n1: ", n1) // n1:  130

}

func main() {
	// 对基本数据类型进行反射
	var num int = 100
	testReflect(num)
}

这里可以通过下图深入进行了解反射:

对结构体反射

package main

import (
	"fmt"
	"reflect"
)

// 利用一个函数,函数的参数定义为空接口
func testReflect(i interface{}) {
	// 1.调用TypeOf()函数,返回reflect.Type类型数据
	reType := reflect.TypeOf(i)
	fmt.Println("reType: ", reType)           // reType:  int
	fmt.Printf("reType的具体类型是: %T \n", reType) // reType的具体类型是: *reflect.rtype
	// 2.调用ValueOf()函数,返回reflect.Value类型数据
	reValue := reflect.ValueOf(i)
	fmt.Println("reValue: ", reValue)           // reValue:  100
	fmt.Printf("reValue的具体类型是: %T \n", reValue) // reValue的具体类型是: reflect.Value

	// reValue转成空接口
	i2 := reValue.Interface()
	// 类型断言
	n, flag := i2.(Student)
	if flag {
		fmt.Printf("学生的名字是:%v, 学生的年龄是: %v", n.Name, n.Age) // 学生的名字是:张三, 学生的年龄是: 18
	}
}

// 定义学生结构体
type Student struct {
	Name string
	Age  int
}

func main() {
	// 对结构体反射
	stu := Student{
		Name: "张三",
		Age:  18,
	}
	testReflect(stu)
}

得到的结果如下所示:

如果想获取变量的类别,可以看一下如下的示例代码:

package main

import (
	"fmt"
	"reflect"
)

// 利用一个函数,函数的参数定义为空接口
func testReflect(i interface{}) {
	// 1.调用TypeOf()函数,返回reflect.Type类型数据
	reType := reflect.TypeOf(i)
	// 2.调用ValueOf()函数,返回reflect.Value类型数据
	reValue := reflect.ValueOf(i)

	// 获取遍历的类别
	// (1)reType
	k1 := reType.Kind()
	fmt.Println("reType:", k1) // reType: struct
	// (2)reValue
	k2 := reValue.Kind()
	fmt.Println("reValue:", k2) // reType: struct

	// 获取变量的类型
	// reValue转成空接口
	i2 := reValue.Interface()
	// 类型断言
	n, flag := i2.(Student)
	if flag { // 类型断言成功
		fmt.Printf("结构体的类型是: %T", n) // 结构体的类型是: main.Student
	}
}

// 定义学生结构体
type Student struct {
	Name string
	Age  int
}

func main() {
	// 对结构体反射
	stu := Student{
		Name: "张三",
		Age:  18,
	}
	testReflect(stu)
}

反射修改变量的值:可以参考如下代码

package main

import (
	"fmt"
	"reflect"
)

// 利用一个函数,函数的参数定义为空接口
func testReflect(i interface{}) {
	reValue := reflect.ValueOf(i)

	// 通过SetInt方法,将i的值设置为10
	reValue.Elem().SetInt(10)
}

func main() {
	// 定义一个基本数据类型
	var num int = 100

	testReflect(&num) // 传入指针地址
	fmt.Println(num)  // 10
}

相关推荐

  1. Golang 网络编程

    2024-06-09 01:08:04       19 阅读
  2. golang网络编程day5

    2024-06-09 01:08:04       26 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-06-09 01:08:04       8 阅读
  2. 【Python教程】压缩PDF文件大小

    2024-06-09 01:08:04       9 阅读
  3. 通过文章id递归查询所有评论(xml)

    2024-06-09 01:08:04       10 阅读

热门阅读

  1. UML 统一建模语言简介

    2024-06-09 01:08:04       6 阅读
  2. 面试 Redis 八股文十问十答第四期

    2024-06-09 01:08:04       8 阅读
  3. Lua 时间工具类

    2024-06-09 01:08:04       6 阅读
  4. Ratchet websocket token 验证

    2024-06-09 01:08:04       7 阅读
  5. Composition API函数

    2024-06-09 01:08:04       6 阅读
  6. Python入门Git:探索版本控制的奥秘

    2024-06-09 01:08:04       7 阅读
  7. advices about writing promotion ppt

    2024-06-09 01:08:04       8 阅读
  8. KMeans聚类分析星

    2024-06-09 01:08:04       4 阅读
  9. 中介子方程七

    2024-06-09 01:08:04       6 阅读
  10. C++的封装(十二):外部构造函数

    2024-06-09 01:08:04       5 阅读