分析某款go端口扫描器之三

news2024/12/23 10:24:12

一、概述

前两篇主要分析些工具集,已经针对web服务的指纹和端口指纹信息进行识别,并没有真正开始扫描。本篇主要分析如何进行IP存活探测以及tcp扫描实现。

项目来源:https://github.com/XinRoom/go-portScan/blob/main/util/file.go

二、/core/host/ 目录(进行主机的ping探测存活)

/core/host/ping.go

此代码主要是用来进行主机存活的。

首先设置判断变量和常见端口列表

var CanIcmp bool // 用于标识是否支持发送 ICMP 包。
var TcpPingPorts = []uint16{80, 22, 445, 23, 443, 81, 161, 3389, 8080, 8081}//是用于 TCP Ping 的默认常见端口列表。

函数列表分析:

  • func IcmpOK(host string) bool 

函数尝试直接发送 ICMP 包来检查主机是否存活。

// IcmpOK 直接发ICMP包
func IcmpOK(host string) bool {
	pinger, err := ping.NewPinger(host)
	if err != nil {
		return false
	}
	pinger.SetPrivileged(true)
	pinger.Count = 1
	pinger.Timeout = 800 * time.Millisecond
	if pinger.Run() != nil { // Blocks until finished. return err
		return false
	}
	if stats := pinger.Statistics(); stats.PacketsRecv > 0 {
		return true
	}
	return false
}
  • func PingOk(host string) bool

函数尝试通过执行不同操作系统的 Ping 命令来检查主机是否存活。

// PingOk Ping命令模式
func PingOk(host string) bool {
	switch runtime.GOOS {
	case "linux":
		cmd := exec.Command("ping", "-c", "1", "-W", "1", host)
		var out bytes.Buffer
		cmd.Stdout = &out
		cmd.Run()
		if strings.Contains(out.String(), "ttl=") {
			return true
		}
	case "windows":
		cmd := exec.Command("ping", "-n", "1", "-w", "500", host)
		var out bytes.Buffer
		cmd.Stdout = &out
		cmd.Run()
		if strings.Contains(out.String(), "TTL=") {
			return true
		}
	case "darwin":
		cmd := exec.Command("ping", "-c", "1", "-t", "1", host)
		var out bytes.Buffer
		cmd.Stdout = &out
		cmd.Run()
		if strings.Contains(out.String(), "ttl=") {
			return true
		}
	}
	return false
}
  • func TcpPing(host string, ports []uint16, timeout time.Duration) (ok bool)

函数使用 TCP 连接在指定端口上对主机进行存活探测,在连接成功或者连接被拒绝时,都会判断主机为存活状态。

func TcpPing(host string, ports []uint16, timeout time.Duration) (ok bool) {
	var wg sync.WaitGroup              // 创建一个 WaitGroup 用于等待所有端口的探测完成
	ctx, cancel := context.WithCancel(context.Background()) // 创建一个上下文和取消函数
	d := net.Dialer{                   // 创建一个 Dialer,用于建立 TCP 连接
		Timeout:   timeout + time.Second, // 设置连接超时时间
		KeepAlive: 0,                    // 禁用 KeepAlive
	}
	for _, port := range ports {      // 遍历端口列表
		time.Sleep(10 * time.Millisecond) // 间隔一段时间再探测下一个端口,避免过于频繁的连接
		wg.Add(1)                     // 每个端口探测前增加 WaitGroup 计数
		go func(_port uint16) {       // 并发进行端口探测
			conn, err := d.DialContext(ctx, "tcp", fmt.Sprintf("%s:%d", host, _port)) // 尝试建立 TCP 连接
			if conn != nil {          // 如果成功建立连接
				conn.Close()          // 关闭连接
				ok = true             // 设置存活状态为 true
			} else if err != nil && strings.Contains(err.Error(), "refused it") { // 如果连接被拒绝
				ok = true             // 设置存活状态为 true
			}
			if ok {                   // 如果已经确认存活
				cancel()              // 取消其他端口的探测
			}
			wg.Done()                 // 完成当前端口的探测
		}(_port)                      // 传入端口号进行端口探测
	}
	wg.Wait()                        // 等待所有端口的探测完成
	return                            // 返回存活状态
}
  • func init()

