这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天
由于在之前已经接触了一些go语言相关的知识,所以本次仅记录感觉比较有收获的部分。
这里只记录感觉比较有收获的部分
1. 语法知识点部分
1.1 slice切片
golang中比较少用数组,切片用的比较多,切片相当于是可变长度的数组,可以使用如python一样的切片操作。
s := make([]string, 1) //make方式创建
s[0] = "a"
s = append(s, "d")
s = append(s, "e", "f")
切片可以使用append增加元素,之后 记得把append后的结果赋值回原来的对象,因为golang中slice的原理是存储一个长度+容量+指向数组的指针,如果append过程中长度不够,就会发生扩容并产生一个新的slice,并返回,所以我们需要赋值回原来的s对象。
由此延伸出golang中slice的底层实现,具体详细描述slice和数组实现的可以看一下:juejin.cn/post/686189…
切片底层结构如下所示:
type Slice struct {
ptr unsafe.Pointer // 指向数组的指针,表明数组的起始元素 Array pointer
len int // 长度 slice length
cap int // 容量 slice capacity
}
切片本身并不是动态数组或者数组指针。它内部实现的数据结构通过指针引用底层数组,设定相关属性将数据读写操作限定在指定的区域内。切片本身是一个只读对象,其工作机制类似数组指针的一种封装。
切片(slice)是对数组一个连续片段的引用,所以切片是一个引用类型(因此更类似于 C/C++ 中的数组类型,或者 Python 中的 list 类型)。这个片段可以是整个数组,或者是由起始和终止索引标识的一些项的子集。需要注意的是,终止索引标识的项不包括在切片内。切片提供了一个与指向数组的动态窗口。
给定项的切片索引可能比相关数组的相同元素的索引小。和数组不同的是,切片的长度可以在运行时修改,最小为 0 最大为相关数组的长度:切片是一个长度可变的数组。 中间说到golang中只有值传递,所以像slice这种很有必要
由于上述切片的性质,即记录的其实是对应的指针、长度、容量,所以可以用在函数调用中,通过函数传递切片时,不会拷贝整个切片,因为切片本身只是个结构体而已。"在golang中只存在值传递,也就是说既然只存在值传递,那么数组在作为形参的时候,将产生副本。为了解决这个问题,就需要引入切片。"即通过切片可以以较少的代价来实现参数传递,同时又能够修改到目标位置的值。
另外文中提到了其他点:
- 不要混淆nil切片和空切片,空切片只是len为0,但是指针是指向一段真正存在的内存空间的。nil切片你可以认为里面的ptr是nil默认值。
关于copy操作:
-
对于切片类型来说,copy操作和赋值操作不一样,copy操作会对slice具体指向地址的数值进行修改,而赋值操作应该只是更换了指向的地址以及长度等字段。
-
在使用内置函数
copy()
拷贝切片时,会将源切片的数据逐个拷贝到目的切片指向的数组中,拷贝数量取两个切片长度的最小值。故copy过程中不会发生扩容。“这也是最容易让新手犯错和调入陷阱的地方,copy并不是单纯的深拷贝!其他语言中的copy应该是字面意思,完全复制一份相同的切片或者数组拷贝。但是在golang中,copy只能复制两个切片中较小长度数量的元素。至于剩下的元素不会发生拷贝。”
1.2 map
map同样是使用make创建,不赘述
golang中的map是无序的,遍历的时候不会按照字母顺序或插入顺序遍历,而是一个偏随机的顺序
1.3 函数
golang中函数参数声明中 变量类型后置
如果要在函数中修改值:
func add2(n int) {
n += 2
}
func add2ptr(n *int) {
*n += 2
}
func main(){
n := 5
add2(n)
add2ptr(&n)
}
第二个函数能够修改main函数中n的值,而第一个函数不行
1.4 json
对于一个结构体,我们只需要保证 每一个字段都是大写,也就是每一个字段都是公开的,那么这个结构体就能够直接调用 json.Marshal 方法来直接进行序列化
1.5 strconv
字符串和数字类型的相互解析
1.6 获取进程信息
os.Args
os.Getenv
os.Setenv
exec.Command
等等
1.7 bufio包
在项目示例中,有:
reader := bufio.NewReader(os.Stdin)
这行代码的作用是:通过缓冲提高效率,io操作本身的效率并不低,低的是频繁的访问本地磁盘的文件。所以bufio就提供了缓冲区(分配一块内存),读和写都先在缓冲区中,最后再读写文件,来降低访问本地磁盘的次数,从而提高效率。
关于bufio可以看一下:可以看一下:zhuanlan.zhihu.com/p/73690883
还有这一篇(感觉更通俗一点):studygolang.com/articles/35…
bufio在要读取的长度和缓存区长度的不同情况下,处理是不同的,中间处理方式值得一看
2 实例中的点
2.1 获取api的方法 以及 相关的代码生成(第三方)
获取api的方法,在浏览器中通过按键点击先发起一次请求,然后在开发者模式中,在network部分找到对应的请求
然后点击 headers perview等查看本次请求的请求信息和响应信息
由此我们可以获取到api的使用方法。
然后这里教了一个比较方便的方法,就是 代码生成 使用curl,在右键后选择 copy as cURL:
其实这个就是curl的命令,此时我们是可以直接使用terminal来发起这个请求的:
而这里为了能够内嵌到代码里面,我们到 curlconverter.com 这个网址,选择go语言,粘贴里面的内容就可以得到对应的go语言代码:
灰常好用
使用api (还是使用代码生成)
这里说到了,要能够发送不同的请求,那么就需要构建对应的结构体进行json序列化处理,所以这里要构建每个字段都是大写的结构体
然后针对于响应体,即返回信息的处理:
- 这里说到了go是一种强类型语言,一般获取返回结果的话方法不太一样
- 在js和python语言里面,body返回的一般都是字典或map结构,可以使用方括号和点来取值
go一般使用的是创建和返回结果对应结构体,然后把字符串反序列化到结构体中,但是由于api对应的响应体比较多 ,所以这里同样使用了 代码生成的方式
这里使用了 oktools.net 来帮助转化
我们找到前面的网页,然后复制对应请求的响应信息,即 preview部分的信息,然后进行转换:
这样就通过第三方完成了这部分结构体的构建,灰常好用
然后由于我们不需要对返回结果进行过多的操作,所以我们这里选择“转换-嵌套”这个选项即可,这样嵌套的结构体会紧凑一些。
2.2 关于socks5协议
2.2.1 背景 和 应用
本次了解了socks5协议,可以看一下 nordvpn.com/zh/blog/soc…
socks5是代理协议,但是没法用来翻墙,因为这是明文传输的。
socks5历史比较久远,诞生于互联网早期。
企业内网为了保证安全性,可能会使用很复杂的防火墙策略。但是这样的副作用就是,就算是管理员自身,访问也非常麻烦,而socks5协议就是在防火墙中开一个口子,让社群用户可以通过单个端口访问内部的所有资源。
实际上很多翻墙软件最终暴露出来的也是一个socks5端口。
爬虫容易遇到ip访问次数发生限制的情况,此时就可以使用一些代理ip池,而中间很多的协议就是socks5协议。
2.2.2 简述简单socks5服务器工作原理
正常没有socks5的情况下,client端的访问过程谁:建立TCP三次握手,握手完成之后,正常发起http请求,服务器返回http响应。而如果使用了socks5,则客户端先和socks5服务器先建立TCP链接,然后再和真正的服务器建立连接。
大致有下面四个阶段:
-
握手:client和socks5服务器之间握手,设置版本、认证方式等;socks5服务器会返回选择的认证方式。就是client给出自己可以用的多种认证方式,然后socks5服务器会在中间选择自己要的一种认证方式
-
认证:本次实现不进行认证,socks5服务器实现的是不加密的方式(即直接选择不加密的认证方式)
-
请求:client向socks5发送请求,告诉说要和谁建立连接,然后socks5服务就是对应的真正服务器建立连接
-
relay:client传输要发送的数据,socks5服务器将对应真正服务器的返回信息交给client端,socks5服务器并不关心中间信息的具体细节。