留神
因为系列文章仅是我在浏览BHG时进行的一些记录,并不是一个很谨严的教程,且本章题目会追随书的章节目录。
TCP协定
传输控制协议(Transmission Control Protocel,TCP)是面向连贯的牢靠通信的次要规范,以及古代网络的根底。BHG要求咱们可能从TCP的工作原理动手,开发能够辨认关上/敞开的端口,通过端口转发绕过进口限度。
TCP的握手机制
分为以下三种状况
- 端口凋谢
如果端口是凋谢的,就会进行经典的三次握手:syn->syn-ack->ack; - 端口敞开
如果端口敞开,服务器会以rst数据包而非syn-ack数据包进行响应; - 防火墙过滤
如果流量被防火墙过滤,客户端通常不会从服务器收到任何响应;
编写TCP扫描器
通过编写一个端口扫描器去了解TCP握手过程以及3种端口状态,以确定TCP端口是否可用,或者已敞开和应用过滤状态进行响应
Go的net包
通过net.Dial(network, address string),启动客户端到服务端的连贯
network参数:字符串类型,用于标识要启动的连贯的类型。Dial不仅实用于TCP,还能够用于创立UNIX套接字、UDP和第4层协定的连贯
address参数:字符串类型,标识要连贯的主机。对于IPv4/TCP连贯,应用host:port的模式
返回值:Conn,err;若连贯胜利,err为nil
示例:根本的单端口扫描器
package mainimport ( "fmt" "net")func main() { _, err := net.Dial("tcp", "scanme.namp.org:80") if err == nil { fmt.Println("Connection successful") }}
示例:非并发多端口扫描器
package mainimport ( "fmt" "net")func main() { for i := 1; i <= 1024; i++ { address := fmt.Sprintf("scanme.nmap.org:%d", i) connm err := net.Dial("tcp", address) if err != nil { continue } conn.Close() fmt.Println("%d open\n", i) }}
示例:并发扫描器
package mainimport ( "fmt" "net" "sync")func main() { //同步计数器 var wg sync.WaitGroup //扫描前1024个端口 for i := 1; i < 1024; i++ { wg.Add(1) //启动goroutine go func(j int) { //延后至外层函数返回时执行 defer wg.Done() address := fmt.Sprintf("scanme.nmap.org:%d", j) conn, err := net.Dial("tcp", address) if err != nil { return } conn.Close() fmt.Printf("%d open\n", j) }(i) } //函数阻塞,期待同步计数器为0 wg.Wait()}毛病:扫描过多的主机或端口,可能会导致网络或零碎限度,造成后果不正确
应用goroutine池治理正在进行的并发工作,通过for循环创立肯定数量的worker goroutine作为资源池
示例:workerimport ( "fmt" "net" "sort")//parameter://ports: int类型的通道,用于接管工作//results: int类型的通道,用于接管后果func worker(ports, results chan int){ for p := range ports { address := fmt.Sprintf("scanme.nmap.org:%d", p) conn, err = net.Dial("tcp", address) if err != nil { // 端口敞开,接管0 results <- 0 continue } conn.Close() // 端口关上,接管端口号 results <- p }}func main() { //应用make函数创立通道 ports := make(chan int, 100) results := make(chan int) var openports []int // cap函数计算通道的容量 // for循环启动所需数量的worker goroutine for i := 0; i < cap(ports); i++ { go worker(ports, results) } // 匿名函数 go func() { for i := 1; i <= 1024; i++ { ports <- i } }() for i := 0; i < 1024; i++ { port := <-results if port != 0 { openports = append(openports, port) } } close(ports) close(results) // 切片排序 sort.Ints(openports) for _, port := range openports { fmt.Printf("%d open\n", port) }}
结构TCP代理
构建回显服务器(仅回显给定响应到服务器)
io.Reader
type Reader interface { Read(p []byte) (n int, err error)}
io.Writer
type Writer interface { Write(p []byte) (n int, err error)}
net.Conn:Go面向流的网络连接
Conn实现了针对接口Reader和Writer定义的函数Read([]byte)和Write([]byte),即Conn即是Reader也是Writer---->TCP是双向连贯,能够发送或接收数据
回显服务器构建流程
1.应用net.Listen(network, address string)在特定端口上关上TCP监听器2.listener.Accpet()期待客户端连贯胜利后,创立并返回一个Conn对象,应用该对象接管和发送数据