函数在包加载时运行,它尝试在本地(127.0.0.1)发送 ICMP 包来检查系统是否支持 ICMP。

func init() {
	if IcmpOK("127.0.0.1") {
		CanIcmp = true
	}
}
  • func IsLive(ip string, tcpPing bool, tcpTimeout time.Duration) (ok bool)

IsLive 函数是用来检查主机是否存活的主要函数。如果支持 ICMP,则使用 IcmpOK 函数;否则,使用 PingOk 函数。如果前面的检查未能确定主机存活且 tcpPing 参数为真,则尝试使用 TcpPing 函数进行 TCP 存活探测。

func IsLive(ip string, tcpPing bool, tcpTimeout time.Duration) (ok bool) {
	if CanIcmp {
		ok = IcmpOK(ip)
	} else {
		ok = PingOk(ip)
	}
	if !ok && tcpPing {
		ok = TcpPing(ip, TcpPingPorts, tcpTimeout)
	}
	return
}

ps:总体而言,这些函数提供了多种方法来检查主机是否存活,包括使用 ICMP 包、执行系统命令(如 Ping 命令)以及 TCP 连接到常见端口。这样做可以在不同系统和网络环境下更全面地检查主机的存活状态

三、port端口解析,以及tcp扫描

1、/core/port/port.go

这个文件代码主要包含了端口扫描相关的逻辑和数据结构。

  • var TopTcpPorts = []uint16{} 常见端口列表

  • 各结构体列表
type Scanner interface {//这是一个接口,定义了扫描器的操作,包括关闭、等待、扫描指定 IP 和端口,以及等待速率限制器。
	Close()
	Wait()
	Scan(ip net.IP, dst uint16) error
	WaitLimiter() error
}

// OpenIpPort retChan,这个结构体表示一个开放的 IP 和端口,包含 IP 地址、端口号、服务名和可能的 HTTP 信息。
type OpenIpPort struct {
	Ip       net.IP
	Port     uint16
	Service  string
	HttpInfo *HttpInfo
}


// Option ...这个结构体包含了扫描的参数配置,如速率限制、超时时间、网卡信息和是否进行服务探测等。
type Option struct {
	Rate        int    // 每秒速度限制, 单位: s, 会在1s内平均发送, 相当于每个包之间的延迟
	Timeout     int    // TCP连接响应延迟, 单位: ms
	NextHop     string // pcap dev name
	FingerPrint bool   // 服务探测
	Httpx       bool   // HttpInfo 探测
}

// HttpInfo Http服务基础信息,这个结构体存储了 HTTP 服务的基础信息,包括状态码、响应包大小、URL、重定向路径、标题、服务名、TLS 信息和 Web 指纹
type HttpInfo struct {
	StatusCode int      // 状态码
	ContentLen int      // 相应包大小
	Url        string   // Url
	Location   string   // 302、301重定向路径
	Title      string   // 标题
	Server     string   // 服务名
	TlsCN      string   // tls使用者名称
	TlsDNS     []string // tlsDNS列表
	Fingers    []string // 识别到的web指纹
}
  • func (op OpenIpPort) String() string

这个函数是为了实现 Stringer 接口,这个接口包含一个 String() 方法,用于定义该类型的字符串表示形式。对于 OpenIpPort 类型的实例,这个函数会返回其 IP 地址、端口号以及可能的服务信息的字符串表示形式

func (op OpenIpPort) String() string {
	buf := strings.Builder{}
	buf.WriteString(op.Ip.String())
	buf.WriteString(":")
	buf.WriteString(strconv.Itoa(int(op.Port)))
	if op.Service != "" {
		buf.WriteString(" ")
		buf.WriteString(op.Service)
	}
	if op.HttpInfo != nil {
		buf.WriteString("\n")
		buf.WriteString(op.HttpInfo.String())
	}
	return buf.String()
}

  • func (hi *HttpInfo) String() string

