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

一、概述

本次主要分析指纹识别部分,针对开放http的端口的服务信息进行识别。

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

二、core--port-fingerprint-webfinger目录

1、fingerprint-webfinger-faviconhash.go

此文件的功能,主要是从html页面中提取favicon的url,然后计算favicon的hash值

此文件中主要包含三个方法:

  • func FindFaviconUrl(body string) string

从 HTML 页面的字符串中提取 favicon 的 URL;使用正则表达式 shortcutText 匹配包含 favicon 的 标签,然后再使用 shortcutHref 提取其 href 属性中的 URL

  • func mmh3Hash32(raw []byte) string

计算给定字节切片的 MurmurHash3 32 位哈希值;使用 MurmurHash3 算法对输入的字节切片进行哈希运算,返回结果作为字符串表示。

  • func standBase64(braw []byte) []byte

对二进制数据进行标准的 Base64 编码,并按行分割。将输入的二进制数据使用标准的 Base64 编码成字符串,然后按照每行字符数为 76 的标准,将编码后的结果分割成多行。

package webfinger

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"github.com/twmb/murmur3"
	"regexp"
)

var (
	shortcutText = regexp.MustCompile(`(?im)`)//提取link标签的正则表达式
	shortcutHref = regexp.MustCompile(`(?im)href=['"]+(.*?)['"]+`)//提取link标签里的href值的正则表达式
)

func FindFaviconUrl(body string) string {
	a := shortcutText.FindStringSubmatch(body)//首先根据正则提取传入的html页面的link标签,赋值给a
	if len(a) > 0 {//如果存在link标签
		faviconLink := a[0]
		b := shortcutHref.FindStringSubmatch(faviconLink)//提取第一个link标签的href的值
		if len(b) > 1 {
			return b[1] //如果提取url成功,则返回url
		}
	}
	return ""//不存在link标签,返回空
}

func mmh3Hash32(raw []byte) string {
	var h32 = murmur3.New32()
	_, err := h32.Write(raw)
	if err == nil {
		return fmt.Sprintf("%d", int32(h32.Sum32()))//使用 MurmurHash3 算法对输入的字节切片进行哈希运算,返回结果作为字符串表示。
	} else {
		return ""
	}
}

func standBase64(braw []byte) []byte {
	bckd := base64.StdEncoding.EncodeToString(braw)
	var buffer bytes.Buffer
	for i := 0; i < len(bckd); i++ {
		ch := bckd[i]
		buffer.WriteByte(ch)
		if (i+1)%76 == 0 {
			buffer.WriteByte('\n')//将输入的二进制数据使用标准的 Base64 编码成字符串,然后按照每行字符数为 76 的标准,将编码后的结果分割成多行。
		}
	}
	buffer.WriteByte('\n')
	return buffer.Bytes()
}

2、fingerprint-webfinger-finger.json

这个文件主要为faviconhash的字典,用来通过faviconhash的值比对来获取指纹信息。

分析某款go端口扫描器之二_第1张图片

3、fingerprint-webfinger-finger.go

此文件代码是一个 Web 系统指纹识别器。它的主要目的是从 HTTP 响应或 Favicon.ico 数据中识别 Web 服务的指纹。下面是一步步的解释:

  • type Date struct
  • type WebFinger struct 

Date 结构体表示特定指纹识别方法的数据结构;

WebFinger 结构体用于保存不同的指纹集合

type Date struct {
	Name     string //指纹名称
	Location string  //在http响应中的位置
	Method   string  //指纹识别方法(如keyword、regular、faviconhash)
	Keyword  []string //关键字或者正则表达式数组
}

type WebFinger struct {
	Name    string    //指纹集合的名称
	Fingers []Date    //包含data结构体的切片
}

var WebFingers []WebFinger

//go:embed finger.json
var DefFingerData []byte
  • func ParseWebFingerData(data []byte) error

解析 Web 指纹数据。

func ParseWebFingerData(data []byte) error {
    err := json.Unmarshal(data, &WebFingers)  //将json数据保存到WebFingers实例中
    if err != nil {
        return err
    }
    return nil
}
  • func LoadWebFingerData(file string) error

从文件中加载 Web 指纹数据。

//LoadWebFingerData 加载web指纹数据
func LoadWebFingerData(file string) error {

    data, err := os.ReadFile(file)
    if err != nil {
        return err
    }
    err = ParseWebFingerData(data)
    if err != nil {
        return err
    }
    return nil
}
  • func web FingerIdent(resp *http.Response) (names []string)

此函数解析 HTTP 响应的内容和头部信息,然后遍历 WebFingers 中的指纹集合,检查是否匹配给定的指纹识别方法和关键字或正则表达式;如果匹配,则返回匹配的指纹名称。 

  •  解析 HTTP 响应的内容和头部信息。
  • 遍历 WebFingers 中的指纹集合,检查是否匹配给定的指纹识别方法和关键字或正则表达式。
  • 如果匹配,则返回匹配的指纹名称。
//WebFingerIdent web系统指纹识别 用指纹库的指纹循环比对response中header部分和body部分是否包含特征
func WebFingerIdent(resp *http.Response) (names []string){
    var dataMap = make(map[string]string)
    body, _ := io.ReadAll(resp.Body)
    dataMap["body"] = string(body)
    var b bytes.Buffer
    resp.Header.Write(&b)
    dataMap["header"] = b.String()
    for _,finger :=  range WebFingers {
        for _, finger2 := range finger.Fingers {
            var flag bool
            if _, ok := dataMap[finger2.Location]; !ok {//取指纹库中Location字段为body和header部分的指纹
                continue
            }
            switch finger2.Method {
                case "keyword" :
                    if iskeyword(dataMap[finger2.Location], finger2.Keyword){
                        flag = true
                    }
                case "regular" :
                    if isregular(dataMap[finger2.Location], finger2.Keyword){
                        flag = true
                    }
            }
            if flag {
                if finger2.Name != ""{
                    finger.Name += "," + finger2.Name
                }
                names = append(names, finger.Name)
                break
            }
        }
    }
    return
    
}
  • func WebFingerIdentByFavicon(body []byte) (names []string)

 此函数通过 Favicon.ico 识别 Web 系统的指纹。首先对对 Favicon.ico 数据进行哈希运算,然后遍历WebFingers 中的指纹集合,检查是否与哈希值匹配指定的指纹识别方法和关键字。如果匹配,则返回匹配的指纹名称。

  • 对 Favicon.ico 数据进行哈希运算。
  • 遍历 WebFingers 中的指纹集合,检查是否与哈希值匹配指定的指纹识别方法和关键字。
  • 如果匹配,则返回匹配的指纹名称。
// WebFingerIdentByFavicon web系统指纹识别,通过Favicon.ico用指纹库的指纹循环比对favico hash之判断是否包含特征
func WebFingerIdentByFavicon(body []byte) (names []string) {
	var data string
	data = mmh3Hash32(standBase64(body))
	for _, finger := range WebFingers {
		for _, finger2 := range finger.Fingers {
			switch finger2.Method {
			case "faviconhash":
				if data != "" && len(finger2.Keyword) > 0 && data == finger2.Keyword[0] {
					if finger2.Name != "" {
						finger.Name += "," + finger2.Name
					}
					names = append(names, finger.Name)
					break
				}
			}
		}
	}
	return
}

4、fingerprint-webfinger-matchfinger.go

此文件包含两个方法,iskeyword和isregular,都是用于检查字符串中是否存在特定的关键字或匹配正则表达式

  • func iskeyword(str string, keyword []string) bool

检查字符串中是否存在指定的关键字。遍历关键字数组,如果字符串中不包含其中任何一个关键字,则立即返回 false,否则返回 true

  • func isregular(str string, keyword []string) bool

检查字符串是否匹配指定的正则表达式。遍历正则表达式数组,使用 regexp.MustCompile 创建正则表达式对象,然后检查字符串是否与每个正则表达式匹配。如果字符串不匹配任何一个正则表达式,则立即返回 false,否则返回 true

func iskeyword(str string, keyword []string) bool {
	if len(keyword) == 0 || str == "" {//先判断传入关键字切片是否为空和字符串是否为空
		return false
	}
    //遍历关键字数组,如果字符串中不包含其中任何一个关键字,则立即返回 
	for _, k := range keyword {//循环遍历keyword切片,检查keyword是否都包含在str字符串中
		if !strings.Contains(str, k) {
			return false
		}
	}
	return true
}

//遍历正则表达式数组,使用 regexp.MustCompile 创建正则表达式对象,然后检查字符串是否与每个正则表达式匹配。如果字符串不匹配任何一个正则表达式,则立即返回 false,否则返回 true。
func isregular(str string, keyword []string) bool {
	if len(keyword) == 0 || str == "" {
		return false
	}
	for _, k := range keyword {
		re := regexp.MustCompile(k)
		if !re.Match([]byte(str)) {
			return false
		}
	}
	return true
}

5、辅助函数

  • mmh3Hash32(raw []byte) string:对数据进行哈希并返回哈希值的字符串表示形式。
  • standBase64(braw []byte) []byte:对数据进行 Base64 编码,并在每行末尾添加换行符。

三、core-port-fingerprint目录

1、core-port-fingerprint-encodings.go

此文件主要是一些用于字符编码转换的函数,以及一个用于从http响应中提取标题并根据响应的内容类型进行相应的解码的函数。

  • func Decodegbk(s []byte) ([]byte, error)

这个函数用于将 GBK 编码的字节序列转换为 UTF-8 编码的字节序列。它使用 simplifiedchinese.GBK.NewDecoder() 进行解码,将输入的 GBK 编码字节流转换为 UTF-8 编码

//将GBK编码转化UTF-编码
func Decodegbk(s []byte) ([]byte, error){
    I := bytes.NewReader(s)
    O := transform.NewReader(I, simplifiedchinese.GBK.NewDecoder())
    d, e := ioutil.ReadAll(O)
    if e != nil {
        return nil, e
    }
    return d, nil

}
  • func Decodebig5(s []byte) ([]byte, error)

类似于 Decodegbk,这个函数将 BIG5 编码的字节序列转换为 UTF-8 编码的字节序列,使用 traditionalchinese.Big5.NewDecoder() 进行解码。

func Decodebig5(s []byte) ([]byte, error){
    I := bytes.NewReader(s)
    O := transform.NewReader(I, traditionalchinese.Big5.NewDecoder())
    d, e := ioutil.ReadAll(O)
    if e != nil {
        return nil, e
    }
    return d, nil
}

  • func Encodebig5(s []byte) ([]byte, error)

这个函数是将 UTF-8 编码的字节序列转换为 BIG5 编码的字节序列,使用 traditionalchinese.Big5.NewEncoder() 进行编码。

func Encodebig5(s []byte) ([]byte, error){
    I := bytes.NewReader(s)
    O := transform.NewReader(I, traditionalchinese.Big5.NewEncoder())
    d, e := ioutil.ReadAll(O)
    if e != nil {
        return nil, e 
    }
    return d,nil
}
  • func DecodeKorean(s []byte) ([]byte, error)

这个函数是用于将韩文编码的字节序列转换为 UTF-8 编码的字节序列,使用 korean.EUCKR.NewDecoder() 进行解码。

func DecodeKorean(s []byte) ([]byte, error) {
    koreanDecoder := korean.EUCKR.NewDecoder()
    return koreanDecoder.Bytes(s)
}
  • func DecodeData(data []byte, headers http.Header) ([]byte, error)

这个函数从 HTTP 响应中提取数据,并根据响应的内容类型进行相应的解码。它会检查响应头中的 Content-Type,尝试根据特定的字符集(如 GBK、EUC-KR 等)对数据进行解码。如果检测到响应头中指定了字符集,会调用相应的解码函数进行转换,如果没有匹配到特定的字符集,就会尝试从 HTML 头部的 meta 标签中提取字符集信息,并根据提取的信息进行解码。最终返回解码后的数据或原始数据(如果未指定字符集或解码失败)。

func DecodeData(data []byte, headers http.Header) ([]byte, error) {

    //Non UTF-8
    if contentTypes, ok := headers["Content-Type"]; ok {
        contentType := strings.ToLower(strings.Join(contentTypes, ";"))//用;连接contenttypes内容,并转化为小写
        
        //根据不同的contentyps值调用不同的解密方法
        switch {
            case stringsutil.ContainsAny(contentType, "charset=gb2312","charset=gbk"):
                return Decodegbk([]byte(data))

            case stringsutil.ContainsAyn(contentType,"euc-kr"):
                return DecodeDorean(data)    
        }
        
        //Content-Type 来自 head tag
        var match = reContentType.FindSubmatch(data)//reContentType来自 core/port/fingerprint/title.go ,作用为regexp.MustCompile(`(?im)\s*charset="(.*?)"|charset=(.*?)"\s*`),根据正则提取对应字段
        var mcontentType = ""
        if len(match) != 0 {
            for i, v := range match{
                if string(v) != "" && i != 0 {
                    mcontentType = string(v)
                }
            }
            mcontentType = strings.ToLower(mcontentType)
        }
        switch {
            case stringsutil.ContainsAny(mcontentType, "gb2312", "gbk"):
                return Decodegbk(data)
        }
    }
    return data, nil
        
}    

