Day1 学习记录|青训营

106 阅读13分钟

标题:Go语言基础语法和Socks5服务器实现简介

摘要: 该代码提供了对Go编程语言的介绍,涵盖了基本语法和概念。代码分为三个主要部分:基本语法、字符串处理和简单Socks5服务器的实现。每个部分都有注释来解释代码的功能。

  1. 基本语法: 代码的第一部分介绍了基本概念,如变量声明、常量、if-else语句、循环(for和switch)、数组、切片和映射(map)。该部分涵盖了Go语言的核心语法,帮助读者熟悉Go的基本操作。

  2. 字符串处理: 第二部分介绍了Go语言中字符串的处理方法。代码演示了字符串的常见操作,如包含判断、计数、索引查找、连接、重复、替换和分割。此外,还介绍了字符串的格式化功能,类似于C语言的printf,用于将数据格式化为字符串。

  3. Socks5服务器实现: 最后一个部分展示了一个简单的Socks5服务器的实现。代码使用net包提供的TCP连接进行监听,并在收到连接请求后创建新的goroutine来处理客户端连接。这个简单的Socks5服务器只是简单地将客户端发送的数据原样返回,还没有实现完整的Socks5协议。

总结: 该Go代码向读者介绍了Go编程语言的基础知识,包括变量、常量、条件语句、循环、数据结构(数组、切片、映射)、字符串处理等。此外,还展示了一个简单的Socks5服务器实现,帮助读者了解如何使用Go语言构建网络应用程序。通过代码的注释和解释,读者可以更好地理解代码的逻辑和功能,并在此基础上继续学习和探索更复杂的Go编程。

首先是对go语法基础的学习,发现go其实挺像c和python的结合体,对于代码的讲解我都写在注释里了,文字部分就不过多赘述

package main

import (
	"fmt"
	"math"
)

func add(a int,b int) int{ // 数据类型后置
	return a + b
}

func exists(hash map[string]int,key string) (value int,ok bool){
	value,ok = hash[key]
	return value,ok
}

func add2ptr(a *int){
	*a += 2 // 这里也要加上*
}