这个函数也是实现 Stringer 接口的方法。对于 HttpInfo 类型的实例,这个函数返回其包含的 HTTP 信息的字符串表示形式,包括状态码、响应包大小、URL、重定向路径、标题、服务名和 Web 指纹等。

func (hi *HttpInfo) String() string {
	if hi == nil {
		return ""
	}
	var buf strings.Builder
	buf.WriteString(fmt.Sprintf("[HttpInfo]%s StatusCode:%d ContentLen:%d Title:%s ", hi.Url, hi.StatusCode, hi.ContentLen, hi.Title))
	if hi.Location != "" {
		buf.WriteString("Location:" + hi.Location + " ")
	}
	if hi.TlsCN != "" {
		buf.WriteString("TlsCN:" + hi.TlsCN + " ")
	}
	if len(hi.TlsDNS) > 0 {
		buf.WriteString("TlsDNS:" + strings.Join(hi.TlsDNS, ",") + " ")
	}
	if hi.Server != "" {
		buf.WriteString("Server:" + hi.Server + " ")
	}
	if len(hi.Fingers) != 0 {
		buf.WriteString(fmt.Sprintf("Fingers:%s ", hi.Fingers))
	}
	return buf.String()
}

  • func ParsePortRangeStr(portStr string) (out [][]uint16, err error)

这个函数用于解析端口字符串,将其转换为端口范围的列表。

// ParsePortRangeStr 解析端口字符串
func ParsePortRangeStr(portStr string) (out [][]uint16, err error) {
	portsStrGroup := strings.Split(portStr, ",")//逗号分隔的端口
	var portsStrGroup3 []string
	var portStart, portEnd uint64
	for _, portsStrGroup2 := range portsStrGroup {
		if portsStrGroup2 == "top1000" {
			continue
		}
		portsStrGroup3 = strings.Split(portsStrGroup2, "-")//-作为端口范围的
		portStart, err = strconv.ParseUint(portsStrGroup3[0], 10, 16)//返回端口的整数值,10进制,类型为int16
		if err != nil {
			return
		}
		portEnd = portStart
		if len(portsStrGroup3) == 2 {
			portEnd, err = strconv.ParseUint(portsStrGroup3[1], 10, 16)
		}
		if err != nil {
			return
		}
		out = append(out, []uint16{uint16(portStart), uint16(portEnd)})
	}
	return
}

  • func IsInPortRange(port uint16, portRanges [][]uint16) bool

这个函数用于检查指定端口是否在端口范围内

// IsInPortRange 判断port是否在端口范围里
func IsInPortRange(port uint16, portRanges [][]uint16) bool {
	for _, portRange := range portRanges {
		if port >= portRange[0] && port <= portRange[1] {
			return true
		}
	}
	return false
}
  • func ShuffleParseAndMergeTopPorts(portStr string) (ports []uint16, err error)

这个函数主要实现了对端口的解析、合并和随机化处理。它会解析传入的端口字符串,根据配置信息选取一些端口,优先使用常见的 TCP 端口,然后从用户指定的端口范围中选择未被选取的端口,并最终随机排序这些端口