ps:这些函数的作用在于根据不同的字符编码格式,将原始的字符序列转换为 UTF-8 编码的字符序列,使其能够被正确解析和处理。DecodeData 函数则是根据 HTTP 响应的内容类型来判断使用何种字符编码转换函数,以确保正确地解码数据

2、core-port-fingerprint-fingerprint.go

这个文件主要用于端口服务的指纹识别,主要功能如下:

首先创建常量和类型:

type Action uint8

const (
	ActionRecv = Action(iota)
	ActionSend
)

const (
	refusedStr   = "refused"
	ioTimeoutStr = "i/o timeout"
)

type ruleData struct {
	Action  Action // send or recv
	Data    []byte // send or match data
	Regexps []*regexp.Regexp
}

type serviceRule struct {
	Tls       bool
	DataGroup []ruleData
}

var serviceRules = make(map[string]serviceRule)
var readBufPool = &sync.Pool{
	New: func() interface{} {
		return make([]byte, 4096)
	},
}

函数如下:

  • func PortIdentify(network string, ip net.IP, _port uint16, dailTimeout time.Duration) (serviceName string, isDailErr bool)

此函数识别端口对应的服务。它按照指定的顺序尝试从一系列服务规则中匹配出对应的服务。如果连接失败或者无法匹配出服务,则返回"unknown"

// PortIdentify 端口识别
func PortIdentify(network string, ip net.IP, _port uint16, dailTimeout time.Duration) (serviceName string, isDailErr bool) {

	matchedRule := make(map[string]struct{})

	unknown := "unknown"
	var matchStatus int

	// 优先判断port可能的服务
	if serviceNames, ok := portServiceOrder[_port]; ok {//portServiceOrder为rule.go文件中常见端口服务对应列表
		for _, service := range serviceNames {
			matchedRule[service] = struct{}{}
			matchStatus = matchRule(network, ip, _port, serviceRules[service], dailTimeout)//获取服务端口开放状态
			if matchStatus == 1 {
				return service, false
			} else if matchStatus == -1 {
				return unknown, true
			}
		}
	}

	// onlyRecv
	{
		var conn net.Conn
		var n int
		buf := readBufPool.Get().([]byte)
		defer func() {
			readBufPool.Put(buf)
		}()
		address := fmt.Sprintf("%s:%d", ip, _port)
		conn, _ = net.DialTimeout(network, address, dailTimeout)
		if conn == nil {
			return unknown, true
		}
		n, _ = read(conn, buf)
		conn.Close()
		if n != 0 {
			for _, service := range onlyRecv {
				_, ok := matchedRule[service] //检测是不是已有服务
				if ok {
					continue
				}
				matchStatus = matchRuleWhithBuf(buf[:n], ip, _port, serviceRules[service])//判断端口服务开放状态
				if matchStatus == 1 {
					return service, false
				}
			}
		}
		for _, service := range onlyRecv {
			matchedRule[service] = struct{}{}
		}
	}

	// 优先判断Top服务
	for _, service := range serviceOrder {
		_, ok := matchedRule[service]
		if ok {
			continue
		}
		matchedRule[service] = struct{}{}
		matchStatus = matchRule(network, ip, _port, serviceRules[service], dailTimeout)
		if matchStatus == 1 {
			return service, false
		} else if matchStatus == -1 {
			return unknown, true
		}
	}

	// other
	for service, rule := range serviceRules {
		_, ok := matchedRule[service]
		if ok {
			continue
		}
		matchStatus = matchRule(network, ip, _port, rule, dailTimeout)
		if matchStatus == 1 {
			return service, false
		} else if matchStatus == -1 {
			return unknown, true
		}
	}

	return unknown, false
}

  • func matchRuleWhithBuf(buf, ip net.IP, _port uint16, serviceRule serviceRule) int

函数根据传入的数据缓冲区,匹配给定的服务规则。如果规则中的数据在缓冲区中找到了匹配,则返回成功状态。

// 指纹匹配函数
func matchRuleWhithBuf(buf, ip net.IP, _port uint16, serviceRule serviceRule) int {
	data := []byte("")
	// 逐个判断
	for _, rule := range serviceRule.DataGroup {
		if rule.Data != nil {//先替换请求中的数据data数据,比如IP和port
			data = bytes.Replace(rule.Data, []byte("{IP}"), []byte(ip.String()), -1)
			data = bytes.Replace(data, []byte("{PORT}"), []byte(strconv.Itoa(int(_port))), -1)
		}
		// 包含数据就正确,使用正则匹配
		if rule.Regexps != nil {
			for _, _regex := range rule.Regexps {
				if _regex.MatchString(convert2utf8(string(buf))) {
					return 1
				}
			}
		}
		if bytes.Compare(data, []byte("")) != 0 && bytes.Contains(buf, data) {
			return 1
		}
	}
	return 0
}

  • func matchRule(network string, ip net.IP, _port uint16, serviceRule serviceRule, dailTimeout time.Duration) int

此函数建立连接并根据连接的情况进行数据的发送和接收,然后匹配给定的服务规则。如果规则中的数据在接收的数据中找到了匹配,则返回成功状态。

// 指纹匹配函数
func matchRule(network string, ip net.IP, _port uint16, serviceRule serviceRule, dailTimeout time.Duration) int {
	var err error
	var isTls bool
	var conn net.Conn
	var connTls *tls.Conn

	address := fmt.Sprintf("%s:%d", ip, _port)

	// 建立连接
	if serviceRule.Tls {//先确定是不是https
		// tls
		connTls, err = tls.DialWithDialer(&net.Dialer{Timeout: dailTimeout}, network, address, &tls.Config{
			InsecureSkipVerify: true,
			MinVersion:         tls.VersionTLS10,
		})
		if err != nil {
			if strings.HasSuffix(err.Error(), ioTimeoutStr) || strings.Contains(err.Error(), refusedStr) {
				return -1
			}
			return 0
		}
		defer connTls.Close()
		isTls = true
	} else {//如果不是https,使用net.conn连接
		conn, err = net.DialTimeout(network, address, dailTimeout)
		if conn == nil {
			return -1
		}
		defer conn.Close()
	}

	buf := readBufPool.Get().([]byte)
	defer func() {
		readBufPool.Put(buf)
	}()

	data := []byte("")
	// 逐个判断
	for _, rule := range serviceRule.DataGroup {
		if rule.Data != nil {//替换所有data数据中的IP和port,针对http和https
			data = bytes.Replace(rule.Data, []byte("{IP}"), []byte(ip.String()), -1)
			data = bytes.Replace(data, []byte("{PORT}"), []byte(strconv.Itoa(int(_port))), -1)
		}

		if rule.Action == ActionSend {//先判断获取发送数据,进行conn连接
			if isTls {
				connTls.SetWriteDeadline(time.Now().Add(time.Second))
				_, err = connTls.Write(data)
			} else {
				conn.SetWriteDeadline(time.Now().Add(time.Second))
				_, err = conn.Write(data)
			}
			if err != nil {
				// 出错就退出
				return 0
			}
		} else {//针对recive进行数据判断
			var n int
			if isTls {
				n, err = read(connTls, buf)
			} else {
				n, err = read(conn, buf)
			}
			// 出错就退出
			if n == 0 {
				return 0
			}
			// 包含数据就正确  使用正则筛选返回包数据
			if rule.Regexps != nil {
				for _, _regex := range rule.Regexps {
					if _regex.MatchString(convert2utf8(string(buf[:n]))) {
						return 1
					}
				}
			}
            //判断数据是否为空,以及buf中是否包含数据
			if bytes.Compare(data, []byte("")) != 0 && bytes.Contains(buf[:n], data) {
				return 1
			}
		}
	}

	return 0
}

  • func read(conn interface{}, buf []byte) (int, error)

函数负责从连接中读取数据,并在指定时间内设置读取的截止时间。它会尝试从传入的连接中读取数据到缓冲区中,并返回读取的字节数和可能出现的错误。如果读取的数据长度为 0 或者发生错误,则会返回相应的状态信息。

func read(conn interface{}, buf []byte) (int, error) {
	switch conn.(type) {
	case net.Conn:
		conn.(net.Conn).SetReadDeadline(time.Now().Add(time.Second))
		return conn.(net.Conn).Read(buf[:])
	case *tls.Conn:
		conn.(*tls.Conn).SetReadDeadline(time.Now().Add(time.Second))
		return conn.(*tls.Conn).Read(buf[:])
	}
	return 0, errors.New("unknown type")
}

  • func convert2utf8(src string) string

函数用于修复正则表达式在匹配非UTF-8字符时可能出现的问题。它会将UTF-8以外的字符转换成对应的UTF-8字符,确保正则表达式可以正确匹配。

func convert2utf8(src string) string {
	var dst string
	for i, r := range src {
		var v string
		if r == utf8.RuneError {
			// convert, rune => string, intstring() => encoderune()
			v = string(src[i])
		} else {
			v = string(r)
		}
		dst += v
	}
	return dst
}

 ps:代码中利用 sync.Pool 作为缓冲池,用来存储读取数据的缓冲区,提高效率。总体来说,这些函数实现了通过发送和接收数据并根据特定规则匹配服务的功能,用于识别特定端口上可能运行的服务

3、core-port-fingerprint-rules.go

这个文件主要为一个指纹识别器,它尝试根据不同服务的特征来判断特定端口上的服务。这里的 serviceOrder 是服务的顺序列表,onlyRecv 是仅接收数据的服务列表,portServiceOrder 是特定端口和服务的映射关系。

var serviceOrder = []string{"http", "https", "ssh", "redis", "mysql"}

var onlyRecv []string

var portServiceOrder = map[uint16][]string{
	21:    {"ftp"},
	22:    {"ssh"},
	80:    {"http", "https"},
	443:   {"https", "http"},
	445:   {"smb"},
	1035:  {"oracle"},
	1080:  {"socks5", "socks4"},
	1081:  {"socks5", "socks4"},
	1082:  {"socks5", "socks4"},
	1083:  {"socks5", "socks4"},
	1433:  {"sqlserver"},
	1521:  {"oracle"},
	1522:  {"oracle"},
	1525:  {"oracle"},
	1526:  {"oracle"},
	1574:  {"oracle"},
	1748:  {"oracle"},
	1754:  {"oracle"},
	3306:  {"mysql"},
	3389:  {"ms-wbt-server"},
	6379:  {"redis"},
	9001:  {"mongodb"},
	11211: {"memcached"},
	14238: {"oracle"},
	27017: {"mongodb"},
	20000: {"oracle"},
	49153: {"mongodb"},
}
  • func init()

init() 函数中,为了不同的服务(如HTTP、HTTPS、SSH等),定义了对应的识别规则。每个服务都有其特定的数据发送和接收规则,用于匹配返回的数据以确定服务是否存在。例如,对于 HTTP 服务,它尝试发送一个 HTTP 头部,并尝试匹配返回数据中是否包含 HTTP/;对于 SSH 服务,它会尝试匹配返回数据中是否包含 SSH 相关的标识等。这些规则都是通过发送特定数据和匹配接收到的数据来进行识别的