func main() {
	// 变量的声明,三种:1.var,2.var + 数据类型,3. :=
	//第一种会自动识别你所赋值的数据类型
	//第二种是定义时就认为规定了他是什么数据类型
	//第三种与第一种同理,赋值时自动识别该变量的数据类型
	fmt.Println("hello,world")
	a := "hello"
	b := a + ", world"
	var c int = 1
	var d, e int = 2, 3
	var f float64
	fmt.Println(a,b,c,d,e,f)

	// 常量的声明 const

	const s string = "constant"
	const h = 50000
	const i = 3e20 / h
	fmt.Println(s,h,i,math.Sin(h),math.Sin(i))

	// if else 分支语句
	// if 后面必须跟大括号
	if 7 % 2 == 0 {
		fmt.Println("7 is even")
	}else{
		fmt.Println("7 is odd")
	}

	if num := 9; num < 0{
		fmt.Println(num,"is negative")	
	}else if num < 10 {
		fmt.Println(num,"balabala")
	}else {
		fmt.Println(num,"balabala")
	}

	// 循环
	// 1.死循环
	for {
		fmt.Println("nihao")
		break
	}

	// 2.标准有限循环
	for i := 0; i < 10; i ++{
		fmt.Println(i)
	}

	// switch 分支
	nums := 4
	switch nums {
	case 1:
		fmt.Println(1)
	case 2:
		fmt.Println(2)
	case 3:
		fmt.Println(3)
	case 4, 5:
		fmt.Println(4,"or",5)
	default:
		fmt.Println("other")			
	}

	// 数组
	// 两种定义方式,与变量定义类似,但数组定义时必须规定数据类型
	// 1.利用var定义数组
	var arr [5] int
	arr[4] = 4
	for i := 0; i < len(arr); i ++{
		arr[i] = i
	}
	fmt.Println(arr)
	
	// 2.利用 := 来定义数组
	brr := [5] int {1,2,3,4,5}
	fmt.Println(brr)

	// 切片(变长数组)

	//利用make来创建切片
	str := make([]string, 3)
	str[0] = "a"
	str[1] = "b"
	str[2] = "c"
	fmt.Println(str,len(str))
	str = append(str,"d")
	fmt.Println(str,len(str))

	ct := make([]string, len(str))
	copy(ct,str)
	fmt.Println(ct)
	fmt.Println(ct[2:])

	// 还可以利用 := 来创建切片,就是不规定大小的数组
	bt := []string {"a","b"}
	fmt.Println(bt,len(bt))
	
	// go切片不支持负数索引

	//map 字典(哈希表)
	//map的创建用make函数来创建
	//创建map需要两个类型,括号中的是key的类型,括号外的是value的类型
	hash_map1 := make(map[string]int)
	hash_map1["atk"] = 1
	hash_map1["xjp"] = 2
	fmt.Println("hash_map1:",hash_map1)
	_,ok := hash_map1["atk"] // 查询元素是否在哈希表中,索引一个map会返回两个值,一个是其key对应的value,第二个是该key是否存在于map中
	// 如果索引值不存在,第一个返回值会返回定义value类型的零值
	_,ok1 := hash_map1["nihao"]
	fmt.Println(ok,ok1)

	//同理,map也可以直接赋值定义
	hash_map2 := map[string]int {"nihao":2}
	fmt.Println("hash_map2:",hash_map2)

	// 若要删除字典中的元素,需要用delete函数
	delete(hash_map1,"atk")
	fmt.Println("hash_map1:",hash_map1)


	// range,用来迭代数组,切片,字典或通道的关键字,且遍历map是完全无序的,完全随机~
	for key,value := range hash_map1{
		fmt.Println(key,value)
	} // 迭代字典,两个返回值分别是key和value
	// 也可以只迭代key或者只迭代value
	// for key := range hash_map1
	// for _,value := range hash_map1

	for i, item := range arr{
		fmt.Println(i,item)
	}// 迭代数组或者切片时第一个参数自动为索引,第二个参数为当前索引的值,如果不想用索引,可以用_来代替
	for _,item := range arr{
		fmt.Println(item)
	}

	// 函数
	k := 10
	l := 20
	p := add(k,l) // add 为自己定义的一个函数
	fmt.Println(p)

	val,okk := exists(hash_map1,"xjp")
	fmt.Println(val,okk)

	// 指针 go虽然有指针,但是不如c和c++一样,go的指针用途有限,主要用于传实参
	//普通函数传参一般传的都是形参,如果需要在函数内修改的话,需要传指针类型的实参
	nu := 10
	fmt.Println(nu)
	add2ptr(&nu)
	fmt.Println(nu)

}

上述是对go基础语法的总结和练习,然后又学习了拓展知识,strings包,Json转换,还有包括结构体,时间与数字解析方面的知识

package main

import (
	"encoding/json"
	"fmt"
	"strings"
)

type point struct{
	name,password int
}

type user struct{
	Name string
	Age int
}

func main() {
	a := "hellllo"
	fmt.Println(strings.Contains(a,"ll")) // 字符串匹配,查看a是否包含第二个参数的字符串
	fmt.Println(strings.Count(a,"ll")) // 字符串计数,计算a中包含几个ll
	fmt.Println(strings.Index(a,"e")) // 查找字符串的索引下标
	fmt.Println(strings.Join([]string{"hello","world"},"-")) // 将数组中的字符串连接起来,第二个参数为连接项
	fmt.Println(strings.Repeat(a,2)) // 将字符串复制几次
	fmt.Println(strings.Replace(a,"e","E",-1)) // 将字符串中的目标字符替换为我们规定的字符,最后一个参数为替换几次,-1代表所有全部替换
	fmt.Println(strings.Split("he-ll-o","-")) // 与python的split函数一样,将连接符删掉并返回断掉后的数组
	fmt.Println(strings.ToLower(a)) // 将字符串所有字符转换为小写
	fmt.Println(strings.ToUpper(a)) // 将字符串所有字符转换为大写
	b := strings.Split(a,"") // 通过这种形式可以将字符串转化为字符数组
	fmt.Println(b)

	// 字符串格式化 就是c里面的prinf,数字%d,字符串%s,字符%c,%f为浮点数,%v为万能类型,会自动识别
	p := point{1,2}
	fmt.Printf("p=%+v\n",p)

	c := user{"atk",20}
	buf,err := json.Marshal(c) // json转码只识别大写字母开头的结构体变量
	fmt.Println(buf,string(buf),err)
	var sp user
	err = json.Unmarshal(buf,&sp)
	fmt.Println(sp)
}
```
```
package main