// ShuffleParseAndMergeTopPorts shuffle parse portStr and merge TopTcpPorts
func ShuffleParseAndMergeTopPorts(portStr string) (ports []uint16, err error) {
	if portStr == "" {
		ports = TopTcpPorts //未指定则用默认top端口
		return
	}
	var portRanges [][]uint16
	portRanges, err = ParsePortRangeStr(portStr)
	if err != nil {
		return
	}
	// 优先发送top端口
	selectTopPort := make(map[uint16]struct{}) // TopPort
	hasTopStr := strings.Contains(portStr, "top1000")
	for _, _port := range TopTcpPorts {
		if hasTopStr || IsInPortRange(_port, portRanges) { //检测端口是否在范围内
			selectTopPort[_port] = struct{}{}
			ports = append(ports, _port)
		}
	}
	selectPort := make(map[uint16]struct{}) // OtherPort
	for _, portRange := range portRanges {
		var ok bool
		for _port := portRange[0]; _port <= portRange[1]; _port++ {
			if _port == 0 {
				continue
			}
			if _, ok = selectTopPort[_port]; ok {
				continue
			} else if _, ok = selectPort[_port]; ok {
				continue
			}
			selectPort[_port] = struct{}{}
			ports = append(ports, _port) //得到所有端口,并将top端口排在前面
			if _port == 65535 {
				break
			}
		}
	}
	if len(ports) == 0 {
		err = errors.New("ports len is 0")
		return
	}
	// 端口随机化
	skip := uint64(len(selectTopPort)) // 跳过Top
	_ports := make([]uint16, len(ports))
	copy(_ports, ports)
	sf := util.NewShuffle(uint64(len(ports)) - skip)
	if sf != nil {
		for i := skip; i < uint64(len(_ports)); i++ {
			ports[i] = _ports[skip+sf.Get(i-skip)]
		}
	}
	return
}

ps:这些函数主要提供了对端口进行解析、筛选和随机化的功能,以便用于端口扫描和服务探测。它将常见的 TCP 端口列表与用户输入的端口范围合并,随机化排序以减少扫描的可预测性。

2、/core/port/tcp/tcp.go

整体来说,这个代码文件定义了一个 TCP 端口扫描器,可以根据指定的 IP 地址和端口号对目标进行扫描,并可选地进行服务探测和 HTTP 信息探测。同时,它实现了速率限制以及 goroutine 的管理,确保扫描操作的安全性和效率。

var DefaultTcpOption = port.Option{//这里定义了默认的 TCP 扫描选项,包括扫描速率和超时时间等。
	Rate:    1000,
	Timeout: 800,
}

type TcpScanner struct {//管理 TCP 端口扫描器的状态和操作。结构体中包含了需要的字段和方法。
	ports   []uint16             // 指定端口
	retChan chan port.OpenIpPort // 返回值队列
	limiter *limiter.Limiter
	ctx     context.Context
	timeout time.Duration
	isDone  bool
	option  port.Option
	wg      sync.WaitGroup
}
  • func NewTcpScanner(retChan chan port.OpenIpPort, option port.Option) (ts *TcpScanner, err error)

这个函数是一个构造器,用于创建一个新的 TCP 扫描器实例。它接收一个返回值通道 retChan 和扫描选项 option,并返回一个 TcpScanner 的指针。函数首先对传入的选项进行验证,确保速率大于等于 10,超时时间大于 0。然后,它初始化了一个 TcpScanner 结构体实例,设置了返回通道、速率限制器、上下文、超时时间和其他选项,并将该实例赋值给 ts,最后返回该实例和可能的错误。

// NewTcpScanner Tcp扫描器
func NewTcpScanner(retChan chan port.OpenIpPort, option port.Option) (ts *TcpScanner, err error) {
	// option verify
	if option.Rate < 10 {
		err = errors.New("rate can not be set less than 10") // 如果速率小于 10,则返回错误
		return
	}
	if option.Timeout <= 0 {
		err = errors.New("timeout can not be set to 0") // 如果超时时间小于等于 0,则返回错误
		return
	}

	// 初始化 TcpScanner 结构体
	ts = &TcpScanner{
		retChan: retChan, // 设置返回通道
		limiter: limiter.NewLimiter(limiter.Every(time.Second/time.Duration(option.Rate)), option.Rate/10), // 设置速率限制器
		ctx:     context.Background(), // 初始化上下文
		timeout: time.Duration(option.Timeout) * time.Millisecond, // 设置超时时间
		option:  option, // 设置选项
	}

	return // 返回 TcpScanner 实例和可能的错误
}

  • func (ts *TcpScanner) Scan(ip net.IP, dst uint16) error

