1 Star 1 Fork 1

坐公交也用券/gns

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
gns.go 11.65 KB
一键复制 编辑 原始数据 按行查看 历史
坐公交也用券 提交于 2024-11-23 12:18 . 增加自动发布脚本
package gns
import (
"encoding/binary"
"fmt"
"golang.org/x/net/context"
"io"
"net"
"net/http"
"os"
"os/exec"
"runtime"
"strconv"
"strings"
"time"
"gitee.com/liumou_site/gbm"
"gitee.com/liumou_site/logger"
"github.com/spf13/cast"
)
// buildDNSQuery 构建一个标准的DNS查询数据包
func buildDNSQuery(domain string) []byte {
// DNS查询的基本结构
transactionID := 0x1234
flags := 0x0100 // 标准查询
questions := 1
answerRRs := 0
authorityRRs := 0
additionalRRs := 0
// 构建查询部分
var query []byte
for _, part := range strings.Split(domain, ".") {
query = append(query, byte(len(part)))
query = append(query, part...)
}
query = append(query, 0) // 结束标志
query = append(query, 0x00, 0x01) // 类型 A
query = append(query, 0x00, 0x01) // 类 IN
// 构建完整的DNS查询数据包
packet := make([]byte, 2+2+2+2+2+2+len(query))
binary.BigEndian.PutUint16(packet[0:2], uint16(transactionID))
binary.BigEndian.PutUint16(packet[2:4], uint16(flags))
binary.BigEndian.PutUint16(packet[4:6], uint16(questions))
binary.BigEndian.PutUint16(packet[6:8], uint16(answerRRs))
binary.BigEndian.PutUint16(packet[8:10], uint16(authorityRRs))
binary.BigEndian.PutUint16(packet[10:12], uint16(additionalRRs))
copy(packet[12:], query)
return packet
}
// HttpStatus 检查给定主机的HTTP状态。
// 这个函数根据API的端口和是否使用HTTPS来构造完整的URL,然后检查该URL的HTTP状态。
// 参数:
//
// host: 需要检查HTTP状态的主机名。
//
// 返回值:
//
// 如果URL的HTTP状态表示成功,返回true;否则返回false。
func (api *Api) HttpStatus(host string) bool {
// 根据API的端口号构造URL。
var urls string
if api.port == 80 {
// 如果端口是标准的HTTP端口80,则仅使用主机名。
urls = host
} else {
// 否则,将端口号转换为字符串并附加到主机名后。
p := cast.ToString(api.port)
urls = host + ":" + p
}
// 根据API是否使用HTTPS,构造完整的URL。
if api.https {
urls = "https://" + urls
} else {
urls = "http://" + urls
}
// 将协议模式转换为大写,用于日志记录。
mode := strings.ToUpper(api.agreement)
// 记录生成的URL和使用的模式,用于调试。
logger.Debug("Generate url", urls)
logger.Debug("Usage mode", mode)
// 检查构造的URL的HTTP状态,并返回检查结果。
return api.HttpStatusUrl(urls, time.Duration(api.Timeout)) == nil
}
// PortTcpStatus 检查指定IP和端口是否可以成功建立TCP连接。
// 参数:
//
// ip: 目标IP地址
// port: 目标端口号
// out: 连接超时时间的倍数,默认为1秒
//
// 返回值:
//
// 如果连接成功返回true,否则返回false
func PortTcpStatus(ip, port string, out time.Duration) bool {
// 尝试建立TCP连接,超时时间为out秒
conn, err := net.DialTimeout("tcp", net.JoinHostPort(ip, port), time.Second*out)
if err != nil {
// 连接失败,记录错误日志
logger.Error("连接错误: %s", err)
return false
}
// 使用defer确保连接关闭
defer func(conn net.Conn) {
err := conn.Close()
if err != nil {
// 关闭连接时发生错误,记录日志
logger.Error("关闭连接错误: %s", err)
}
}(conn)
// 连接成功,记录日志
logger.Info("连接成功: [ %s:%s ]", ip, port)
return true
}
type ConnError struct {
Message string
}
func (e *ConnError) Error() string {
return e.Message
}
// PortUdpStatus 检查指定IP和端口的UDP连接状态。
// 该函数尝试在指定时间内建立UDP连接,并发送一个DNS查询数据包以测试连接。
// 参数:
//
// ip: 要连接的IP地址。
// port: 要连接的端口号。
// Timeout: 连接和读取操作的超时时间。
//
// 返回值:
//
// 如果连接成功建立并能够发送和接收数据,则返回nil。
// 如果发生错误,返回自定义的ConnError,包含错误信息。
func PortUdpStatus(ip string, port int, timeout time.Duration) error {
var errInfo error
// 将IP和端口组合成网络地址。
addr := net.JoinHostPort(ip, strconv.Itoa(port))
// 尝试在指定时间内建立UDP连接。
conn, err := net.DialTimeout("udp", addr, timeout)
if err != nil {
// 记录连接错误并返回自定义错误信息。
errInfo = fmt.Errorf("连接错误: %v - 地址: %s\n", err, addr)
logger.Error(errInfo)
return &ConnError{Message: errInfo.Error()}
}
// 确保在函数返回前关闭连接。
defer func(conn net.Conn) {
err = conn.Close()
if err != nil {
// 记录关闭连接的错误,但不返回错误。
logger.Error(fmt.Errorf("关闭连接错误: %v - 地址: %s", err, addr))
}
}(conn)
// 构建标准的DNS查询数据包
query := buildDNSQuery("example.com")
// 发送数据包以测试连接。
logger.Info(fmt.Sprintf("发送数据包: %x - 地址: %s", query, addr))
_, err = conn.Write(query)
if err != nil {
// 记录发送数据包的错误并返回自定义错误信息。
errInfo = fmt.Errorf("发送数据包错误: %v - 地址: %s", err, addr)
logger.Error(errInfo)
return &ConnError{Message: errInfo.Error()}
}
// 设置读取超时,以防止长时间运行的读取操作。
err = conn.SetReadDeadline(time.Now().Add(timeout))
if err != nil {
// 记录设置读取超时的错误并返回自定义错误信息。
errInfo = fmt.Errorf("设置读取超时错误: %v - 地址: %s", err, addr)
logger.Error(errInfo)
return &ConnError{Message: errInfo.Error()}
}
// 准备接收响应数据。
buf := make([]byte, 1024)
n, err := conn.Read(buf)
if err != nil {
// 处理读取错误,包括连接中断的情况。
if err == io.EOF {
logger.Error(fmt.Sprintf("读取数据包错误: EOF - 地址: %s", addr))
return &ConnError{Message: fmt.Sprintf("读取数据包错误: EOF - 地址: %s", addr)}
}
errInfo = fmt.Errorf("读取数据包错误: %v - 地址: %s", err, addr)
logger.Error(errInfo)
return &ConnError{Message: errInfo.Error()}
}
// 打印接收到的数据包内容,以便调试。
logger.Debug(fmt.Sprintf("接收到的数据包: %x - 地址: %s", buf[:n], addr))
// 如果所有操作都成功,记录成功信息并返回nil。
logger.Info("连接成功: ", addr)
return nil
}
// DownloadFile 从指定的URL下载文件并保存到本地路径。
// 参数:
//
// url: 要下载的文件的URL。
// save: 保存文件的本地路径。
//
// 返回值:
//
// 如果下载成功,返回true;否则返回false。
func DownloadFile(url string, save string) bool {
logger.Debug("Downloading URL: %s", url) // 记录开始下载的URL
logger.Debug("Save File Path: %s", save) // 记录保存文件的路径
res, err := http.Get(url) // 发起HTTP GET请求
status := false // 初始化下载状态为失败
// 检查HTTP请求是否出错
if err != nil {
return false // 如果出错,返回false
} else {
f, err := os.Create(save) // 创建本地文件
// 检查文件创建是否出错
if err != nil {
return false // 如果出错,返回false
} else {
_, err := io.Copy(f, res.Body) // 将HTTP响应体复制到本地文件
// 检查复制过程中是否出错
if err != nil {
return false // 如果出错,返回false
}
status = true // 下载成功,更新状态为true
}
}
logger.Info("End of download") // 记录下载结束
return status // 返回下载状态
}
// printError 打印错误信息。
// 如果错误不为nil,它会根据错误的类型打印不同的错误信息。
// 如果是上下文取消错误,会打印请求已取消的信息。
// 如果是上下文超时错误,会打印请求超时的信息。
// 对于其他类型的错误,会打印其他类型的错误信息。
func printError(err error) {
if err != nil {
// 判断是否为上下文取消错误
if err == context.DeadlineExceeded {
logger.Error("请求已取消:", err)
} else if err == context.Canceled {
logger.Error("请求超时:", err)
} else {
logger.Error("其他类型的错误: ", err)
}
}
}
// HttpStatusUrl 发送一个 GET 请求到指定的 URL,并检查 HTTP 状态码。
// 如果请求成功,函数返回 nil;如果有错误发生,返回相应的错误。
func (api *Api) HttpStatusUrl(url string, timeout time.Duration) error {
// 创建一个新的上下文,并设置超时时间
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() // 确保在函数结束时取消上下文,释放资源
req, err := http.NewRequest(http.MethodGet, url, nil) // 创建一个新的 GET 请求
if err != nil {
logger.Error("Failed to create HTTP request: ", err)
return err // 如果创建请求失败,返回错误
}
// 将新的上下文作为请求的上下文,这样请求可以在指定的时间内完成
req = req.WithContext(ctx)
// 判断api.Client是否有效,如果没有,则创建一个新的 HTTP 客户端
if api.Client == nil {
api.Client = &http.Client{} // 如果没有客户端,创建一个新的
}
// 发送请求
resp, err := api.Client.Do(req) // 执行请求并获取响应
if err != nil {
logger.Error("Failed to send HTTP request: ", err)
return err // 如果请求发送失败,返回错误
}
defer func(Body io.ReadCloser) {
err = Body.Close() // 确保在返回前关闭响应体
if err != nil {
logger.Error(err)
// 如果关闭响应体时发生错误,这里可以记录日志,但不影响函数的返回
}
}(resp.Body)
// 检查响应状态码
if resp.StatusCode != api.code {
logger.Error("Unexpected HTTP status code: ", resp.StatusCode)
return fmt.Errorf("unexpected HTTP status code: %d", resp.StatusCode)
}
// 如果需要,可以在这里处理其他响应相关的逻辑
return nil // 请求成功,返回 nil
}
// shellSystem 执行命令并支持实时获取输出,和Python的os.system函数一样,返回退出代码
func (p *PingCmdAPI) shellSystem() (code int, out string, err error) {
var arg []string
commandPath := "ping" // 定义命令路径
if runtime.GOOS == "windows" {
arg = append([]string{"-w", cast.ToString(p.timeout)}, p.host) // 在切片头部添加/C参数
} else {
arg = append([]string{"-c", cast.ToString(p.pingCount), "-W", cast.ToString(p.timeout)}, p.host) // 在切片头部添加-c参数
}
cmdline := commandPath + gbm.SliceToString(arg, " ")
cmd := exec.Command(commandPath, arg...) // 创建命令实例
// 命令的错误输出和标准输出都连接到同一个管道
stdout, err := cmd.StdoutPipe()
cmd.Stderr = cmd.Stdout
if err != nil {
logger.Error("管道创建失败")
logger.Debug("Execute command: ", cmdline)
return 1, "管道创建失败", err
}
runErr := cmd.Start()
if runErr != nil {
logger.Error("命令启动失败 :", runErr)
logger.Debug("Execute command: ", cmdline)
return 2, "命令启动失败", runErr
}
// 创建字符串保存输出信息
var result string
// 从管道中实时获取输出并打印到终端
for {
tmp := make([]byte, 1024)
_, err := stdout.Read(tmp)
// 如果开启了实时打印,则将信息逐行输出到终端
if p.Realtime {
fmt.Print(string(tmp))
}
result = result + cast.ToString(string(tmp))
if err != nil {
break
}
}
// 等待命令退出,并等待任何复制到stdin或从stdout或stderr复制完成。
waitErr := cmd.Wait()
// ExitCode返回已退出进程的退出代码,如果进程尚未退出或被信号终止,则返回-1。
exitCode := cmd.ProcessState.ExitCode()
if p.ExitStatus {
logger.Info("Exit Code: ", exitCode)
}
if waitErr == nil {
if exitCode != 0 {
logger.Error("Error :", waitErr)
logger.Debug("Execute command: ", cmdline)
return exitCode, result, runErr
}
if exitCode == 0 {
return exitCode, result, nil
}
}
return exitCode, result, waitErr
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/liumou_site/gns.git
git@gitee.com:liumou_site/gns.git
liumou_site
gns
gns
master

搜索帮助

0d507c66 1850385 C8b1a773 1850385