import (
	"errors" // 使用错误处理需要调库
	"fmt"
)

// 结构体,与c类似,不过是数据类型后置
// 类似python的类,go的结构体也可以附带一些方法
type user struct {
	name string
	password string
}

func checkpassword(people user,password string) bool {
	return password == people.password
}

func (u user) checkpassword2(password string) bool{
	return u.password == password
}

func findUser(u []user, name string) (v *user, err error){
	for _,us := range u{
		if us.name == name{
			return &us,nil
		}
	}
	return nil,errors.New("Not found")
}

func main() {
	a := user{name:"atk",password:"123"} // 结构体变量的初始化
	b := user{name:"xjp",password:"456"}
	c := user{name:"wxz"}
	c.password = "1024"
	var d user
	d.name = "szs"
	d.password = "65536"
	fmt.Println(a,b,c,d)
	fmt.Println(checkpassword(a,"123"))
	fmt.Println(a.checkpassword2("456"))


	// 错误处理
	u,err := findUser([]user{a,b},"atk")
	if err != nil{
		fmt.Println(err)
		return
	}
	fmt.Println(u.name)

	if us,err1 := findUser([]user{a,b},"nihao"); err1 != nil{
		fmt.Println(err)
		return
	}else{
		fmt.Println(us.name)
	}
	
}
```
```
package main

import (
	"fmt"
	"strconv"
	"time"
)

func main(){
	now := time.Now()
	fmt.Println(now)
	shijianchuo := now.Unix()
	fmt.Println(shijianchuo)

	f, _ := strconv.ParseFloat("1.234",64) // 将字符串转换为数字,第一个参数为待转换的字符串,第二个参数为转换的进制数,不写代表自动识别
	fmt.Println(f)

	// 也可以用Atoi来自动识别并转换为数字
	n, _ := strconv.Atoi("123.5") // Atoi只能转化整数
	fmt.Println(n)

	n1, _ := strconv.Atoi("111")
	fmt.Println(n1)

	n2 := strconv.Itoa(123)
	fmt.Println(n2)
}
```
然后进入实践练习模块,一共三个练习,第一个练习是猜数字,非常简单,这里直接放代码
```
package main

import (
	"fmt"
	"math/rand"
	"time"
	"bufio"
	"os"
	"strings"
	"strconv"
)

func main(){
	rand.Seed(time.Now().Unix())
	n := rand.Intn(101)
	for {
		fmt.Print("请输入您要猜测的数字:")
		reader := bufio.NewReader(os.Stdin)
		input,err := reader.ReadString('\n')
		if err != nil{
			fmt.Println(err)
		}
		input = strings.TrimSuffix(input,"\r\n")
		guess,err := strconv.Atoi(input)
		fmt.Print(guess)
		if guess > n{
			fmt.Println("大了")
		}else if guess < n{
			fmt.Println("小了")
		}else{
			fmt.Println("恭喜你,猜对了!")
			break
		}
	}
}