这个函数用于执行对指定 IP 和目标端口进行扫描的操作。它会启动一个 goroutine,在其中进行端口扫描并将结果发送到 retChan 通道中。函数首先检查扫描器是否已关闭,然后将一个任务添加到等待组 wg 中。接着,它初始化了一个 port.OpenIpPort 结构体实例,表示正在扫描的 IP 和端口。接下来的部分涉及服务指纹识别和 HTTP 信息探测的逻辑。如果设置了相应的选项,它将调用相关的函数进行识别并填充 openIpPort 结构体中的信息。最后,如果没有进行服务指纹识别或者 HTTP 信息探测,它将尝试通过 net.DialTimeout 进行连接,判断端口是否开放,并将结果发送到通道中。

// Scan 对指定IP和dis port进行扫描
func (ts *TcpScanner) Scan(ip net.IP, dst uint16) error {
	if ts.isDone {
		return errors.New("scanner is closed") // 如果扫描器已关闭,则返回错误
	}
	ts.wg.Add(1) // 增加等待组中的任务数
	go func() {
		defer ts.wg.Done() // 标记任务结束
		//fmt.Println(1)
		openIpPort := port.OpenIpPort{
			Ip:   ip,
			Port: dst,
		}
		var isDailErr bool
		if ts.option.FingerPrint {
			openIpPort.Service, isDailErr = fingerprint.PortIdentify("tcp", ip, dst, 2*time.Second) // 进行服务指纹识别
			if isDailErr {
				return // 如果识别过程出错,直接返回
			}
		}
		if ts.option.Httpx && (openIpPort.Service == "" || openIpPort.Service == "http" || openIpPort.Service == "https") {
			openIpPort.HttpInfo, isDailErr = fingerprint.ProbeHttpInfo(ip, dst, 2*time.Second) // 进行 HTTP 信息探测
			if isDailErr {
				return // 如果探测过程出错,直接返回
			}
			if openIpPort.HttpInfo != nil {
				if strings.HasPrefix(openIpPort.HttpInfo.Url, "https") {
					openIpPort.Service = "https" // 如果是 HTTPS,则标记为 HTTPS 服务
				} else {
					openIpPort.Service = "http" // 如果是 HTTP,则标记为 HTTP 服务
				}
			}
		}
		if !ts.option.FingerPrint && !ts.option.Httpx {
			conn, _ := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, dst), ts.timeout) // 尝试连接端口
			if conn != nil {
				conn.Close() // 如果连接成功,则关闭连接
			} else {
				return // 如果连接失败,直接返回
			}
		}
		ts.retChan <- openIpPort // 将扫描结果发送到通道
	}()
	return nil
}

  • 辅助函数

func (ts *TcpScanner) Wait()

func (ts *TcpScanner) Close()

func (ts *TcpScanner) WaitLimiter() error

//这个方法用于等待所有启动的 goroutine 完成扫描操作。它会等待 wg 等待组中的所有 goroutine 完成。
func (ts *TcpScanner) Wait() {
	ts.wg.Wait()
}

// Close chan这个方法用于关闭 retChan 通道,表示扫描已经完成。它还会设置 isDone 标志,表示扫描器已关闭
func (ts *TcpScanner) Close() {
	ts.isDone = true
	close(ts.retChan)
}

