留神

因为系列文章仅是我在浏览BHG时进行的一些记录,并不是一个很谨严的教程,且本章题目会追随书的章节目录。


TCP协定

传输控制协议(Transmission Control Protocel,TCP)是面向连贯的牢靠通信的次要规范,以及古代网络的根底。BHG要求咱们可能从TCP的工作原理动手,开发能够辨认关上/敞开的端口,通过端口转发绕过进口限度。

TCP的握手机制

分为以下三种状况

  1. 端口凋谢
    如果端口是凋谢的,就会进行经典的三次握手:syn->syn-ack->ack;
  2. 端口敞开
    如果端口敞开,服务器会以rst数据包而非syn-ack数据包进行响应;
  3. 防火墙过滤
    如果流量被防火墙过滤,客户端通常不会从服务器收到任何响应;

编写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对象,应用该对象接管和发送数据