Go 实现 SOCKS5 代理 | 青训营笔记

4,296 阅读3分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第 1 篇笔记

闲话

很高兴看到青训营第一堂课就能介绍 SOCKS5 这么实用的技术,由于某些原因这项技术与我们的日常生活可谓是密不可分。本堂课的另外两个题目个人感觉就是 “Hello World” 和读 doc 调包,因此选择第三个题目写一篇笔记。作为一个非科班小白,分享一下对这个题目的学习过程。(P.S. 这个网页版 Markdown 编辑器还挺好用的)

SOCKS5

The protocol described here is designed to provide a framework for client-server applications in both the TCP and UDP domains to conveniently and securely use the services of a network firewall. The protocol is conceptually a "shim-layer" between the application layer and the transport layer, and as such does not provide network- layer gateway services, such as forwarding of ICMP messages. (RFC 1928)

从 RFC 文件中可以看出,SOCKS5 是一种工作在应用层和传输层之间的协议,通俗一点讲可以起到接管应用层流量并进行转发的作用,这也可以解释为什么我们开启 SOCKS5 代理时,ping 或者 tracert 命令不起作用,因为它们工作在更低层。此外相比于 SOCKS4,增加了基于 SSH 的验证功能和对 UDP 流量的支持,应用性更广也更加安全。

Go 语法

在学习 Go 之前,我只接触过 C/C++、Java、Python 这三种语言,Go 的基础语法跟这三位可谓是相差甚远,引起不适。因此我通过学习示例代码,挑出了几个关键的语法记录如下:

  1. defer

defer statement pushes a function call onto a list. The list of saved calls is executed after the surrounding function returns. Defer is commonly used to simplify functions that perform various clean-up actions.

defer的功能是让修饰的语句在周围函数执行结束后再执行,其主要作用就是简化代码,例如不用单独考虑文件的关闭等等。此外defer具有以下三条特性:1) 执行defer语句时,相关变量的值为defer当前所在行的值;2) defer的执行顺序为先进后出;3) defer修饰函数内的匿名函数时,可以修改函数的返回值。

  1. panic, recover

Panic is a built-in function that stops the ordinary flow of control and begins panicking.

Recover is a built-in function that regains control of a panicking goroutine.

简而言之panic就是让程序立刻返回,除了defer修饰的语句以外都不再执行,而recover则负责接住panic的返回值。关于这三者的搭配,官方给了一个看着很恶心的例子,小白的我实在是看得头疼,但是也想到了一个可能的应用:以前在用 C 写带剪枝的深搜时,特别希望能有一个语法,判断我找到了答案,然后一口气返回到底,然而实际上总要用一个全局变量 flag 来实现。

  1. go

go starts a goroutine, which is managed by golang run-time. - WiSaGaN

两个字母实现“多线程”,太爽了。想起我之前做的一个 bot,费了很大功夫才实现“既保持隔一段时间发送一个 http 请求,又持续监听端口”。在这只需要go一下就好了。

  1. log, fmt

使用log输出信息相比fmt主要有两个好处:1) 有时间信息;2) 线程安全。

  1. []byte, string

[]byte相比string多了一个容量信息,并且是可以动态增长的。具体可参考 []byte vs string in Go

  1. chan, <- channel 是一种传递数据的管道(队列),主要应用在多线程中。<-是将 channel 中的值赋给变量。

  2. 调用其他源码文件中的函数

我不太喜欢写一个很长的 main,所以我把各个函数分成不同的文件。在 Go 里只要源码文件位于同一个 package 里面就可以直接调用,很方便,GoLand 还会自己管理 import,++方便。