// WaitLimiter Waiting for the speed limit这个方法用于等待速率限制器。它会通过 limiter 控制扫描的速率,以确保按照设定的速率发送扫描请求
func (ts *TcpScanner) WaitLimiter() error {
	return ts.limiter.Wait(ts.ctx)
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1307485.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

云架构俭约之道七法则(The Frugal Architect)

文章目录 一、前言关于 Law 与 Rule 的区别 二、云架构俭约之道七法则Design&#xff08;设计方面&#xff09;Law 1&#xff1a;把成本作为非功能性需求Law 2&#xff1a;可持续性系统需要将成本与业务相匹配Law 3&#xff1a;架构设计是一系列权衡的取舍 Measure&#xff08;…

Unity | 渡鸦避难所-2 | 搭建场景并添加碰撞器

1 规范项目结构 上期中在导入一系列的商店资源包后&#xff0c;Assets 目录已经变的混乱不堪 开发过程中&#xff0c;随着资源不断更新&#xff0c;遵循一定的项目结构和设计规范是非常必要的。这可以增加项目的可读性、维护性、扩展性以及提高团队协作效率 这里先做下简单的…

vivado约束方法2

排序约束条件 因为XDC约束是按顺序应用的&#xff0c;并且是基于明确的优先级排列的规则&#xff0c;您必须仔细检查约束的顺序。 Vivado IDE为您的设计提供了全面的可视性。要逐步验证约束&#xff0c;请执行以下操作&#xff1a; 1.运行相应的报告命令。 2.查看Tcl控制台…

微信小程序自定义提示框组件并使用插槽 tooltip

创建tooltip组件引用 创建一个自定义组件&#xff0c;例如命名为 tooltip tooltip.wxml&#xff1a;用于定义组件的结构&#xff1b; <!--components/tooltip/tooltip.wxml--> <view class"tooltip-wrapper" hidden"{{hidden}}" style"lef…

spingboot项目实战之若依框架创建新模块

前言 目前的脚手架系统很多&#xff0c;比较早接触诺依框架&#xff0c;以若依框架为参考如何创建新模块 步骤 1. 下载诺依框架&#xff0c;依照参考说明一步步&#xff0c;能做到系统运行起来。 2. 准备好mysql文件&#xff0c;创建新数据库表 3. 数据库管理工具navicat…

如何退回chrome旧版ui界面?关闭Chrome浏览器新 UI 界面

之前启用新UI的方式 Chrome 已经很久没有进行过大的样式修改&#xff0c;但近期在稳定分支中添加了新的 flags 实验性标志&#xff0c;带来了全新的设计与外观&#xff0c;启用方式如下&#xff1a; 在 Chrome 浏览器的搜索栏中输入并访问 chrome://flags 搜索“refresh 2023…

Leetcode 46 全排列

题意理解&#xff1a; 首先明确全排列是什么&#xff1f; 使用集合里所有的元素&#xff0c;使用不同的顺序进行排列&#xff0c;所有的排列集合即为全排列。 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] 这里的元素不会…

《YOLOv8改进系列》专栏目录介绍

YOLOv8改进系列 &#x1f680; 专栏目录介绍 本专栏内容是紧跟最新、最前沿的改进方法&#xff0c;不仅适用检测任务&#xff0c;分类任务&#xff0c;分割任务&#xff0c;关键点任务同样适用。文章适用于多种场景&#xff0c;包括但不限于小目标、工业缺陷、轻量化等领域。 …

猫粮哪个牌子质量好性价比高?口碑比较好的主食冻干猫粮牌子推荐

猫咪生骨肉主食冻干猫粮喂养方式是越来越火了&#xff0c;作为一个离职的十年经验宠物护理师&#xff0c;对宠物健康营养方面的知识一直在研究&#xff0c;不光是为了我自己养的猫咪身体健康&#xff0c;也要为客户的猫咪健康负责&#xff01;现在很多养猫人士对主食冻干猫粮喂…

AE (3)_主观亮度和对比度调试

#灵感# 画面的亮度、对比度是最直接&#xff0c;观看者最先获得的感受。所以有必要花时间认真调整。 ------哈哈&#xff0c;虽然调试的时间其实不如磨清晰度多。 目录 举个图例&#xff1a; 三个参数AE、gamma、LTM&#xff1a; 调试顺序&#xff1a; 举个图例&#xff1…

beebox靶场A2 low级别身份验证通关教程

1. 断开的身份验证 - 验证码绕过 Broken Auth. - CAPTCHA Bypassing 打开burp抓包在页面上输入账号为bee 密码随便输&#xff0c;输入验证码开始抓包拦截&#xff0c; 发到重放器进行重放&#xff0c;发现只是一直提示密码错误&#xff0c;那证明只要不刷新页面验证码应该就不…

2023年第三届产业数字化【金铲奖】重磅来袭!

做具备产业数字化价值的企业、案例标杆、资本机构的见证者、发现者、陪伴者。 出品|产业家 一年一度的金铲奖来了&#xff01; 在过去的一年时间里&#xff0c;我们清晰地看到&#xff0c;产业数字化的潮水更加汹涌澎湃且势不可挡&#xff0c;越来越多的企业开始寻求数字化…

照片如何抠图换背景?分享三个一键抠图的方法

照片如何抠图换背景&#xff1f;通过使用一键抠图工具&#xff0c;您可以将图片中的主体从原始背景中分离出来&#xff0c;并将其放置在新的背景中。这种技术可以用于各种情况&#xff0c;例如在照片编辑中增加创意效果、改变照片的氛围或者为产品展示添加专业外观。通过抠图并…

LeetCode刷题--- 验证二叉搜索树

个人主页&#xff1a;元清加油_【C】,【C语言】,【数据结构与算法】-CSDN博客 http://t.csdnimg.cn/ZxuNL个人专栏&#xff1a;力扣递归算法题 http://t.csdnimg.cn/ZxuNL 【C】 http://t.csdnimg.cn/c9twt 前言&#xff1a;这个专栏主要讲述递归递归、搜索与回溯算法&#x…

redis:一、面试题常见分类+缓存穿透的定义、解决方案、布隆过滤器的原理和误判现象、面试回答模板

redis面试题常见分类 缓存穿透 定义 缓存穿透是一种现象&#xff0c;引发这种现象的原因大概率是遭到了恶意攻击。具体就是查询一个一定不存在的数据&#xff0c;mysql查询不到数据也不会直接写入缓存&#xff0c;就会导致这个数据的每次请求都需要查DB&#xff0c;数据库压力…

ChatGLM3:打造更智能、更安全的代码解释器和工具使用体验

ChatGLM3 是由智谱AI训练的第三代大型语言模型&#xff0c;它不仅能理解和生成人类语言&#xff0c;还能执行代码、调用工具&#xff0c;并以 markdown 格式进行响应。为了提高用户体验&#xff0c;同时避免用户输入的注入攻击&#xff0c;ChatGLM3 采用了全新的对话格式。下载…

免费!视频下方人物(照片)介绍字幕制作pr字幕条模板素材

Premiere Pro模板&#xff0c;具有15个视频下方三分之一处添加人物介绍(图像)字幕pr字幕条模板素材下载。 适用于Premiere Pro 2019及以上版本。可以更改颜色更改样式。在视频的开头、中间和结尾使用。包括视频教程。不包括音乐。来自PR模板网&#xff1a;https://prmuban.com/…

el-table/avue-curd 相同列内容合并

1.效果 2.html 3.js spanMethod({ row, column, rowIndex }) {if (column.property deviceName) {if (rowIndex > 0 && row.deviceName this.data[rowIndex - 1].deviceName) {return {rowspan: 0,colspan: 1,};}let rowspan 1;for (let i rowIndex 1; i < …

conda的安装及使用 以pycharm 为例

下载 https://docs.conda.io/en/latest/miniconda.html 下载 window版本 74M且下着吧。 安装 一路next或agree &#xff0c;不同意人家也不会按装 。重要的是安装目录 让andconda当老大 pycharm的使用 创建项目时如下图选择 成功后进入项目的Terminal则如下图表示成功

macOS Sonoma 14.2RC(23C63)发布

系统介绍 黑果魏叔12 月 6 日消息&#xff0c;苹果今日向 Mac 电脑用户推送了 macOS 14.2 RC更新&#xff08;内部版本号&#xff1a;2323C633&#xff09;&#xff0c;本次更新距离上次发布隔了 49 天。 预计正式版会在下周到来。届时用户可以打开“设置”->“通用”->…