func init() {
    
	// http
	serviceRules["http"] = serviceRule{ //pingerprint.go中结构体,包含Tls和DataGroup
		Tls: false,
		DataGroup: []ruleData{
			{//设置http和https的规则数据,包含请求头和接收包
				ActionSend,
				[]byte("HEAD / HTTP/1.1\r\nHost: {IP}\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0\r\nAccept: */*\r\nAccept-Language: en\r\nAccept-Encoding: deflate\r\n\r\n"),
				nil,
			},
			{
				ActionRecv,
				[]byte("HTTP/"),
				nil,
			},
		},
	}
	// https
	serviceRules["https"] = serviceRule{
		Tls:       true,
		DataGroup: serviceRules["http"].DataGroup,
	}

	// ssh
	serviceRules["ssh"] = serviceRule{
		Tls: false,
		DataGroup: []ruleData{
			{//设置ssh的匹配规则
				ActionRecv,
				nil,
				[]*regexp.Regexp{
					regexp.MustCompile(`^SSH-([\d.]+)-`),
					regexp.MustCompile(`^SSH-(\d[\d.]+)-`),
					regexp.MustCompile(`^SSH-(\d[\d.]*)-`),
					regexp.MustCompile(`^SSH-2\.0-`),
					regexp.MustCompile(`^SSH-1\.`),
				},
			},
		},
	}

	// ftp
	serviceRules["ftp"] = serviceRule{
		Tls: false,
		DataGroup: []ruleData{
			{//设置ftp的匹配规则
				ActionRecv,
				nil,
				[]*regexp.Regexp{
					regexp.MustCompile(`^220 ([-/.+\w]+) FTP server`),
					regexp.MustCompile(`^220[ |-](.*?)FileZilla`),
					regexp.MustCompile(`^(?i)220[ |-](.*?)version`),
					regexp.MustCompile(`^220 3Com `),
					regexp.MustCompile(`^220-GuildFTPd`),
					regexp.MustCompile(`^220-.*\r\n220`),
					regexp.MustCompile(`^220 Internet Rex`),
					regexp.MustCompile(`^530 Connection refused,`),
					regexp.MustCompile(`^220 IIS ([\w._-]+) FTP`),
					regexp.MustCompile(`^220 PizzaSwitch `),
					regexp.MustCompile(`(?i)^220 ([-.+\w]+) FTP`),
					regexp.MustCompile(`(?i)^220[ |-](.*?)FTP`),
				},
			},
		},
	}

	// socks4
	serviceRules["socks4"] = serviceRule{
		Tls: false,
		DataGroup: []ruleData{
			{//设置socks4发送包的数据部分
				ActionSend,
				[]byte("\x04\x01\x00\x16\x7f\x00\x00\x01rooo\x00"),
				nil,
			},
			{//设置socks4接收包的匹配规则
				ActionRecv,
				nil,
				[]*regexp.Regexp{
					regexp.MustCompile(`^\x00\x5a`),
					regexp.MustCompile(`^\x00\x5b`),
					regexp.MustCompile(`^\x00\x5c`),
					regexp.MustCompile(`^\x00\x5d`),
				},
			},
		},
	}

	// socks5
	serviceRules["socks5"] = serviceRule{
		Tls: false,
		DataGroup: []ruleData{
			{//设置socks5的发送包数据
				ActionSend,
				[]byte("\x05\x04\x00\x01\x02\x80\x05\x01\x00\x03\x0dwww.baidu.com\x00\x50GET / HTTP/1.0\r\n\r\n"),
				nil,
			},
			{//设置socks5的接收包规则
				ActionRecv,
				nil,
				[]*regexp.Regexp{
					regexp.MustCompile(`^\x05\x00\x05\x01`),
					regexp.MustCompile(`^\x05\x00\x05\x00\x00\x01.{6}HTTP`),
					regexp.MustCompile(`^\x05\x02`),
					regexp.MustCompile(`^\x05\x00`),
				},
			},
		},
	}

	 tls
	//serviceRules["tls"] = serviceRule{
	//	Tls: false,
	//	DataGroup: []ruleData{
	//		{
	//			ActionSend,
	//			[]byte("\x16\x03\x00\x00S\x01\x00\x00O\x03\x00?G\xd7\xf7\xba,\xee\xea\xb2`~\xf3\x00\xfd\x82{\xb9\xd5\x96\xc8w\x9b\xe6\xc4\xdb<=\xdbo\xef\x10n\x00\x00(\x00\x16\x00\x13\x00\x0a\x00f\x00\x05\x00\x04\x00e\x00d\x00c\x00b\x00a\x00`\x00\x15\x00\x12\x00\x09\x00\x14\x00\x11\x00\x08\x00\x06\x00\x03\x01\x00"),
	//			nil,
	//		},
	//		{
	//			ActionRecv,
	//			nil,
	//			[]*regexp.Regexp{
	//				regexp.MustCompile(`^[\x16\x15]\x03\x00`),
	//				regexp.MustCompile(`^[\x16\x15]\x03...\x02`),
	//			},
	//		},
	//	},
	//}

	// smb
	serviceRules["smb"] = serviceRule{
		Tls: false,
		DataGroup: []ruleData{
			{//设置smb协议的发送包数据
				ActionSend,
				[]byte("\x00\x00\x00\xa4\xff\x53\x4d\x42\x72\x00\x00\x00\x00\x08\x01\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x06\x00\x00\x01\x00\x00\x81\x00\x02PC NETWORK PROGRAM 1.0\x00\x02MICROSOFT NETWORKS 1.03\x00\x02MICROSOFT NETWORKS 3.0\x00\x02LANMAN1.0\x00\x02LM1.2X002\x00\x02Samba\x00\x02NT LM 0.12\x00\x02NT LANMAN 1.0\x00"),
				nil,
			},
			{//设置smb协议的接收包规则
				ActionRecv,
				nil,
				[]*regexp.Regexp{
					regexp.MustCompile(`MBr\x00\x00\x00\x00\x88\x01@\x00`),
				},
			},
		},
	}

	// ms-wbt-server
	serviceRules["ms-wbt-server"] = serviceRule{
		Tls: false,
		DataGroup: []ruleData{
			{
				ActionSend,
				[]byte("\x03\x00\x00*%\xe0\x00\x00\x00\x00\x00Cookie: mstshash=pcpc\r\n\x01\x00\x08\x00\x03\x00\x00\x00"),
				nil,
			},
			{
				ActionRecv,
				nil,
				[]*regexp.Regexp{
					regexp.MustCompile(`\x03\x00\x00.\x0e\xd0\x00\x00\x124\x00`),
				},
			},
		},
	}

	// jdwp
	serviceRules["jdwp"] = serviceRule{
		Tls: false,
		DataGroup: []ruleData{
			{
				ActionRecv,
				[]byte("JDWP-Handshake"),
				nil,
			},
		},
	}

	// jdbc
	serviceRules["jdbc"] = serviceRule{
		Tls: false,
		DataGroup: []ruleData{
			{
				ActionRecv,
				[]byte("HSQLDB JDBC Network Listener"),
				nil,
			},
		},
	}

	// Db
	// mysql
	serviceRules["mysql"] = serviceRule{
		Tls: false,
		DataGroup: []ruleData{
			{
				ActionRecv,
				nil,
				[]*regexp.Regexp{
					regexp.MustCompile(`(?s)^.\x00\x00\x00\xff..Host .* is not allowed to connect to this .* server$`),
					regexp.MustCompile(`^.\x00\x00\x00\xff..Too many connections`),
					regexp.MustCompile(`(?s)^.\x00\x00\x00\xff..Host .* is blocked because of many connection errors`),
					regexp.MustCompile(`(?s)^.\x00\x00\x00\x0a(\d\.[-_~.+:\w]+MariaDB-[-_~.+:\w]+)`),
					regexp.MustCompile(`(?s)^.\x00\x00\x00\x0a(\d\.[-_~.+\w]+)\x00`),
					regexp.MustCompile(`(?s)^.\x00\x00\x00\xffj\x04'[\d.]+' .* MySQL`),
				},
			},
		},
	}
	// redis
	serviceRules["redis"] = serviceRule{
		Tls: false,
		DataGroup: []ruleData{
			{
				ActionSend,
				[]byte("GET / HTTP/1.1\r\n"),
				nil,
			},
			{
				ActionRecv,
				nil,
				[]*regexp.Regexp{
					regexp.MustCompile(`-ERR operation not permitted\r\n`),
					regexp.MustCompile(`-ERR wrong number of arguments for 'get' command\r\n`),
				},
			},
		},
	}

	// sqlserver
	serviceRules["sqlserver"] = serviceRule{
		Tls: false,
		DataGroup: []ruleData{
			{
				ActionSend,
				[]byte("\x12\x01\x00\x34\x00\x00\x00\x00\x00\x00\x15\x00\x06\x01\x00\x1b\x00\x01\x02\x00\x1c\x00\x0c\x03\x00\x28\x00\x04\xff\x08\x00\x01\x55\x00\x00\x00\x4d\x53\x53\x51\x4c\x53\x65\x72\x76\x65\x72\x00\x48\x0f\x00\x00"),
				nil,
			},
			{
				ActionRecv,
				[]byte("\x04\x01\x00\x25\x00\x00\x01\x00\x00\x00\x15\x00\x06\x01\x00\x1b\x00\x01\x02\x00\x1c\x00\x01\x03\x00\x1d\x00\x00\xff"),
				nil,
			},
		},
	}

	// oracle
	serviceRules["oracle"] = serviceRule{
		Tls: false,
		DataGroup: []ruleData{
			{
				ActionSend,
				[]byte("\x00Z\x00\x00\x01\x00\x00\x00\x016\x01,\x00\x00\x08\x00\x7F\xFF\x7F\x08\x00\x00\x00\x01\x00 \x00:\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x004\xE6\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00(CONNECT_DATA=(COMMAND=version))"),
				nil,
			},
			{
				ActionRecv,
				nil,
				[]*regexp.Regexp{
					regexp.MustCompile(`(?s)^\x00\x20\x00\x00\x02\x00\x00\x00\x016\x00\x00\x08\x00\x7f\xff\x01\x00\x00\x00\x00\x20`),
					regexp.MustCompile(`^\+\x00\x00\x00$`),
					regexp.MustCompile(`^\x00.\x00\x00\x02\x00\x00\x00.*\(IAGENT`),
					regexp.MustCompile(`^..\x00\x00\x04\x00\x00\x00"\x00..\(DESCRIPTION=`),
					regexp.MustCompile(`^\x00.\x00\x00[\x02\x04]\x00\x00\x00.*\(`),
					regexp.MustCompile(`^\x00.\x00\x00[\x02\x04]\x00\x00\x00.*TNSLSNR`),
					regexp.MustCompile(`^\x00,\x00\x00\x04\x00\x00"`),
				},
			},
		},
	}

	// mongodb
	serviceRules["mongodb"] = serviceRule{
		Tls: false,
		DataGroup: []ruleData{
			{
				ActionSend,
				[]byte("\x41\x00\x00\x00\x3a\x30\x00\x00\xff\xff\xff\xff\xd4\x07\x00\x00\x00\x00\x00\x00test.$cmd\x00\x00\x00\x00\x00\xff\xff\xff\xff\x1b\x00\x00\x00\x01serverStatus\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00"),
				nil,
			},
			{
				ActionRecv,
				nil,
				[]*regexp.Regexp{
					regexp.MustCompile(`(?s)^.*version([: "]+)([.\d]+)"`),
					regexp.MustCompile(`(?s)^\xcb\x00\x00\x00....:0\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\xa7\x00\x00\x00\x01uptime\x00\x00\x00\x00\x00\x00 ` + "`" + `@\x03globalLock\x009\x00\x00\x00\x01totalTime\x00\x00\x00\x00\x7c\xf0\x9a\x9eA\x01lockTime\x00\x00\x00\x00\x00\x00\xac\x9e@\x01ratio\x00!\xc6\$G\xeb\x08\xf0>\x00\x03mem\x00<\x00\x00\x00\x10resident\x00\x03\x00\x00\x00\x10virtual\x00\xa2\x00\x00\x00\x08supported\x00\x01\x12mapped\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01ok\x00\x00\x00\x00\x00\x00\x00\xf0\?\x00$`),
					regexp.MustCompile(`(?s)^.\x00\x00\x00....:0\x00\x00\x01\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\+\x00\x00\x00\x02errmsg\x00\x0e\x00\x00\x00need to login\x00\x01ok\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`),
					regexp.MustCompile(`(?s)^.\x00\x00\x00....:0\x00\x00\x01\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00.\x00\x00\x00\x01ok\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02errmsg\x00.\x00\x00\x00not authorized on`),
				},
			},
		},
	}

	// memcached
	serviceRules["memcached"] = serviceRule{
		Tls: false,
		DataGroup: []ruleData{
			{
				ActionSend,
				[]byte("stats\\n"),
				nil,
			},
			{
				ActionRecv,
				nil,
				[]*regexp.Regexp{
					regexp.MustCompile(`(?s)^STAT pid \d`),
					regexp.MustCompile(`(?s)^ERROR\r\n`),
				},
			},
		},
	}

	// onlyRecv 仅接收数据的服务列表
	for k, m := range serviceRules {
		if len(m.DataGroup) == 1 {
			onlyRecv = append(onlyRecv, k)
		}
	}
}

 ps:总体来说,这个代码段通过发送特定数据并检查返回数据中是否包含特定标识来识别不同服务的存在。如果匹配成功,则确定该服务在特定端口上运行。

4、core-port-fingerprint-title.go

此文件主要处理从 HTML 文档中提取标题以及获取页面重定向的目标 URL。

首先:定义变量,设置获取title、contenttype、refresh、replace的正则

var (
	cutset        = "\n\t\v\f\r"
	reTitle       = regexp.MustCompile(`(?im)<\s*title.*>(.*?)<\s*/\s*title>`)
	reContentType = regexp.MustCompile(`(?im)\s*charset="(.*?)"|charset=(.*?)"\s*`)
	reRefresh     = regexp.MustCompile(`(?im)\s*content=['"]\d;url=['"](.*?)['"]`)
	reReplace     = regexp.MustCompile(`(?im)window\.location\.replace\(['"](.*?)['"]\)`)
)
  • func ExtractTitle(body []byte) (title string)

这个函数用于从 HTML 页面中提取标题。它首先尝试使用 DOM 解析器解析文档,并提取其中的标题。如果解析出错,就使用正则表达式 reTitle 在 HTML 文档中查找标题标签,提取标题内容。最后对提取到的标题进行处理,去除空白字符和特殊符号

