这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天
基础知识我们都可以通过看书获得,那么我的笔记主要是整理对项目代码的理解。
v1版本:
/*
这段代码是一个简单的 TCP 服务器。它在本地监听地址 "127.0.0.1:1080" ,接受客户端的连接,并在新的 goroutine 中处理每个客户端的请求。对于每个客户端,服务器读取客户端发来的字节,并将其原样返回给客户端
*/
package main
import (
"bufio"
"log"
"net"
)
func main() {
/*
这段代码创建了一个 TCP 服务器,它在 IP 地址 "127.0.0.1" 和端口 1080 上进行监听。
使用 net.Listen() 函数创建了一个新的监听器(服务器),用于指定的网络和地址。
在这种情况下,网络是 "tcp",地址是 "127.0.0.1:1080"。变量 "server" 被赋值为函数返回的监听器。
*/
server, err := net.Listen("tcp", "127.0.0.1:1080")
if err != nil {
panic(err)
}
/*
这段代码是一个循环,它在之前创建的服务器上监听传入连接。在循环内部,调用了 server.Accept() 函数,它等待客户端连接并返回连接(存储在 "client" 变量中)和错误(存储在 "err" 中)。
如果发生错误,使用 log.Printf() 函数输出错误消息,并继续循环的下一次迭代。如果没有错误,使用 go 关键字启动一个新的 goroutine,并将 "process" 函数作为参数传递给 "client" 连接。这使得服务器可以同时处理多个客户端连接。
*/
for {
client, err := server.Accept()
if err != nil {
log.Printf("Accept failed %v", err)
continue
}
go process(client)
}
}
/*
"process"的函数,它接受一个网络连接 "conn" 作为参数。
它有一个defer语句,在函数结束时关闭连接。然后创建一个bufio.NewReader,并将它与连接关联
这个循环读取输入流中的每一个字节,读取字节时如果有错误就break,并将读取的字节写入输出流,如果写入错误就break.
*/
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
}
}
}
v2版本:
/*
在main函数中,使用net.Listen()函数创建了一个监听器,监听IP地址"127.0.0.1"和端口1080。
在循环中,调用server.Accept()函数等待客户端连接,并在没有错误时启动新的goroutine处理连接。
process函数负责处理客户端连接。它首先关闭连接,然后使用bufio.NewReader将客户端连接与读取器关联。该函数调用auth函数进行身份验证,如果失败则输出错误信息。
auth函数负责身份验证。它从读取器中读取协议版本、支持的认证方法数量和支持的认证方法。如果收到的协议版本不是SOCKS5,则返回错误。该函数返回错误或nil。
这个程序的运行,客户端发送连接请求到服务端,服务端会进行验证,如果验证成功,则返回验证成功的信息给客户端。
*/
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)
err := auth(reader, conn)
if err != nil {
log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
return
}
log.Println("auth success")
}
/*
它接受一个读取器和一个网络连接作为参数。
它首先从读取器中读取协议版本,如果读取失败或协议版本不是SOCKS5,则返回错误。
然后读取支持的认证方法数量,接着读取支持的认证方法,并将其打印。最后发送回应给客户端,表示不需要认证,返回nil或错误。
*/
func auth(reader *bufio.Reader, conn net.Conn) (err error) {
// +----+----------+----------+
// |VER | NMETHODS | METHODS |
// +----+----------+----------+
// | 1 | 1 | 1 to 255 |
// +----+----------+----------+
// VER: 协议版本,socks5为0x05
// NMETHODS: 支持认证的方法数量
// METHODS: 对应NMETHODS,NMETHODS的值为多少,METHODS就有多少个字节。RFC预定义了一些值的含义,内容如下:
// X’00’ NO AUTHENTICATION REQUIRED
// X’02’ USERNAME/PASSWORD
ver, err := reader.ReadByte()
if err != nil {
return fmt.Errorf("read ver failed:%w", err)
}
if ver != socks5Ver {
return fmt.Errorf("not supported ver:%v", ver)
}
methodSize, err := reader.ReadByte()
if err != nil {
return fmt.Errorf("read methodSize failed:%w", err)
}
method := make([]byte, methodSize)
_, err = io.ReadFull(reader, method)
if err != nil {
return fmt.Errorf("read method failed:%w", err)
}
log.Println("ver", ver, "method", method)
// +----+--------+
// |VER | METHOD |
// +----+--------+
// | 1 | 1 |
// +----+--------+
_, err = conn.Write([]byte{socks5Ver, 0x00})
if err != nil {
return fmt.Errorf("write failed:%w", err)
}
return nil
}
v3,v4版本涉及的知识点其实很大部分是重复的,只是优化了一下。