# TCP-SCANNER **Repository Path**: pagliacci1337/tcp-scanner ## Basic Information - **Project Name**: TCP-SCANNER - **Description**: 学习杨旭老师的项目视频 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: https://www.bilibili.com/video/BV13K4y1a7dt/?spm_id_from=333.999.0.0&vd_source=630785c0523269065bf703207f002a47 - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-02-28 - **Last Updated**: 2023-03-01 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README - TCP,也就是***传输控制协议***(Transmission Control Protocol)。 --- >这是一个使用Go语言编写的TCP端口扫描器程序,其中包含了两个版本的端口扫描方法。 第一个版本是非并发扫描方法,使用了一个for循环遍历所有要扫描的端口,然后逐个进行扫描并输出扫描结果。 第二个版本是使用并发的方法进行扫描。在主函数中使用了一个等待组(sync.WaitGroup),在for循环中使用了匿名函数来实现并发扫描。每个匿名函数负责扫描一个端口,如果该端口开放,则输出扫描结果。使用等待组来等待所有并发的任务完成后再结束程序,并输出扫描的总耗时。 该程序使用了net包来进行TCP连接,并使用了fmt包来进行输出。在使用并发扫描的过程中,使用了sync包中的等待组来实现并发任务的等待。 --- ## 这个项目涉及了以下知识点: - Go 语言的基础语法 - 并发编程 - 网络编程:TCP 连接、IP 地址、端口等 - 通道和通道缓冲区 - 切片和排序 ## 作为面试官,我可能会就以下问题向面试者提问: - 1. 为什么使用 `net.Dial` 进行 TCP 连接测试? 答案: `net.Dial` 可以用于建立 TCP 连接,并返回一个 `net.Conn` 对象,因此可以使用该函数进行 TCP 连接测试。 - 2. 为什么使用并发的方式进行 TCP 连接测试? 答案:并发可以提高程序的效率,尤其是在大规模的 TCP 连接测试中,通过并发可以同时测试多个目标地址的连接状态,可以大大缩短测试的时间。 - 3. 为什么使用 `sync.WaitGroup` 进行并发控制? 答案: `sync.WaitGroup` 是一个同步工具,可以用于等待一组并发操作完成后再继续执行其他操作。在本例中,可以使用 `sync.WaitGroup` 来等待所有的并发连接测试任务完成后再输出总共用时。 程序运行时需要输入测试地址吗? 答案:不需要。在程序中已经硬编码了需要测试的 IP 地址和端口范围。如果需要测试其他 IP 地址和端口,可以直接修改程序中的代码。 - 4. 为什么使用并发扫描比非并发扫描更快? 答:并发扫描可以同时进行多个连接的建立和关闭,避免了单线程下的等待时间,从而提高了效率。 - 5. 在代码中使用了sync.WaitGroup,请简述其作用。 答:sync.WaitGroup用于等待一组goroutine的执行完成,当所有goroutine执行完毕后,主函数才会结束。在本代码中,使用sync.WaitGroup保证了所有扫描操作都完成后再输出程序执行时间。 - 6. 为什么扫描的端口范围是21到120? 答:这个范围是作者设定的,可能是因为作者想要扫描一些常用的端口。一般情况下,端口范围会根据具体的需求进行设定,例如扫描所有的常用端口(1到65535)或者只扫描某一个特定的端口。 - 7. 为什么在 `worker()` 函数中使用 `continue` 而不是 `return` 来结束迭代? 答:因为 `worker()` 函数是在一个 goroutine 中运行的,如果使用 `return` 语句来结束迭代,那么会直接退出 goroutine,导致该 goroutine 不再接收新的任务。使用 `continue` 语句来结束迭代,可以确保 goroutine 仍然可以接收新的任务。 - 8. 如果将 `res` 通道的缓冲区大小改为 1,会发生什么? 答:当 `res` 通道的缓冲区大小为 1 时,如果有多个 worker 函数同时完成任务,其中的一些结果可能会被覆盖。如果在处理扫描结果时需要保证结果的完整性,那么需要使用一个更大的缓冲区,或者使用无缓冲通道来避免数据丢失。 - 9. 在 `worker()` 函数中,为什么要使用 `fmt.Sprintf()` 函数来构建地址字符串? 答:因为在 Go 中,字符串和其他数据类型之间不能直接进行拼接操作。使用 `fmt.Sprintf()` 函数可以方便地将其他数据类型转换为字符串,并将它们拼接在一起。 - 10. 如果要增加这个扫描器的并发性能,可以怎么做? 答:可以通过以下方式来增加扫描器的并发性能: 调整 worker 数量:根据实际情况调整 `worker()` 函数的数量,以利用多核 CPU 的优势。 使用连接池:将 TCP 连接池化,以减少连接创建和销毁的开销。 使用更高效的扫描技术:使用更高效的端口扫描技术,如 SYN 扫描、TCP 连接扫描等。 使用更灵活的通道选择机制:使用 `select` 语句等更灵活的通道选择机制来处理通道阻塞的问题,以提高扫描速度。 - 11. 在并发编程中,如何避免竞态条件? 答:竞态条件(Race Condition)指多个线程对同一数据进行读写操作,由于执行顺序不确定,结果可能无法预测。为了避免竞态条件,可以采用以下方法: 互斥锁:通过互斥锁(Mutex)来限制同一时间只有一个线程能够访问共享资源。 读写锁:当多个线程同时读取同一个资源时,可以使用读写锁(RWMutex)来提高并发效率。 原子操作:通过原子操作来保证某个变量的读写操作的原子性,从而避免竞态条件。 - 12. 在网络编程中,如何保证数据传输的可靠性? 答:网络编程中数据传输的可靠性是一个重要的问题。为了保证数据传输的可靠性,可以采用以下方法: TCP 协议:TCP 是一种可靠的传输协议,它通过连接管理、数据分段、确认机制、超时重传等机制保证了数据的可靠传输。 序列号与确认机制:TCP 协议采用序列号和确认机制来保证数据的传输可靠性。发送方将每个数据段都打上序列号,并等待接收方的确认信息。接收方收到数据后,会回复确认信息,并将下一个期望收到的数据段的序列号告诉发送方。如果发送方在超时时间内没有收到确认信息,则会重新发送数据段。 滑动窗口机制:TCP 协议还采用滑动窗口机制来提高数据的传输效率。发送方和接收方都维护着一个滑动窗口,用于控制数据的发送和接收。发送方通过滑动窗口来控制发送数据的数量和速度,接收方通过滑动窗口来控制可以接收的数据段数量和顺序。 快速重传和快速恢复机制:TCP 协议还采用了快速重传和快速恢复机制,用于处理数据丢失和网络拥塞等问题。当接收方发现数据丢失时,会发送重复的确认信息,以便发送方尽快重新发送数据。当发送方发现网络拥塞时,会通过快速恢复机制减少发送数据的数量,以便网络尽快恢复。 ---