func ExtractTitle(body []byte) (title string) {
    //先从dom中获取
    titleDom, err := getTitleWithDom(body)//从dom节点中获取title
    //如果出错,则回退到用正则匹配
    if  err != nil {
        for _, match := range reTitle.FindAllString(string(body), -1) {
            title = match
            break
        }
    }else {
        title = renderNode(titleDom)//将html转换为字符串类型
    }
    title = html.UnescapeString(trimTitleTags(title))

    //删除多余字符
    title = strings.TrimSpace(strings.Trim(title, cutset)) //去掉首尾的特殊字符
    title = strings.ReplaceAll(title, "\n", "")
    title = strings.ReplaceAll(title, "\r", "")
    
    return title
}

  • func getTitleWithDom(body []byte) (*html.Node, error)

这个函数尝试使用 HTML 解析器来解析 HTML 文档,并找到 </code> 标签对应的节点。它通过递归方式遍历 HTML 文档的节点树,在遍历过程中寻找 <code>title</code> 节点</p> <pre><code class="language-Go">func getTitleWithDom(body []byte) (*html.Node, error){ var title *html.Node var crawler func(*html.Node) crawler = func(node *html.Node) {//用于遍历 HTML 文档的节点树,查找 <title> 节点。 if node.Type == html.ElementNode && node.Data == "title" { title = node return } for child := node.FirstChild; child != nil && title == nil; child = child.NextSibling { crawler(child) } } htmlDoc, err := html.Parse(bytes.NewReader(body))//使用 html.Parse 函数解析传入的 HTML 文档,将其转换为一个 HTML 文档对象(htmlDoc) if err != nil { return nil, err } crawler(htmlDoc) if title != nil { return title, nil } return nil, fmt.Errorf("title not found") }</code></pre> <p></p> </blockquote> <ul> <li>func renderNode(n *html.Node) string</li> </ul> <blockquote> <p>这个函数将 HTML 节点转换为字符串形式,使用 <code>html.Render</code> 将节点内容写入缓冲区,然后将缓冲区的内容以字符串形式返回。</p> <pre><code class="language-Go">func renderNode(n *html.Node) string{ var buf bytes.Buffer w := io.Writer(&buf) html.Render(w, n) //nolint return buf.String() }</code></pre> </blockquote> <ul> <li>func trimTitleTags(title string) string</li> </ul> <blockquote> <p>这个函数用于处理提取到的标题字符串,去除 <code><title></code> 标签的开始和结束标记,只返回标题文本内容</p> <pre><code class="language-Go">func trimTitleTags(title string) string { titleBegin := strings.Index(title, ">") titleEnd := strings.Index(title,"</") if titleEnd < 0 || titleBegin < 0 { return title } return title[titleBegin + 1 : titleEnd] }</code></pre> <p></p> </blockquote> <ul> <li>func GetLocation(body []byte) (location string)</li> </ul> <blockquote> <p>这个函数用于从 HTML 文档中获取重定向的目标 URL。它首先使用正则表达式 <code>reRefresh</code> 在文档中查找重定向的目标 URL,如果未找到,则使用正则表达式 <code>reReplace</code> 进行查找。如果找到,就返回第一个匹配到的重定向 URL。</p> <pre><code class="language-Go">func GetLocation(body []byte) (location string) { for _, match := range reRefresh.FindAllStringSubmatch(string(body),1){ location = match[1] break } if location == "" { for _, match := range reReplace.FindAllStringSubmatch(string(body),1){ location = match[1] break } } return }</code></pre> <p></p> </blockquote> <p><em><u><strong><span style="background-color:#a2e043;"> ps:这些函数主要用于解析 HTML 文档,从中提取标题并检索重定向目标 URL。它们结合使用 DOM 解析器和正则表达式来从文档中提取所需的信息。</span></strong></u></em></p> <h3>5、core-port-fingerprint-http.go</h3> <p>这个文件主要设置HTTP客户端和定义功能来处理响应体,识别其编码并限制最大读取大小。包含函数如下:</p> <p>首先定义:</p> <pre><code class="language-Go">var ErrOverflow = errors.New("OverflowMax") type Options struct { }</code></pre> <ul> <li><span style="color:#956fe7;">func newHttpClient(dialTimeout time.Duration) *http.Client</span></li> </ul> <blockquote> <ul> <li>创建并配置了一个具有特定传输配置的HTTP客户端,包括TLS设置、超时、最大连接数和禁用长连接等。</li> <li>设置了一个自定义的 <code>CheckRedirect</code> 函数来限制最大重定向次数为两次。</li> </ul> <pre><code class="language-Go">func newHttpClient(dialTimeout time.Duration) *http.Client { transport := &http.Transport{ TLSClientConfig: &tls.Config{//设置httpclient配置 InsecureSkipVerify: true, MinVersion: tls.VersionTLS10, }, DialContext: (&net.Dialer{ Timeout: dialTimeout, }).DialContext, MaxIdleConnsPerHost: 1, IdleConnTimeout: 100 * time.Millisecond, TLSHandshakeTimeout: 3 * time.Second, ExpectContinueTimeout: 3 * time.Second, DisableKeepAlives: true, ForceAttemptHttp2: false, Proxy: http.ProxyFromEnvironment, } // proxy //if options.ProxyUrl != "" { // proxyUrl, err := url.Parse(options.ProxyUrl) // if err != nil { // log.Fatalln(err) // } // transport.Proxy = http.ProxyURL(proxyUrl) //} return &http.Client{ Timeout : 3 * time.Second, Transport: transport, CheckRedirect: func(req *http.Request, via []*http.Request) error{ if len(via) >= 2{ return errors.New("stopped after 2 redirects") } return nil }, } }</code></pre> <p></p> </blockquote> <ul> <li><span style="color:#956fe7;">func getBody(resp *http.Response) (body []byte, err error)</span></li> </ul> <blockquote> <ul> <li>根据响应头中的 <code>Content-Encoding</code> 字段确定响应体的编码格式。</li> <li>通过使用适当的读取器(例如 <code>gzip.NewReader</code> 和 <code>flate.NewReader</code>)处理不同的压缩格式,如 gzip 和 deflate。</li> <li>通过调用 <code>readMaxSize</code> 函数,限制响应体的最大大小为300KB。</li> </ul> <pre><code class="language-Go">// getBody 识别响应Body的编码,读取body数据 func getBody(resp *http.Response) (body []byte, err error) { if resp.Body == nil || resp.Body == http.NoBody { return } var reader io.Reader switch resp.Header.Get("Content-Encoding") { case "gzip": reader, err = gzip.NewReader(resp.Body) case "deflate": reader = flate.NewReader(resp.Body) //case "br": // reader = brotli.NewReader(resp.Body) default: reader = resp.Body } if err == nil { body, err = readMaxSize(reader, 300*1024) // Max Size 300kb } return }</code></pre> <p></p> </blockquote> <ul> <li><span style="color:#956fe7;">func readMaxSize(r io.Reader, maxsize int) ([]byte, error)</span></li> </ul> <blockquote> <ul> <li>从 <code>io.Reader</code>(<code>r</code>)读取数据,同时限制最大尺寸(<code>maxsize</code>)。</li> <li>为一个字节切片(<code>b</code>)分配内存,并以块的方式读取数据,将其附加到切片中,直到达到最大尺寸。</li> <li>如果达到了尺寸限制,函数返回到目前为止读取的数据以及一个 <code>ErrOverflow</code> 错误。</li> </ul> <pre><code class="language-Go"> //这个函数的作用是安全地从 io.Reader 中读取数据,并在达到指定的最大尺寸时停止读取,以避免读取过多的数据造成内存溢出或其他问题。 func readMaxSize(r io.Reader, maxsize int) ([]byte, error) { b := make([]byte, 0, 512) for { if len(b) >= maxsize { return b, ErrOverflow } if len(b) == cap(b) { // Add more capacity (let append pick how much). b = append(b, 0)[:len(b)] } n, err := r.Read(b[len(b):cap(b)]) b = b[:len(b)+n] if err != nil { if err == io.EOF { err = nil } return b, err } } }</code></pre> <p></p> </blockquote> <p><em><u><strong><span style="background-color:#a2e043;">ps:这些函数为主要目的是创建一个具有特定配置的HTTP客户端,并提供功能来处理响应体的解码,考虑到不同的压缩格式,并限制读取数据的最大尺寸,以防止潜在的过大响应引发问题。</span></strong></u></em></p> <p></p> <p></p> <h3>6、core-port-fingerprint-httpInfo.go</h3> <p>这个文件主要实现了HTTP服务信息的探测和提取。主要函数如下:</p> <p>先定义变量httpsTopPort,高频的https端口</p> <pre><code class="language-Go">var httpsTopPort = []uint16{443, 4443, 1443, 8443} var httpClient *http.Client</code></pre> <ul> <li><span style="color:#956fe7;">func ProbeHttpInfo(ip net.IP, _port uint16, dialTimeout time.Duration) (httpInfo *port.HttpInfo, isDailErr bool)</span></li> </ul> <blockquote> <ul> <li>接收一个 IP、端口和请求超时时间,用于在给定的 IP 地址和端口上探测 HTTP 服务信息。</li> <li>根据指定的端口列表确定探测的协议(HTTP 或 HTTPS)。</li> <li>使用 <code>httpClient</code> 对象发送 HTTP 请求,并根据响应提取信息。</li> <li>通过循环尝试 HTTP 和 HTTPS 请求,根据响应来获取信息,包括状态码、重定向 URL、服务器信息、标题、TLS 证书相关信息等。</li> <li>通过 <code>webfinger</code> 包来分析响应的指纹信息,还会尝试提取页面的 favicon。</li> </ul> <pre><code class="language-Go">func ProbeHttpInfo(ip net.IP, _port uint16, dialTimeout time.Duration) (httpInfo *port.HttpInfo, isDailErr bool) { if httpClient == nil { httpClient = newHttpClient(dialTimeout) } var err error var rewriteUrl string var body []byte var resp *http.Response var schemes []string if util.IsUint16InList(_port, httpsTopPort) { //检测请求的_port是否在httpsTopPort列表中 schemes = []string{"https", "http"} }else { schemes = []string["http", "https"} } for _, scheme := range schemes { //循环使用https和http去请求 var rewriteNum int url2 := fmt.Sprintf("%s://%s:%d/", scheme, ip.String(), _port) goReq: resp, body, err = getReq(url2) //获取响应包和响应body if err != nil { if strings.HasSuffix(err.Error(), ioTimeoutStr) || strings.Contains(err.Error(), regusedStr) { return nil, true } continue } if resp != nil { if resp.ContentLength == -1 { resp.ContentLength = int64(len(body)) } //先在响应头中获取重定向url rewriteUrl2, _ := resp.Location() //重新获取url,提取响应包头中的location参数值 if rewriteUrl2 != nil { rewriteUrl = rewriteUrl2.String() }else { rewriteUrl = "" } //其次在body中获取重定向url location := GetLocation(body) //在title.go中,用于获取body中的location 的url,即重定向url if rewriteUrl == "" && location != "" { rewriteUrl = location } if location != "" && rewriteNum < 3 { if !strings.HasPrefix(location, "http") { //判断location前缀中是否http开头,如果不是,则进入下一个if if strings.HasPrefix(location, "/") { resp.Request.URL.Path = location }else { resp.Request.URL.Path = resp.Request.URL.Path[:strings.LastIndex(resp.Request.URL.Path, "/")+1] + location//将 HTTP 请求的 URL 路径修改为原路径中最后一个斜杠之前的部分,再添加上 location 变量的值。这样,新的路径将以最后一个斜杠结尾,然后连接上 location 的值。 } location = resp.Request.URL.String() } url2 = location rewriteNum++ goto goReq } httpInfo = new(port.HttpInfo) //在port.go文件中,为HttpInfo结构体 httpInfo.Url = resp.Request.URL.String() httpInfo.StatusCode = resp.StatusCode httpInfo.ContentLen = int(resp.ContentLength) httpInfo.Location = rewriteUrl httpInfo.Server = resp.Header.Get("Server") httpInfo.Title = ExtracTitle(body) //title.go文件中,获取响应包中的title if resp.TLS != nil && len(resp.TLS.PeerCertificates) > 0 { //如果是https,获取tls握手证书的信息 httpInfo.TlsCN = resp.TLS.PeerCertificates[0].Subject.CommonName //证书主题部分的通用名称信息 httpInfo.TlsDNS = resp.TLS.PeerCertificates[0].DNSNames //证书中包含的DNS名称列表 } //指纹信息 err = webfinger.ParseWebFingerData(webfinger.DefFingerData) if err == nil { resp.Body = io.NopCloser(bytes.NewReader(body)) httpInfo.Fingers = webfinger.WebFingerIdent(resp) //j检测识别响应头和响应body识别指纹 //favicon fau := webfinger.FindFaviconUrl(string(body)) //获取响应body中的favionUrl if fau != ""{ if !strings.HasPrefix(fau, "http") { fau = resp.Request.URL.String() + fau } _, body2, err2 := getReq(fau) //获取请求favicon的响应包信息 if err2 == nil && len(body2) != 0{ httpInfo.Fingers = append(httpInfo.Fingers, webfinger.WebFingerIdentByFavicon(body2)...)//检测favicon指纹 } } } if resp.StatusCode != 400 { break } } } return httpInfo, false }</code></pre> <p></p> </blockquote> <ul> <li><span style="color:#956fe7;">func getReq(url2 string) (resp *http.Response, body []byte, err error)</span></li> </ul> <blockquote> <ul> <li>构造一个 HTTP GET 请求,设置请求头,然后使用 <code>httpClient</code> 执行请求。</li> <li>从响应中读取内容,如果是文本类型则进行编码解析(尝试将非 UTF-8 编码的文本解析为 UTF-8)。</li> </ul> <pre><code class="language-Go">func getReq (url2 string) (resp *http.Response, body []byte, err error) { req, err := http.NewRequest(http.MethodGet, url2, http.Nobody) //创建get请求 if err != nil { return } req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36") req.Header.Set("Accept-Encoding", "gzip, deflate") req.Close = true //关闭keepalive resp, err = httpClient.Do(req) if err != nil { return } if resp.Body != http.NoBody && resp.Body != nil { //如果body不为空且不为http.NoBody,则提取body body, _ = getBody(resp) //在http.go文件中,主要为识别body的编码,读取body数据 if contentTypes, _ := resp.Header["Content-Type"]; len(contentTypes) > 0 { if strings.Contains(contentTypes[0], "text") {//如果响应类型是文本类型,则对响应体进行解码,将非utf-8编码的文本解析为utf-8 _body, err2 := DecodeData(body, resp.Header) if err2 == nil { body = _body } resp.Body = io.NopCloser(bytes.NewReader(body)) } } } return }</code></pre> <p></p> </blockquote> <p>ps:这些函数主要是尝试使用 HTTP 和 HTTPS 进行请求,根据响应提取 HTTP 服务的相关信息,包括标题、服务器信息、TLS 证书信息以及其他可能的指纹信息,为后续服务识别和指纹探测提供数据支持。</p> <h2 style="background-color:transparent;">四、小结</h2> <p>这部分的功能主要围绕在对http和https指纹的识别,通过网页titile和icon的hash进行指纹判断;其次是针对端口进行服务识别,主要匹配规则库在“fingerprint-webfinger-finger.json” 文件和“core-port-fingerprint-rules.go”文件中。</p> <p></p> </div> </div> </div> </div> </div> <!--PC和WAP自适应版--> <div id="SOHUCS" sid="1736114035088220160"></div> <script type="text/javascript" src="/views/front/js/chanyan.js"></script> <!-- 文章页-底部 动态广告位 --> <div class="youdao-fixed-ad" id="detail_ad_bottom"></div> </div> <div class="col-md-3"> <div class="row" id="ad"> <!-- 文章页-右侧1 动态广告位 --> <div id="right-1" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_1"> </div> </div> <!-- 文章页-右侧2 动态广告位 --> <div id="right-2" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_2"></div> </div> <!-- 文章页-右侧3 动态广告位 --> <div id="right-3" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_3"></div> </div> </div> </div> </div> </div> </div> <div class="container"> <h4 class="pt20 mb15 mt0 border-top">你可能感兴趣的:(golang,开发语言,后端)</h4> <div id="paradigm-article-related"> <div class="recommend-post mb30"> <ul class="widget-links"> <li><a href="/article/1835509897106649088.htm" title="Long类型前后端数据不一致" target="_blank">Long类型前后端数据不一致</a> <span class="text-muted">igotyback</span> <a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a> <div>响应给前端的数据浏览器控制台中response中看到的Long类型的数据是正常的到前端数据不一致前后端数据类型不匹配是一个常见问题,尤其是当后端使用Java的Long类型(64位)与前端JavaScript的Number类型(最大安全整数为2^53-1,即16位)进行数据交互时,很容易出现精度丢失的问题。这是因为JavaScript中的Number类型无法安全地表示超过16位的整数。为了解决这个问</div> </li> <li><a href="/article/1835501948011376640.htm" title="使用 FinalShell 进行远程连接(ssh 远程连接 Linux 服务器)" target="_blank">使用 FinalShell 进行远程连接(ssh 远程连接 Linux 服务器)</a> <span class="text-muted">编程经验分享</span> <a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/1.htm">开发工具</a><a class="tag" taget="_blank" href="/search/%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">服务器</a><a class="tag" taget="_blank" href="/search/ssh/1.htm">ssh</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a> <div>目录前言基本使用教程新建远程连接连接主机自定义命令路由追踪前言后端开发,必然需要和服务器打交道,部署应用,排查问题,查看运行日志等等。一般服务器都是集中部署在机房中,也有一些直接是云服务器,总而言之,程序员不可能直接和服务器直接操作,一般都是通过ssh连接来登录服务器。刚接触远程连接时,使用的是XSHELL来远程连接服务器,连接上就能够操作远程服务器了,但是仅用XSHELL并没有上传下载文件的功能</div> </li> <li><a href="/article/1835489438243844096.htm" title="Low Power概念介绍-Voltage Area" target="_blank">Low Power概念介绍-Voltage Area</a> <span class="text-muted">飞奔的大虎</span> <div>随着智能手机,以及物联网的普及,芯片功耗的问题最近几年得到了越来越多的重视。为了实现集成电路的低功耗设计目标,我们需要在系统设计阶段就采用低功耗设计的方案。而且,随着设计流程的逐步推进,到了芯片后端设计阶段,降低芯片功耗的方法已经很少了,节省的功耗百分比也不断下降。芯片的功耗主要由静态功耗(staticleakagepower)和动态功耗(dynamicpower)构成。静态功耗主要是指电路处于等</div> </li> <li><a href="/article/1835448238103162880.htm" title="springboot+vue项目实战一-创建SpringBoot简单项目" target="_blank">springboot+vue项目实战一-创建SpringBoot简单项目</a> <span class="text-muted">苹果酱0567</span> <a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95%E9%A2%98%E6%B1%87%E6%80%BB%E4%B8%8E%E8%A7%A3%E6%9E%90/1.htm">面试题汇总与解析</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/boot/1.htm">boot</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E4%B8%AD%E9%97%B4%E4%BB%B6/1.htm">中间件</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a> <div>这段时间抽空给女朋友搭建一个个人博客,想着记录一下建站的过程,就当做笔记吧。虽然复制zjblog只要一个小时就可以搞定一个网站,或者用cms系统,三四个小时就可以做出一个前后台都有的网站,而且想做成啥样也都行。但是就是要从新做,自己做的意义不一样,更何况,俺就是专门干这个的,嘿嘿嘿要做一个网站,而且从零开始,首先呢就是技术选型了,经过一番思量决定选择-SpringBoot做后端,前端使用Vue做一</div> </li> <li><a href="/article/1835447479580061696.htm" title="uniapp map组件自定义markers标记点" target="_blank">uniapp map组件自定义markers标记点</a> <span class="text-muted">以对_</span> <a class="tag" taget="_blank" href="/search/uni-app%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/1.htm">uni-app学习记录</a><a class="tag" taget="_blank" href="/search/uni-app/1.htm">uni-app</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a> <div>需求是根据后端返回数据在地图上显示标记点,并且根据数据状态控制标记点颜色,标记点背景通过两张图片实现控制{{item.options.labelName}}exportdefault{data(){return{storeIndex:0,locaInfo:{longitude:120.445172,latitude:36.111387},markers:[//标点列表{id:1,//标记点idin</div> </li> <li><a href="/article/1835443696431099904.htm" title="笋丁网页自动回复机器人V3.0.0免授权版源码" target="_blank">笋丁网页自动回复机器人V3.0.0免授权版源码</a> <span class="text-muted">希希分享</span> <a class="tag" taget="_blank" href="/search/%E8%BD%AF%E5%B8%8C%E7%BD%9158soho_cn/1.htm">软希网58soho_cn</a><a class="tag" taget="_blank" href="/search/%E6%BA%90%E7%A0%81%E8%B5%84%E6%BA%90/1.htm">源码资源</a><a class="tag" taget="_blank" href="/search/%E7%AC%8B%E4%B8%81%E7%BD%91%E9%A1%B5%E8%87%AA%E5%8A%A8%E5%9B%9E%E5%A4%8D%E6%9C%BA%E5%99%A8%E4%BA%BA/1.htm">笋丁网页自动回复机器人</a> <div>笋丁网页机器人一款可设置自动回复,默认消息,调用自定义api接口的网页机器人。此程序后端语言使用Golang,内存占用最高不超过30MB,1H1G服务器流畅运行。仅支持Linux服务器部署,不支持虚拟主机,请悉知!使用自定义api功能需要有一定的建站基础。源码下载:https://download.csdn.net/download/m0_66047725/89754250更多资源下载:关注我。安</div> </li> <li><a href="/article/1835437775344726016.htm" title="博客网站制作教程" target="_blank">博客网站制作教程</a> <span class="text-muted">2401_85194651</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/maven/1.htm">maven</a> <div>首先就是技术框架:后端:Java+SpringBoot数据库:MySQL前端:Vue.js数据库连接:JPA(JavaPersistenceAPI)1.项目结构blog-app/├──backend/│├──src/main/java/com/example/blogapp/││├──BlogApplication.java││├──config/│││└──DatabaseConfig.java</div> </li> <li><a href="/article/1835432358141063168.htm" title="深入浅出 -- 系统架构之负载均衡Nginx的性能优化" target="_blank">深入浅出 -- 系统架构之负载均衡Nginx的性能优化</a> <span class="text-muted">xiaoli8748_软件开发</span> <a class="tag" taget="_blank" href="/search/%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84/1.htm">系统架构</a><a class="tag" taget="_blank" href="/search/%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84/1.htm">系统架构</a><a class="tag" taget="_blank" href="/search/%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/1.htm">负载均衡</a><a class="tag" taget="_blank" href="/search/nginx/1.htm">nginx</a> <div>一、Nginx性能优化到这里文章的篇幅较长了,最后再来聊一下关于Nginx的性能优化,主要就简单说说收益最高的几个优化项,在这块就不再展开叙述了,毕竟影响性能都有多方面原因导致的,比如网络、服务器硬件、操作系统、后端服务、程序自身、数据库服务等,对于性能调优比较感兴趣的可以参考之前《JVM性能调优》中的调优思想。优化一:打开长连接配置通常Nginx作为代理服务,负责分发客户端的请求,那么建议开启H</div> </li> <li><a href="/article/1835428821877223424.htm" title="计算机毕业设计PHP仓储综合管理系统(源码+程序+VUE+lw+部署)" target="_blank">计算机毕业设计PHP仓储综合管理系统(源码+程序+VUE+lw+部署)</a> <span class="text-muted">java毕设程序源码王哥</span> <a class="tag" taget="_blank" href="/search/php/1.htm">php</a><a class="tag" taget="_blank" href="/search/%E8%AF%BE%E7%A8%8B%E8%AE%BE%E8%AE%A1/1.htm">课程设计</a><a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a> <div>该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流项目运行环境配置:phpStudy+Vscode+Mysql5.7+HBuilderX+Navicat11+Vue+Express。项目技术:原生PHP++Vue等等组成,B/S模式+Vscode管理+前后端分离等等。环境需要1.运行环境:最好是小皮phpstudy最新版,我们在这个版本上开发的。其他版本理论上也可以。2.开发</div> </li> <li><a href="/article/1835420753252675584.htm" title="微信小程序开发注意事项" target="_blank">微信小程序开发注意事项</a> <span class="text-muted">jun778895</span> <a class="tag" taget="_blank" href="/search/%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F/1.htm">微信小程序</a><a class="tag" taget="_blank" href="/search/%E5%B0%8F%E7%A8%8B%E5%BA%8F/1.htm">小程序</a> <div>微信小程序开发是一个融合了前端开发、用户体验设计、后端服务(可选)以及微信小程序平台特性的综合性项目。这里,我将详细介绍一个典型的小程序开发项目的全过程,包括项目规划、设计、开发、测试及部署上线等各个环节,并尽量使内容达到或超过2000字的要求。一、项目规划1.1项目背景与目标假设我们要开发一个名为“智慧校园助手”的微信小程序,旨在为学生提供一站式校园生活服务,包括课程表查询、图书馆座位预约、食堂</div> </li> <li><a href="/article/1835404621376483328.htm" title="Golang语言基础知识点总结" target="_blank">Golang语言基础知识点总结</a> <span class="text-muted">最帅猪猪侠</span> <a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a> <div>Golang语言基础知识点小总结1.go语言有两大类型:值类型:数值类型,bool,string,数组,struct结构体变量直接存储值,内存通常在栈中分配,修改值,不会对源对象产生影响引用类型:指针,slice切片,管道chan,map,interface变量存储的是一个地址,这个地址对应的空间才真正存储数据值,内存通常在堆上分配,当没有任何变量引用这个地址时,该地址对应的数据空间就成为一个垃圾</div> </li> <li><a href="/article/1835397938482868224.htm" title="golang获取用户输入的几种方式" target="_blank">golang获取用户输入的几种方式</a> <span class="text-muted">余生逆风飞翔</span> <a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a> <div>一、定义结构体typeUserInfostruct{Namestring`json:"name"`Ageint`json:"age"`Addstring`json:"add"`}typeReturnDatastruct{Messagestring`json:"message"`Statusstring`json:"status"`DataUserInfo`json:"data"`}二、get请求的</div> </li> <li><a href="/article/1835395418381447168.htm" title="【Golang】实现 Excel 文件下载功能" target="_blank">【Golang】实现 Excel 文件下载功能</a> <span class="text-muted">RumIV</span> <a class="tag" taget="_blank" href="/search/Golang/1.htm">Golang</a><a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a><a class="tag" taget="_blank" href="/search/excel/1.htm">excel</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a> <div>在当今的网络应用开发中,提供数据导出功能是一项常见的需求。Excel作为一种广泛使用的电子表格格式,通常是数据导出的首选格式之一。在本教程中,我们将学习如何使用Go语言和GinWeb框架来创建一个Excel文件,并允许用户通过HTTP请求下载该文件。准备工作在开始之前,请确保您的开发环境中已经安装了Go语言和相关的开发工具。此外,您还需要安装GinWeb框架和excelize包,这两个包都将用于我</div> </li> <li><a href="/article/1835393400380157952.htm" title="VUE3 + xterm + nestjs实现web远程终端 或 连接开启SSH登录的路由器和交换机。" target="_blank">VUE3 + xterm + nestjs实现web远程终端 或 连接开启SSH登录的路由器和交换机。</a> <span class="text-muted">焚木灵</span> <a class="tag" taget="_blank" href="/search/node.js/1.htm">node.js</a><a class="tag" taget="_blank" href="/search/vue/1.htm">vue</a> <div>可远程连接系统终端或开启SSH登录的路由器和交换机。相关资料:xtermjs/xterm.js:Aterminalfortheweb(github.com)后端实现(NestJS):1、安装依赖:npminstallnode-ssh@nestjs/websockets@nestjs/platform-socket.io2、我们将创建一个名为RemoteControlModule的NestJS模块,</div> </li> <li><a href="/article/1835391887482122240.htm" title="golang 实现文件上传下载" target="_blank">golang 实现文件上传下载</a> <span class="text-muted">wangwei830</span> <a class="tag" taget="_blank" href="/search/go/1.htm">go</a> <div>Gin框架上传下载上传(支持批量上传)httpRouter.POST("/upload",func(ctx*gin.Context){forms,err:=ctx.MultipartForm()iferr!=nil{fmt.Println("error",err)}files:=forms.File["fileName"]for_,v:=rangefiles{iferr:=ctx.SaveUplo</div> </li> <li><a href="/article/1835385458356482048.htm" title="uniapp实现动态标记效果详细步骤【前端开发】" target="_blank">uniapp实现动态标记效果详细步骤【前端开发】</a> <span class="text-muted">2401_85123349</span> <a class="tag" taget="_blank" href="/search/uni-app/1.htm">uni-app</a> <div>第二个点在于实现将已经被用户标记的内容在下一次获取后刷新它的状态为已标记。这是什么意思呢?比如说上面gif图中的这些人物对象,有一些已被该用户添加为关心,那么当用户下一次进入该页面时,这些已经被添加关心的对象需要以“红心”状态显现出来。这个点的难度还不算大,只需要在每一次获取后端的内容后对标记对象进行状态更新即可。II.动态标记效果实现思路和步骤首先,整体的思路是利用动态类名对不同的元素进行选择。</div> </li> <li><a href="/article/1835382812061036544.htm" title="golang实现从服务器下载文件到本地指定目录" target="_blank">golang实现从服务器下载文件到本地指定目录</a> <span class="text-muted">余生逆风飞翔</span> <a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a><a class="tag" taget="_blank" href="/search/%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">服务器</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a> <div>一、连接服务器,采用sftp连接模式packagemiddlewaresimport("fmt""time""github.com/pkg/sftp""golang.org/x/crypto/ssh")//建立服务器连接funcConnect(user,password,hoststring,portint)(*sftp.Client,error){var(auth[]ssh.AuthMethod</div> </li> <li><a href="/article/1835372217710505984.htm" title="【Golang】 Golang 的 GORM 库中的 Rows 函数" target="_blank">【Golang】 Golang 的 GORM 库中的 Rows 函数</a> <span class="text-muted">不爱洗脚的小滕</span> <a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a> <div>文章目录前言一、Rows函数解释二、代码实现三、总结前言在使用Go语言进行数据库操作时,GORM(GoObject-RelationalMapping)库是一个常用的工具。它提供了一种简洁和强大的方式来处理数据库操作。本文将介绍GORM库中的Rows函数,这是一个用于执行原生SQL查询并返回结果的函数。一、Rows函数解释在GORM库中,Rows函数用于执行原生SQL查询并返回*sql.Rows结</div> </li> <li><a href="/article/1835371965410537472.htm" title="【Golang】使用 Golang 语言和 excelize 库将数据写入Excel" target="_blank">【Golang】使用 Golang 语言和 excelize 库将数据写入Excel</a> <span class="text-muted">不爱洗脚的小滕</span> <a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a><a class="tag" taget="_blank" href="/search/excel/1.htm">excel</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a> <div>文章目录前言一、Excelize简介二、代码实现1.获取依赖2.示例代码三、总结前言在数据处理和分析中,Excel作为一种常见的电子表格格式,被广泛应用于各种场景。然而,如何在Go语言中有效地处理Excel文件呢?在这篇博客中,我将介绍如何使用Go语言和excelize库将数据写入Excel文件。一、Excelize简介Excelize是一个用于读取和写入MicrosoftExcel™(XLSX)</div> </li> <li><a href="/article/1835353691520069632.htm" title="后端开发刷题 | 把数字翻译成字符串(动态规划)" target="_blank">后端开发刷题 | 把数字翻译成字符串(动态规划)</a> <span class="text-muted">jingling555</span> <a class="tag" taget="_blank" href="/search/%E7%AC%94%E8%AF%95%E9%A2%98%E7%9B%AE/1.htm">笔试题目</a><a class="tag" taget="_blank" href="/search/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/1.htm">动态规划</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/1.htm">数据结构</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a> <div>描述有一种将字母编码成数字的方式:'a'->1,'b->2',...,'z->26'。现在给一串数字,返回有多少种可能的译码结果数据范围:字符串长度满足0=10&&num<=26){if(i==1){dp[i]+=1;}else{dp[i]+=dp[i-2];}}}returndp[nums.length()-1];}}</div> </li> <li><a href="/article/1835352325032603648.htm" title="第三十一节:Vue路由:前端路由vs后端路由的了解" target="_blank">第三十一节:Vue路由:前端路由vs后端路由的了解</a> <span class="text-muted">曹老师</span> <div>1.认识前端路由和后端路由前端路由相对于后端路由而言的,在理解前端路由之前先对于路由有一个基本的了解路由:简而言之,就是把信息从原地址传输到目的地的活动对于我们来说路由就是:根据不同的url地址展示不同的页面内容1.1后端路由以前咱们接触比较多的后端路由,当改变url地址时,浏览器会向服务器发送请求,服务器根据这个url,返回不同的资源内容后端路由的特点就是前端每次跳转到不同url地址,都会重新访</div> </li> <li><a href="/article/1835350535818014720.htm" title="如何建设数据中台(五)——数据汇集—打破企业数据孤岛" target="_blank">如何建设数据中台(五)——数据汇集—打破企业数据孤岛</a> <span class="text-muted">weixin_47088026</span> <a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95%E5%92%8C%E6%80%BB%E7%BB%93/1.htm">学习记录和总结</a><a class="tag" taget="_blank" href="/search/%E4%B8%AD%E5%8F%B0/1.htm">中台</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E4%B8%AD%E5%8F%B0/1.htm">数据中台</a><a class="tag" taget="_blank" href="/search/%E7%A8%8B%E5%BA%8F%E4%BA%BA%E7%94%9F/1.htm">程序人生</a><a class="tag" taget="_blank" href="/search/%E7%BB%8F%E9%AA%8C%E5%88%86%E4%BA%AB/1.htm">经验分享</a> <div>数据汇集——打破企业数据孤岛要构建企业级数据中台,第一步就是将企业内部各个业务系统的数据实现互通互联,打破数据孤岛,主要通过数据汇聚和交换来实现。企业采集的数据可以是线上采集、线下数据采集、互联网数据采集、内部数据采集等。线上数据采集主要载体分为互联网和移动互联网两种,对应有系统平台、网页、H5、小程序、App等,可以采用前端或后端埋点方式采集数据。线下数据采集主要是通过硬件来采集,例如:WiFi</div> </li> <li><a href="/article/1835329169400098816.htm" title="Go编程语言前景怎么样?参加培训好就业吗" target="_blank">Go编程语言前景怎么样?参加培训好就业吗</a> <span class="text-muted">QFdongdong</span> <div>Go语言专门针对多处理器系统应用程序的编程进行了优化,使用Go编译的程序可以媲美C或C++代码的速度,而且更加安全、支持并行进程。不仅可以开发web,可以开发底层,目前知乎就是用golang开发。区块链首选语言就是go,以-太坊,超级账本都是基于go语言,还有go语言版本的btcd.Go的目标是希望提升现有编程语言对程序库等依赖性(dependency)的管理,这些软件元素会被应用程序反复调用。由</div> </li> <li><a href="/article/1835320664073007104.htm" title="Go 面向包的设计和架构分层" target="_blank">Go 面向包的设计和架构分层</a> <span class="text-muted">云满笔记</span> <a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a><a class="tag" taget="_blank" href="/search/%E6%9E%B6%E6%9E%84/1.htm">架构</a><a class="tag" taget="_blank" href="/search/directory/1.htm">directory</a><a class="tag" taget="_blank" href="/search/layout/1.htm">layout</a><a class="tag" taget="_blank" href="/search/src/1.htm">src</a><a class="tag" taget="_blank" href="/search/project/1.htm">project</a> <div>标题Go面向包的设计和架构分层序前项目架构分层工具包项目应用项目cmd/internal/internal/pkg/pkg/vendor/面向包的设计和验证包的位置依赖包导入应用级别的策略数据的发送和接收错误处理测试捕获错误不建议的目录结论Go面向包的设计和架构分层序本篇内容主要讲解golang项目的面向包设计准则和基础的架构分层。信息来自原文ArdanLabs:Package-Oriented-</div> </li> <li><a href="/article/1835302696509337600.htm" title="Golang channel 死锁" target="_blank">Golang channel 死锁</a> <span class="text-muted">羊城程序猿</span> <a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a><a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a> <div>死锁是指两个或两个以上的协程的执行过程中,由于竞争资源或由于彼此通信而造成的一种阻塞的现象,若无外力作用,他们将无法推进下去,以下是总结出来的几种死锁情况。1.死锁1:一个通道在一个主go程里同时进行读和写2.死锁2:go程开启之前使用通道3.死锁3:通道1中调用了通道2,通道2中调用通道14.死锁4:直接读取空channel的死锁5.死锁5:超过channel缓存继续写入数据导致死锁6.向已关闭</div> </li> <li><a href="/article/1835302697079762944.htm" title="golang学习笔记--MPG模型" target="_blank">golang学习笔记--MPG模型</a> <span class="text-muted">xxzed</span> <a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a><a class="tag" taget="_blank" href="/search/%23/1.htm">#</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/1.htm">学习笔记</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/%E7%AC%94%E8%AE%B0/1.htm">笔记</a><a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a> <div>MPG模式:M(Machine):操作系统的主线程P(Processor):协程执行需要的资源(上下文context),可以看作一个局部的调度器,使go代码在一个线程上跑,他是实现从N:1到N:M映射的关键G(Goroutine):协程,有自己的栈。包含指令指针(instructionpointer)和其它信息(正在等待的channel等等),用于调度。一个P下面可以有多个G1、当前程序有三个M,</div> </li> <li><a href="/article/1835302695590785024.htm" title="Github 2024-09-12 Go开源项目日报Top10" target="_blank">Github 2024-09-12 Go开源项目日报Top10</a> <span class="text-muted">老孙正经胡说</span> <a class="tag" taget="_blank" href="/search/github/1.htm">github</a><a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E6%BA%90/1.htm">开源</a><a class="tag" taget="_blank" href="/search/Github%E8%B6%8B%E5%8A%BF%E5%88%86%E6%9E%90/1.htm">Github趋势分析</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE/1.htm">开源项目</a><a class="tag" taget="_blank" href="/search/Python/1.htm">Python</a><a class="tag" taget="_blank" href="/search/Golang/1.htm">Golang</a> <div>根据GithubTrendings的统计,今日(2024-09-12统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下:开发语言项目数量Go项目10C项目1Terraform:基础设施即代码的开源工具创建周期:3626天开发语言:Go协议类型:OtherStar数量:40393个Fork数量:9397次关注人数:40393人贡献人数:358人OpenIssues数量:1943个Git</div> </li> <li><a href="/article/1835297531945381888.htm" title="Golang Channel" target="_blank">Golang Channel</a> <span class="text-muted">PandaSkr</span> <a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a> <div>Channel解析1.Channel源码分析1.1Channel数据结构typehchanstruct{qcountuint//channel的元素数量dataqsizuint//channel循环队列长度bufunsafe.Pointer//指向循环队列的指针elemsizeuint16//元素大小closeduint32//channel是否关闭0-未关闭elemtype*_type//元素类</div> </li> <li><a href="/article/1835296397365178368.htm" title="创建一个完整的购物商城系统是一个复杂的项目,涉及前端(用户界面)、后端(服务器逻辑)、数据库等多个部分。由于篇幅限制,我无法在这里提供一个完整的系统代码,但我可以分别给出一些关键部分的示例代码,涵盖几" target="_blank">创建一个完整的购物商城系统是一个复杂的项目,涉及前端(用户界面)、后端(服务器逻辑)、数据库等多个部分。由于篇幅限制,我无法在这里提供一个完整的系统代码,但我可以分别给出一些关键部分的示例代码,涵盖几</a> <span class="text-muted">uthRaman</span> <a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/ui/1.htm">ui</a><a class="tag" taget="_blank" href="/search/%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">服务器</a> <div>前端(HTML/CSS/JavaScript)grsyzp.cnHTML页面结构(index.html)html购物商城欢迎来到购物商城JavaScript(Ajax请求商品数据,app.js)javascriptdocument.addEventListener('DOMContentLoaded',function(){fetch('/api/products').then(response=</div> </li> <li><a href="/article/1835290475628687360.htm" title="若依后端正常启动但是uniapp移动端提示后端接口异常" target="_blank">若依后端正常启动但是uniapp移动端提示后端接口异常</a> <span class="text-muted">大可大可抖</span> <a class="tag" taget="_blank" href="/search/uni-app/1.htm">uni-app</a> <div>pc端能用模拟器也能正常连接接口,手机端真机调试连不上接口解决:1.先看config.js的填自己的ip地址module.exports={//baseUrl:'https://vue.ruoyi.vip/prod-api',baseUrl:"http://192.168.101.5:8080",}2.网络环境问题(防火墙)点击属性然后选择专用</div> </li> <li><a href="/article/110.htm" title="强大的销售团队背后 竟然是大数据分析的身影" target="_blank">强大的销售团队背后 竟然是大数据分析的身影</a> <span class="text-muted">蓝儿唯美</span> <a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90/1.htm">数据分析</a> <div>Mark Roberge是HubSpot的首席财务官,在招聘销售职位时使用了大量数据分析。但是科技并没有挤走直觉。 大家都知道数理学家实际上已经渗透到了各行各业。这些热衷数据的人们通过处理数据理解商业流程的各个方面,以重组弱点,增强优势。 Mark Roberge是美国HubSpot公司的首席财务官,HubSpot公司在构架集客营销现象方面出过一份力——因此他也是一位数理学家。他使用数据分析 </div> </li> <li><a href="/article/237.htm" title="Haproxy+Keepalived高可用双机单活" target="_blank">Haproxy+Keepalived高可用双机单活</a> <span class="text-muted">bylijinnan</span> <a class="tag" taget="_blank" href="/search/%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/1.htm">负载均衡</a><a class="tag" taget="_blank" href="/search/keepalived/1.htm">keepalived</a><a class="tag" taget="_blank" href="/search/haproxy/1.htm">haproxy</a><a class="tag" taget="_blank" href="/search/%E9%AB%98%E5%8F%AF%E7%94%A8/1.htm">高可用</a> <div>我们的应用MyApp不支持集群,但要求双机单活(两台机器:master和slave): 1.正常情况下,只有master启动MyApp并提供服务 2.当master发生故障时,slave自动启动本机的MyApp,同时虚拟IP漂移至slave,保持对外提供服务的IP和端口不变 F5据说也能满足上面的需求,但F5的通常用法都是双机双活,单活的话还没研究过 服务器资源 10.7</div> </li> <li><a href="/article/364.htm" title="eclipse编辑器中文乱码问题解决" target="_blank">eclipse编辑器中文乱码问题解决</a> <span class="text-muted">0624chenhong</span> <a class="tag" taget="_blank" href="/search/eclipse%E4%B9%B1%E7%A0%81/1.htm">eclipse乱码</a> <div>使用Eclipse编辑文件经常出现中文乱码或者文件中有中文不能保存的问题,Eclipse提供了灵活的设置文件编码格式的选项,我们可以通过设置编码 格式解决乱码问题。在Eclipse可以从几个层面设置编码格式:Workspace、Project、Content Type、File 本文以Eclipse 3.3(英文)为例加以说明: 1. 设置Workspace的编码格式: Windows-&g</div> </li> <li><a href="/article/491.htm" title="基础篇--resources资源" target="_blank">基础篇--resources资源</a> <span class="text-muted">不懂事的小屁孩</span> <a class="tag" taget="_blank" href="/search/android/1.htm">android</a> <div>最近一直在做java开发,偶尔敲点android代码,突然发现有些基础给忘记了,今天用半天时间温顾一下resources的资源。 String.xml    字符串资源   涉及国际化问题  http://www.2cto.com/kf/201302/190394.html   string-array</div> </li> <li><a href="/article/618.htm" title="接上篇补上window平台自动上传证书文件的批处理问卷" target="_blank">接上篇补上window平台自动上传证书文件的批处理问卷</a> <span class="text-muted">酷的飞上天空</span> <a class="tag" taget="_blank" href="/search/window/1.htm">window</a> <div> @echo off : host=服务器证书域名或ip,需要和部署时服务器的域名或ip一致 ou=公司名称, o=公司名称 set host=localhost set ou=localhost set o=localhost set password=123456 set validity=3650 set salias=s</div> </li> <li><a href="/article/745.htm" title="企业物联网大潮涌动:如何做好准备?" target="_blank">企业物联网大潮涌动:如何做好准备?</a> <span class="text-muted">蓝儿唯美</span> <a class="tag" taget="_blank" href="/search/%E4%BC%81%E4%B8%9A/1.htm">企业</a> <div>物联网的可能性也许是无限的。要找出架构师可以做好准备的领域然后利用日益连接的世界。 尽管物联网(IoT)还很新,企业架构师现在也应该为一个连接更加紧密的未来做好计划,而不是跟上闸门被打开后的集成挑战。“问题不在于物联网正在进入哪些领域,而是哪些地方物联网没有在企业推进,” Gartner研究总监Mike Walker说。 Gartner预测到2020年物联网设备安装量将达260亿,这些设备在全</div> </li> <li><a href="/article/872.htm" title="spring学习——数据库(mybatis持久化框架配置)" target="_blank">spring学习——数据库(mybatis持久化框架配置)</a> <span class="text-muted">a-john</span> <a class="tag" taget="_blank" href="/search/mybatis/1.htm">mybatis</a> <div>Spring提供了一组数据访问框架,集成了多种数据访问技术。无论是JDBC,iBATIS(mybatis)还是Hibernate,Spring都能够帮助消除持久化代码中单调枯燥的数据访问逻辑。可以依赖Spring来处理底层的数据访问。 mybatis是一种Spring持久化框架,要使用mybatis,就要做好相应的配置: 1,配置数据源。有很多数据源可以选择,如:DBCP,JDBC,aliba</div> </li> <li><a href="/article/999.htm" title="Java静态代理、动态代理实例" target="_blank">Java静态代理、动态代理实例</a> <span class="text-muted">aijuans</span> <a class="tag" taget="_blank" href="/search/Java%E9%9D%99%E6%80%81%E4%BB%A3%E7%90%86/1.htm">Java静态代理</a> <div> 采用Java代理模式,代理类通过调用委托类对象的方法,来提供特定的服务。委托类需要实现一个业务接口,代理类返回委托类的实例接口对象。 按照代理类的创建时期,可以分为:静态代理和动态代理。 所谓静态代理: 指程序员创建好代理类,编译时直接生成代理类的字节码文件。 所谓动态代理: 在程序运行时,通过反射机制动态生成代理类。   一、静态代理类实例: 1、Serivce.ja</div> </li> <li><a href="/article/1126.htm" title="Struts1与Struts2的12点区别" target="_blank">Struts1与Struts2的12点区别</a> <span class="text-muted">asia007</span> <a class="tag" taget="_blank" href="/search/Struts1%E4%B8%8EStruts2/1.htm">Struts1与Struts2</a> <div>1) 在Action实现类方面的对比:Struts 1要求Action类继承一个抽象基类;Struts 1的一个具体问题是使用抽象类编程而不是接口。Struts 2 Action类可以实现一个Action接口,也可以实现其他接口,使可选和定制的服务成为可能。Struts 2提供一个ActionSupport基类去实现常用的接口。即使Action接口不是必须实现的,只有一个包含execute方法的P</div> </li> <li><a href="/article/1253.htm" title="初学者要多看看帮助文档 不要用js来写Jquery的代码" target="_blank">初学者要多看看帮助文档 不要用js来写Jquery的代码</a> <span class="text-muted">百合不是茶</span> <a class="tag" taget="_blank" href="/search/jquery/1.htm">jquery</a><a class="tag" taget="_blank" href="/search/js/1.htm">js</a> <div>解析json数据的时候需要将解析的数据写到文本框中,  出现了用js来写Jquery代码的问题;   1, JQuery的赋值  有问题    代码如下: data.username 表示的是:  网易            $("#use</div> </li> <li><a href="/article/1380.htm" title="经理怎么和员工搞好关系和信任" target="_blank">经理怎么和员工搞好关系和信任</a> <span class="text-muted">bijian1013</span> <a class="tag" taget="_blank" href="/search/%E5%9B%A2%E9%98%9F/1.htm">团队</a><a class="tag" taget="_blank" href="/search/%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86/1.htm">项目管理</a><a class="tag" taget="_blank" href="/search/%E7%AE%A1%E7%90%86/1.htm">管理</a> <div>        产品经理应该有坚实的专业基础,这里的基础包括产品方向和产品策略的把握,包括设计,也包括对技术的理解和见识,对运营和市场的敏感,以及良好的沟通和协作能力。换言之,既然是产品经理,整个产品的方方面面都应该能摸得出门道。这也不懂那也不懂,如何让人信服?如何让自己懂?就是不断学习,不仅仅从书本中,更从平时和各种角色的沟通</div> </li> <li><a href="/article/1507.htm" title="如何为rich:tree不同类型节点设置右键菜单" target="_blank">如何为rich:tree不同类型节点设置右键菜单</a> <span class="text-muted">sunjing</span> <a class="tag" taget="_blank" href="/search/contextMenu/1.htm">contextMenu</a><a class="tag" taget="_blank" href="/search/tree/1.htm">tree</a><a class="tag" taget="_blank" href="/search/Richfaces/1.htm">Richfaces</a> <div>组合使用target和targetSelector就可以啦,如下: <rich:tree id="ruleTree" value="#{treeAction.ruleTree}" var="node" nodeType="#{node.type}" selectionChangeListener=&qu</div> </li> <li><a href="/article/1634.htm" title="【Redis二】Redis2.8.17搭建主从复制环境" target="_blank">【Redis二】Redis2.8.17搭建主从复制环境</a> <span class="text-muted">bit1129</span> <a class="tag" taget="_blank" href="/search/redis/1.htm">redis</a> <div>开始使用Redis2.8.17 Redis第一篇在Redis2.4.5上搭建主从复制环境,对它的主从复制的工作机制,真正的惊呆了。不知道Redis2.8.17的主从复制机制是怎样的,Redis到了2.4.5这个版本,主从复制还做成那样,Impossible is nothing! 本篇把主从复制环境再搭一遍看看效果,这次在Unbuntu上用官方支持的版本。   Ubuntu上安装Red</div> </li> <li><a href="/article/1761.htm" title="JSONObject转换JSON--将Date转换为指定格式" target="_blank">JSONObject转换JSON--将Date转换为指定格式</a> <span class="text-muted">白糖_</span> <a class="tag" taget="_blank" href="/search/JSONObject/1.htm">JSONObject</a> <div>项目中,经常会用JSONObject插件将JavaBean或List<JavaBean>转换为JSON格式的字符串,而JavaBean的属性有时候会有java.util.Date这个类型的时间对象,这时JSONObject默认会将Date属性转换成这样的格式:   {"nanos":0,"time":-27076233600000,</div> </li> <li><a href="/article/1888.htm" title="JavaScript语言精粹读书笔记" target="_blank">JavaScript语言精粹读书笔记</a> <span class="text-muted">braveCS</span> <a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a> <div>【经典用法】:   //①定义新方法 Function .prototype.method=function(name, func){ this.prototype[name]=func; return this; } //②给Object增加一个create方法,这个方法创建一个使用原对</div> </li> <li><a href="/article/2015.htm" title="编程之美-找符合条件的整数 用字符串来表示大整数避免溢出" target="_blank">编程之美-找符合条件的整数 用字符串来表示大整数避免溢出</a> <span class="text-muted">bylijinnan</span> <a class="tag" taget="_blank" href="/search/%E7%BC%96%E7%A8%8B%E4%B9%8B%E7%BE%8E/1.htm">编程之美</a> <div> import java.util.LinkedList; public class FindInteger { /** * 编程之美 找符合条件的整数 用字符串来表示大整数避免溢出 * 题目:任意给定一个正整数N,求一个最小的正整数M(M>1),使得N*M的十进制表示形式里只含有1和0 * * 假设当前正在搜索由0,1组成的K位十进制数</div> </li> <li><a href="/article/2142.htm" title="读书笔记" target="_blank">读书笔记</a> <span class="text-muted">chengxuyuancsdn</span> <a class="tag" taget="_blank" href="/search/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/1.htm">读书笔记</a> <div>1、Struts访问资源 2、把静态参数传递给一个动作 3、<result>type属性 4、s:iterator、s:if c:forEach 5、StringBuilder和StringBuffer 6、spring配置拦截器 1、访问资源 (1)通过ServletActionContext对象和实现ServletContextAware,ServletReque</div> </li> <li><a href="/article/2269.htm" title="[通讯与电力]光网城市建设的一些问题" target="_blank">[通讯与电力]光网城市建设的一些问题</a> <span class="text-muted">comsci</span> <a class="tag" taget="_blank" href="/search/%E9%97%AE%E9%A2%98/1.htm">问题</a> <div>       信号防护的问题,前面已经说过了,这里要说光网交换机与市电保障的关系       我们过去用的ADSL线路,因为是电话线,在小区和街道电力中断的情况下,只要在家里用笔记本电脑+蓄电池,连接ADSL,同样可以上网........     </div> </li> <li><a href="/article/2396.htm" title="oracle 空间RESUMABLE" target="_blank">oracle 空间RESUMABLE</a> <span class="text-muted">daizj</span> <a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a><a class="tag" taget="_blank" href="/search/%E7%A9%BA%E9%97%B4%E4%B8%8D%E8%B6%B3/1.htm">空间不足</a><a class="tag" taget="_blank" href="/search/RESUMABLE/1.htm">RESUMABLE</a><a class="tag" taget="_blank" href="/search/%E9%94%99%E8%AF%AF%E6%8C%82%E8%B5%B7/1.htm">错误挂起</a> <div>空间RESUMABLE操作  转 Oracle从9i开始引入这个功能,当出现空间不足等相关的错误时,Oracle可以不是马上返回错误信息,并回滚当前的操作,而是将操作挂起,直到挂起时间超过RESUMABLE TIMEOUT,或者空间不足的错误被解决。 这一篇简单介绍空间RESUMABLE的例子。 第一次碰到这个特性是在一次安装9i数据库的过程中,在利用D</div> </li> <li><a href="/article/2523.htm" title="重构第一次写的线程池" target="_blank">重构第一次写的线程池</a> <span class="text-muted">dieslrae</span> <a class="tag" taget="_blank" href="/search/%E7%BA%BF%E7%A8%8B%E6%B1%A0+python/1.htm">线程池 python</a> <div>最近没有什么学习欲望,修改之前的线程池的计划一直搁置,这几天比较闲,还是做了一次重构,由之前的2个类拆分为现在的4个类. 1、首先是工作线程类:TaskThread,此类为一个工作线程,用于完成一个工作任务,提供等待(wait),继续(proceed),绑定任务(bindTask)等方法 #!/usr/bin/env python # -*- coding:utf8 -*- </div> </li> <li><a href="/article/2650.htm" title="C语言学习六指针" target="_blank">C语言学习六指针</a> <span class="text-muted">dcj3sjt126com</span> <a class="tag" taget="_blank" href="/search/c/1.htm">c</a> <div>初识指针,简单示例程序: /* 指针就是地址,地址就是指针 地址就是内存单元的编号 指针变量是存放地址的变量 指针和指针变量是两个不同的概念 但是要注意: 通常我们叙述时会把指针变量简称为指针,实际它们含义并不一样 */ # include <stdio.h> int main(void) { int * p; // p是变量的名字, int * </div> </li> <li><a href="/article/2777.htm" title="yii2 beforeSave afterSave beforeDelete" target="_blank">yii2 beforeSave afterSave beforeDelete</a> <span class="text-muted">dcj3sjt126com</span> <a class="tag" taget="_blank" href="/search/delete/1.htm">delete</a> <div>public function afterSave($insert, $changedAttributes) { parent::afterSave($insert, $changedAttributes); if($insert) { //这里是新增数据 } else { //这里是更新数据 } }  </div> </li> <li><a href="/article/2904.htm" title="timertask" target="_blank">timertask</a> <span class="text-muted">shuizhaosi888</span> <a class="tag" taget="_blank" href="/search/timertask/1.htm">timertask</a> <div>java.util.Timer timer = new java.util.Timer(true); // true 说明这个timer以daemon方式运行(优先级低, // 程序结束timer也自动结束),注意,javax.swing // 包中也有一个Timer类,如果import中用到swing包, // 要注意名字的冲突。 TimerTask task = new</div> </li> <li><a href="/article/3031.htm" title="Spring Security(13)——session管理" target="_blank">Spring Security(13)——session管理</a> <span class="text-muted">234390216</span> <a class="tag" taget="_blank" href="/search/session/1.htm">session</a><a class="tag" taget="_blank" href="/search/Spring+Security/1.htm">Spring Security</a><a class="tag" taget="_blank" href="/search/%E6%94%BB%E5%87%BB%E4%BF%9D%E6%8A%A4/1.htm">攻击保护</a><a class="tag" taget="_blank" href="/search/%E8%B6%85%E6%97%B6/1.htm">超时</a> <div>session管理 目录   1.1     检测session超时 1.2     concurrency-control 1.3     session 固定攻击保护         </div> </li> <li><a href="/article/3158.htm" title="公司项目NODEJS实践0.3[ mongo / session ...]" target="_blank">公司项目NODEJS实践0.3[ mongo / session ...]</a> <span class="text-muted">逐行分析JS源代码</span> <a class="tag" taget="_blank" href="/search/mongodb/1.htm">mongodb</a><a class="tag" taget="_blank" href="/search/session/1.htm">session</a><a class="tag" taget="_blank" href="/search/nodejs/1.htm">nodejs</a> <div>    http://www.upopen.cn   一、前言         书接上回,我们搭建了WEB服务端路由、模板等功能,完成了register 通过ajax与后端的通信,今天主要完成数据与mongodb的存取,实现注册 / 登录 /</div> </li> <li><a href="/article/3285.htm" title="pojo.vo.po.domain区别" target="_blank">pojo.vo.po.domain区别</a> <span class="text-muted">LiaoJuncai</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/VO/1.htm">VO</a><a class="tag" taget="_blank" href="/search/POJO/1.htm">POJO</a><a class="tag" taget="_blank" href="/search/javabean/1.htm">javabean</a><a class="tag" taget="_blank" href="/search/domain/1.htm">domain</a> <div>  POJO = "Plain Old Java Object",是MartinFowler等发明的一个术语,用来表示普通的Java对象,不是JavaBean, EntityBean 或者 SessionBean。POJO不但当任何特殊的角色,也不实现任何特殊的Java框架的接口如,EJB, JDBC等等。      即POJO是一个简单的普通的Java对象,它包含业务逻辑</div> </li> <li><a href="/article/3412.htm" title="Windows Error Code" target="_blank">Windows Error Code</a> <span class="text-muted">OhMyCC</span> <a class="tag" taget="_blank" href="/search/windows/1.htm">windows</a> <div>0 操作成功完成. 1 功能错误. 2 系统找不到指定的文件. 3 系统找不到指定的路径. 4 系统无法打开文件. 5 拒绝访问. 6 句柄无效. 7 存储控制块被损坏. 8 存储空间不足, 无法处理此命令. 9 存储控制块地址无效. 10 环境错误. 11 试图加载格式错误的程序. 12 访问码无效. 13 数据无效. 14 存储器不足, 无法完成此操作. 15 系</div> </li> <li><a href="/article/3539.htm" title="在storm集群环境下发布Topology" target="_blank">在storm集群环境下发布Topology</a> <span class="text-muted">roadrunners</span> <a class="tag" taget="_blank" href="/search/%E9%9B%86%E7%BE%A4/1.htm">集群</a><a class="tag" taget="_blank" href="/search/storm/1.htm">storm</a><a class="tag" taget="_blank" href="/search/topology/1.htm">topology</a><a class="tag" taget="_blank" href="/search/spout/1.htm">spout</a><a class="tag" taget="_blank" href="/search/bolt/1.htm">bolt</a> <div>storm的topology设计和开发就略过了。本章主要来说说如何在storm的集群环境中,通过storm的管理命令来发布和管理集群中的topology。   1、打包 打包插件是使用maven提供的maven-shade-plugin,详细见maven-shade-plugin。 <plugin> <groupId>org.apache.maven.</div> </li> <li><a href="/article/3666.htm" title="为什么不允许代码里出现“魔数”" target="_blank">为什么不允许代码里出现“魔数”</a> <span class="text-muted">tomcat_oracle</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a> <div>  在一个新项目中,我最先做的事情之一,就是建立使用诸如Checkstyle和Findbugs之类工具的准则。目的是制定一些代码规范,以及避免通过静态代码分析就能够检测到的bug。   迟早会有人给出案例说这样太离谱了。其中的一个案例是Checkstyle的魔数检查。它会对任何没有定义常量就使用的数字字面量给出警告,除了-1、0、1和2。   很多开发者在这个检查方面都有问题,这可以从结果</div> </li> <li><a href="/article/3793.htm" title="zoj 3511 Cake Robbery(线段树)" target="_blank">zoj 3511 Cake Robbery(线段树)</a> <span class="text-muted">阿尔萨斯</span> <a class="tag" taget="_blank" href="/search/%E7%BA%BF%E6%AE%B5%E6%A0%91/1.htm">线段树</a> <div> 题目链接:zoj 3511 Cake Robbery 题目大意:就是有一个N边形的蛋糕,切M刀,从中挑选一块边数最多的,保证没有两条边重叠。 解题思路:有多少个顶点即为有多少条边,所以直接按照切刀切掉点的个数排序,然后用线段树维护剩下的还有哪些点。 #include <cstdio> #include <cstring> #include <vector&</div> </li> </ul> </div> </div> </div> <div> <div class="container"> <div class="indexes"> <strong>按字母分类:</strong> <a href="/tags/A/1.htm" target="_blank">A</a><a href="/tags/B/1.htm" target="_blank">B</a><a href="/tags/C/1.htm" target="_blank">C</a><a href="/tags/D/1.htm" target="_blank">D</a><a href="/tags/E/1.htm" target="_blank">E</a><a href="/tags/F/1.htm" target="_blank">F</a><a href="/tags/G/1.htm" target="_blank">G</a><a href="/tags/H/1.htm" target="_blank">H</a><a href="/tags/I/1.htm" target="_blank">I</a><a href="/tags/J/1.htm" target="_blank">J</a><a href="/tags/K/1.htm" target="_blank">K</a><a href="/tags/L/1.htm" target="_blank">L</a><a href="/tags/M/1.htm" target="_blank">M</a><a href="/tags/N/1.htm" target="_blank">N</a><a href="/tags/O/1.htm" target="_blank">O</a><a href="/tags/P/1.htm" target="_blank">P</a><a href="/tags/Q/1.htm" target="_blank">Q</a><a href="/tags/R/1.htm" target="_blank">R</a><a href="/tags/S/1.htm" target="_blank">S</a><a href="/tags/T/1.htm" target="_blank">T</a><a href="/tags/U/1.htm" target="_blank">U</a><a href="/tags/V/1.htm" target="_blank">V</a><a href="/tags/W/1.htm" target="_blank">W</a><a href="/tags/X/1.htm" target="_blank">X</a><a href="/tags/Y/1.htm" target="_blank">Y</a><a href="/tags/Z/1.htm" target="_blank">Z</a><a href="/tags/0/1.htm" target="_blank">其他</a> </div> </div> </div> <footer id="footer" class="mb30 mt30"> <div class="container"> <div class="footBglm"> <a target="_blank" href="/">首页</a> - <a target="_blank" href="/custom/about.htm">关于我们</a> - <a target="_blank" href="/search/Java/1.htm">站内搜索</a> - <a target="_blank" href="/sitemap.txt">Sitemap</a> - <a target="_blank" href="/custom/delete.htm">侵权投诉</a> </div> <div class="copyright">版权所有 IT知识库 CopyRight © 2000-2050 E-COM-NET.COM , All Rights Reserved. <!-- <a href="https://beian.miit.gov.cn/" rel="nofollow" target="_blank">京ICP备09083238号</a><br>--> </div> </div> </footer> <!-- 代码高亮 --> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shCore.js"></script> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shLegacy.js"></script> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shAutoloader.js"></script> <link type="text/css" rel="stylesheet" href="/static/syntaxhighlighter/styles/shCoreDefault.css"/> <script type="text/javascript" src="/static/syntaxhighlighter/src/my_start_1.js"></script> </body> </html>