这个是根据老师的思路用os来写的,课后习题让我们用scanf来写,以下是代码

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main(){
	rand.Seed(time.Now().Unix())
	n := rand.Intn(101)
	for {
		var num int
		println("请输入您要猜的数字")
		fmt.Scanln(&num)
		if num < n{
			println("小了")
		}else if num > n{
			println("大了")
		}else{
			println("恭喜你,猜对了")
			break
		}
	}
}
  1. 字符串操作(主要部分1): 第一段代码展示了strings包提供的各种字符串操作函数。这些函数包括ContainsCountIndexJoinRepeatReplaceSplitToLowerToUpper,以及将字符串转换为字符数组。它还通过fmt.Printf展示了字符串格式化的例子,以及使用json包进行JSON编码和解码。
  2. 结构体和方法(主要部分2): 第二段代码引入了Go中的结构体和方法的概念。它定义了一个user结构体,包含namepassword字段。代码演示了常规函数和与结构体关联的方法。checkpassword函数检查给定的密码是否与用户的密码匹配,而checkpassword2方法是与user实例关联的类似方法。findUser函数在一组用户中按名称搜索用户,并返回找到的用户的指针或错误。
  3. 时间操作和数字转换(主要部分3): 第三段代码展示了时间操作和数字转换。它使用time包获取当前时间和Unix时间戳。此外,还演示了如何使用strconv.ParseFloatstrconv.Atoi将字符串转换为数字,以及如何使用strconv.Itoa将数字转换为字符串。
  4. 猜数字游戏(主要部分4和scanf版本): 最后一段代码呈现了一个简单的猜数字游戏。第一个版本使用bufio.NewReaderos.Stdin进行用户输入,并根据猜测的数字是否太大或太小提供反馈。用户会被提示继续猜测,直到猜中正确数字。
  5. 猜数字游戏(scanf版本): 猜数字游戏的第二个版本使用fmt.Scanln进行用户输入。它的功能类似于第一个版本,提示用户猜测正确的数字。

总的来说,这些代码片段提供了Go编程概念的实际示例,包括字符串操作、结构体使用、方法、错误处理、时间操作和用户输入。猜数字游戏练习是将在前面部分学到的概念应用起来的互动方式。通过实践这些练习,你可以更好地理解Go编程,并提高编码技能。

103c2b3f887b445d86d057a281c60b3.png 第二个项目是调用一个翻译的接口,我们需要这个翻译网址的url,通过Convert curl commands to Go (curlconverter.com)网站转化成go语言形式,然后编译出来Json并通过JSON转Golang Struct - 在线工具 - OKTools网站翻译Json编码,转化成结构体,来极大简化我们的代码工作量

package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"encoding/json"
	"os"
)

// 定义结构体参数
type DictRequest struct{
	TransType string `json:"trans_type"`
	Source string `json:"source"`
	UserID string `json:"user_id"`
}

// 利用Json编译出来的代码去网站上翻译出来go的结构体
type DictResponse struct {
	Rc   int `json:"rc"`
	Wiki struct {
		KnownInLaguages int `json:"known_in_laguages"`
		Description     struct {
			Source string      `json:"source"`
			Target interface{} `json:"target"`
		} `json:"description"`
		ID   string `json:"id"`
		Item struct {
			Source string `json:"source"`
			Target string `json:"target"`
		} `json:"item"`
		ImageURL  string `json:"image_url"`
		IsSubject string `json:"is_subject"`
		Sitelink  string `json:"sitelink"`
	} `json:"wiki"`
	Dictionary struct {
		Prons struct {
			EnUs string `json:"en-us"`
			En   string `json:"en"`
		} `json:"prons"`
		Explanations []string      `json:"explanations"`
		Synonym      []string      `json:"synonym"`
		Antonym      []string      `json:"antonym"`
		WqxExample   [][]string    `json:"wqx_example"`
		Entry        string        `json:"entry"`
		Type         string        `json:"type"`
		Related      []interface{} `json:"related"`
		Source       string        `json:"source"`
	} `json:"dictionary"`
}

func query(word string) {
	client := &http.Client{}
	request := DictRequest{TransType:"en2zh",Source: word}
	buf, err := json.Marshal(request)
	if err != nil{
		log.Fatal(err)
	}
	var data = bytes.NewReader(buf)
	req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
	if err != nil {
		log.Fatal(err)
	}
	req.Header.Set("authority", "api.interpreter.caiyunai.com")
	req.Header.Set("accept", "application/json, text/plain, */*")
	req.Header.Set("accept-language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")
	req.Header.Set("app-name", "xy")
	req.Header.Set("content-type", "application/json;charset=UTF-8")
	req.Header.Set("device-id", "6b28c234ecb18f4f8d7c66755c93d4f4")
	req.Header.Set("origin", "https://fanyi.caiyunapp.com")
	req.Header.Set("os-type", "web")
	req.Header.Set("os-version", "")
	req.Header.Set("referer", "https://fanyi.caiyunapp.com/")
	req.Header.Set("sec-ch-ua", `"Not/A)Brand";v="99", "Microsoft Edge";v="115", "Chromium";v="115"`)
	req.Header.Set("sec-ch-ua-mobile", "?1")
	req.Header.Set("sec-ch-ua-platform", `"Android"`)
	req.Header.Set("sec-fetch-dest", "empty")
	req.Header.Set("sec-fetch-mode", "cors")
	req.Header.Set("sec-fetch-site", "cross-site")
	req.Header.Set("user-agent", "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Mobile Safari/537.36 Edg/115.0.1901.183")
	req.Header.Set("x-authorization", "token:qgemv4jr1y38jyq6vhvi")
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	bodyText, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	if resp.StatusCode != 200{
		log.Fatal("bad",resp.StatusCode)
	}
	var dictResponse DictResponse
	err = json.Unmarshal(bodyText,&dictResponse)
	if err != nil{
		log.Fatal(err)
	}
	fmt.Println(word,"UK:",dictResponse.Dictionary.Prons.En,"US:",dictResponse.Dictionary.Prons.EnUs)
	for _,item := range dictResponse.Dictionary.Explanations{
		fmt.Println(item)
	}
}

func main(){
	if len(os.Args) != 2{
		os.Exit(1)
	}
	word := os.Args[1]
	query(word)
}

最后一个项目是socks5 一阶段代码:

package main

import (
	"bufio"
	"log"
	"net"
)

func main() {
	server, err := net.Listen("tcp", "127.0.0.1:1080")
	if err != nil {
		panic(err)
	}
	for {
		client, err := server.Accept()
		if err != nil {
			log.Printf("Accept failed %v", err)
			continue
		}
		go process(client)
	}
}

func process(conn net.Conn) {
	defer conn.Close()
	reader := bufio.NewReader(conn)
	for {
		b, err := reader.ReadByte()
		if err != nil {
			break
		}
		_, err = conn.Write([]byte{b})
		if err != nil {
			break
		}
	}
}

Day1总结: 在这次学习中,我对Go语言有了初步的了解,并且深感它的简洁和灵活性。在代码的注释和解释下,我逐步理解了Go语言的基本特性和语法结构,如变量声明、条件语句、循环、数组、切片、映射等,这些都是编程中不可或缺的基础知识。

首先,Go语言的变量声明方式让我觉得很舒服,无论是使用var关键字还是简洁的:=符号,都能快速定义变量并赋予初值。另外,Go语言对类型推断的支持也让代码更加简洁,不需要显式指定变量的类型,编译器会根据赋值的数据自动推断出变量类型,这让代码看起来更加清晰。

其次,Go语言的流程控制语句很直观,if-else语句和switch语句的使用让代码逻辑更加清晰。而for循环也有多种形式,支持死循环和有限循环,非常灵活。这使得编写循环结构变得非常方便,而且代码看起来更加简洁。

在字符串处理部分,我学会了Go语言中常见的字符串操作方法,如Contains、Count、Index、Join、Repeat、Replace和Split等。这些方法可以帮助我在实际开发中更加高效地处理字符串数据,比如匹配和替换文本内容,连接和分割字符串等。另外,通过字符串的格式化功能,我可以根据需要将数据格式化为字符串输出,类似于C语言中的printf函数。

通过实现简单的Socks5服务器,我对网络编程有了初步了解。虽然这个Socks5服务器还只是一个简单的原样返回数据的实现,并没有涵盖完整的Socks5协议,但是这个例子让我知道了如何使用Go语言创建网络应用程序。使用net包提供的TCP连接进行监听,并在收到连接请求后创建新的goroutine来处理客户端连接,这种并发模型让网络编程变得更加高效。

在学习过程中,我还了解了一些Go语言标准库中常用的包,比如json包,它提供了JSON编码和解码功能,让我可以方便地处理JSON数据。通过json.Marshal和json.Unmarshal函数,我可以将结构体转化为JSON格式的数据,以及将JSON数据解码为Go语言的结构体,这种转换非常灵活。

总体而言,学习Go语言的基础语法和简单的网络编程让我对这门语言有了初步的认识和体验。我深感Go语言的设计理念简洁高效,且具有很强的并发支持,这使得它在处理高并发的网络应用方面表现出色。通过学习这篇代码和进行实践练习,我对Go语言的学习充满了兴趣,我期待着深入学习更多高级特性和实际应用,将Go语言应用于真实的项目中。同时,通过代码的注释和实践练习,我也发现自己对编程的理解和能力有了进一步的提升,这让我对未来的编程之路更加充满信心。 Go编程练习与实践:探索多个领域的代码应用

在现代软件开发领域中,Go语言以其简洁高效的特性迅速崭露头角。作为一种编译型语言,Go在性能、并发和易用性方面都表现出色。为了深入了解Go语言的多个方面,我们进行了一系列的编程练习和实践。本文将回顾并总结这些练习,涵盖了字符串操作、结构体、方法、网络编程、错误处理、时间操作、JSON解析和代理服务器等多个领域。

字符串操作与JSON编解码

在Go语言中,字符串操作是基本且常用的功能。通过strings包,我们可以轻松地进行包含、计数、查找、替换、分割等操作。字符串格式化则使用fmt.Printf函数,让输出更加美观。另外,json包提供了方便的JSON编码和解码功能,使数据在不同应用间传递更加灵活。通过实例,我们掌握了这些字符串操作和JSON编解码的技巧。

结构体与方法

Go的结构体和方法机制为面向对象编程提供了强大的支持。我们通过创建user结构体以及结构体方法来模拟用户操作,展示了Go中类似于面向对象的概念。通过checkpassword函数和checkpassword2方法,我们体会到了函数和方法在操作数据时的灵活性。此外,findUser函数的实现展示了如何在结构体切片中查找特定元素,并引入了错误处理的概念。

时间操作与数字转换

在现实世界的应用中,时间操作和数字转换是常见的需求。通过timestrconv包,我们学会了如何获取当前时间、转换时间戳,并实现字符串与数字的相互转换。这对于处理时间戳和用户输入的数字等场景非常有用。通过演示不同的转换方法,我们体会到了Go语言中的灵活性和便捷性。

网络编程与HTTP请求

网络编程在现代应用中至关重要。我们探索了Go语言中的网络编程能力,创建了一个简单的SockS5代理服务器。这个代理服务器监听本地地址,接受传入连接并将数据回传给客户端。此外,我们还使用net/http包发送了HTTP POST请求,实现了向翻译API查询单词的功能。通过这些网络编程的实践,我们对Go语言中网络操作的基本原理和实现有了更深刻的理解。

错误处理与代码可靠性

在任何编程语言中,错误处理都是至关重要的一环。我们在多个练习中运用了错误处理,例如在HTTP请求时检查状态码,或者在JSON解析时捕获错误。通过使用log.Fatalpanic函数,我们可以捕获错误并进行适当的处理,以确保代码的可靠性和稳定性。这也教会了我们如何在实际应用中处理和报告错误。

综合实践:猜数字游戏和SockS5代理服务器

我们通过实现猜数字游戏和SockS5代理服务器两个综合实践,将之前学到的知识融会贯通。猜数字游戏通过不同的用户输入方式展示了如何处理用户交互,并使用循环和条件语句进行逻辑控制。SockS5代理服务器则涵盖了网络编程的方方面面,从监听端口到处理连接,再到传输数据,体现了Go语言在网络领域的强大表现。

总结与展望

通过这些练习与实践,我们深入了解了Go语言的多个核心概念和应用领域。字符串操作、结构体、方法、网络编程、时间操作、JSON解析和错误处理等方面的知识都得到了实际应用。同时,我们也体会到了Go语言的简洁性、高效性和易用性。这些练习不仅帮助我们熟悉了解了Go语言的基本特性,还为我们日后在实际项目中的开发提供了有力的支持。

未来,我们可以进一步探索Go语言在更广泛领域的应用,如Web开发、并发编程、数据库操作等。在不断的学习和实践中,我们将不断提升自己的编程技能,创造出更多实用、高效的应用程序。Go语言的未来充满了无限可能,而我们将继续踏上这个令人兴奋的编程之旅。