2024-08-07



package main
 
import (
    "fmt"
    "github.com/gin-gonic/gin"
    "github.com/mattn/go-isatty"
    "github.com/sirupsen/logrus"
    "os"
)
 
// 定义日志输出格式
type customFormatter struct{}
 
func (f *customFormatter) Format(entry *logrus.Entry) ([]byte, error) {
    var msg string
    if isatty.IsTerminal(os.Stdout.Fd()) {
        msg = fmt.Sprintf("\033[36m%s\033[m %-44s: %s\n", entry.Time.Format("2006-01-02 15:04:05"), entry.Level, entry.Message)
    } else {
        msg = fmt.Sprintf("%s %-44s: %s\n", entry.Time.Format("2006-01-02 15:04:05"), entry.Level, entry.Message)
    }
    return []byte(msg), nil
}
 
func main() {
    // 设置 Gin 的运行模式
    gin.SetMode(gin.ReleaseMode)
 
    // 创建一个 Gin 路由器
    r := gin.New()
 
    // 为 Gin 的日志中间件设置日志格式
    log := logrus.New()
    log.SetFormatter(new(customFormatter))
    log.SetOutput(os.Stdout)
    log.SetLevel(logrus.DebugLevel)
    r.Use(GinLogger(log), GinRecovery(log))
 
    // 创建一个基本的 GET 路由
    r.GET("/", func(c *gin.Context) {
        c.String(200, "Hello, world!")
    })
 
    // 启动服务器
    _ = r.Run(":8080")
}
 
// GinLogger 返回一个 Gin 日志中间件,使用指定的 Logger 进行日志记录
func GinLogger(logger *logrus.Logger) gin.HandlerFunc {
    return func(c *gin.Context) {
        // 请求开始时间
        startTime := time.Now()
 
        // 请求结束时,调用该函数
        c.Next()
 
        // 计算请求响应时间
        endTime := time.Now()
        latencyTime := endTime.Sub(startTime)
 
        // 获取请求相关参数
        clientIP := c.ClientIP()
        method := c.Request.Method
        statusCode := c.Writer.Status()
        requestURI := c.Request.RequestURI
        referer := c.Request.Referer()
        dataSize := c.Writer.Size()
        if dataSize < 0 {
            dataSize = 0
        }
 
        // 使用自定义的 Logger 记录日志
        entry := logger.WithFields(logrus.Fields{
            "statusCode": statusCode,
            "latency":    latencyTime.Seconds(),
            "clientIP":   clientIP,
            "method":     method,
            "requestURI": requestURI,
            "referer":    referer,
            "dataSize":   dataSize,
        })
 
        if statusCode >= 400 {
            entry.Error("HTTP Request Error")
        } else {
            entry.Info("HTTP Request Info")
        }
    }
}
 
// GinRecovery 返回一个 Gin 恢复中间件,使用指定的 Logger 来记录错误信息
func GinRecovery(logger *logrus.Logger) gin.HandlerFunc {
    return func(c *gin.Context) {
        defer func() {
            if err := recover(); err !=
2024-08-07

以下是一个使用Go语言开发的基于Eclipse Mosquitto的MQTT客户端的简单示例代码。




package main
 
import (
    "fmt"
    "log"
    "time"
 
    mqtt "github.com/eclipse/paho.mqtt.golang"
)
 
func main() {
    opts := mqtt.NewClientOptions().AddBroker("tcp://broker.hivemq.com:1883")
    opts.SetClientID("go-mqtt-client")
    opts.SetUsername("")
    opts.SetPassword("")
 
    c := mqtt.NewClient(opts)
    if token := c.Connect(); token.Wait() && token.Error() != nil {
        log.Fatal(token.Error())
    }
 
    // Subscribe to topic
    if token := c.Subscribe("go/mqtt/topic", 0, func(c mqtt.Client, msg mqtt.Message) {
        fmt.Printf("Received message on topic: %s\nMessage: %s\n", msg.Topic(), string(msg.Payload()))
    }); token.Wait() && token.Error() != nil {
        log.Fatal(token.Error())
    }
 
    // Publish message every 5 seconds
    for {
        time.Sleep(5 * time.Second)
        if token := c.Publish("go/mqtt/topic", 0, false, "Hello MQTT"); token.Wait() && token.Error() != nil {
            log.Fatal(token.Error())
        }
    }
}

这段代码首先创建了一个MQTT客户端,并连接到了一个公共的MQTT代理(在本例中为HiveMQ的公共代理)。然后,它订阅了一个名为"go/mqtt/topic"的主题,并注册了一个回调函数来处理接收到的消息。最后,它每5秒钟发布一条消息到同一个主题。这个示例展示了如何使用Eclipse Mosquitto的Go语言客户端库进行基本的MQTT发布/订阅操作。

2024-08-07

在Go语言中,文件和目录的操作主要通过osio/ioutil包来完成。以下是一些常用的文件和目录操作函数:

  1. 创建目录:



err := os.Mkdir("new_directory", 0755)
if err != nil {
    log.Fatal(err)
}
  1. 创建多级目录:



err := os.MkdirAll("new_directory/sub_directory", 0755)
if err != nil {
    log.Fatal(err)
}
  1. 删除目录:



err := os.Remove("directory_to_remove")
if err != nil {
    log.Fatal(err)
}
  1. 删除多级目录:



err := os.RemoveAll("directory_to_remove")
if err != nil {
    log.Fatal(err)
}
  1. 打开文件:



file, err := os.Open("file_to_open")
if err != nil {
    log.Fatal(err)
}
defer file.Close()
  1. 创建并写入文件:



data := "hello, world!"
err := ioutil.WriteFile("new_file.txt", []byte(data), 0644)
if err != nil {
    log.Fatal(err)
}
  1. 读取文件:



buf, err := ioutil.ReadFile("file_to_read.txt")
if err != nil {
    log.Fatal(err)
}
fmt.Print(string(buf))
  1. 读取目录:



files, err := ioutil.ReadDir("directory_to_read")
if err != nil {
    log.Fatal(err)
}
for _, file := range files {
    fmt.Println(file.Name())
}

这些操作涵盖了文件和目录的基本创建、删除、打开、关闭、读取和写入操作。在实际应用中,你可能还需要处理其他更复杂的情况,如文件的读写锁定、文件的读取和写入进度等。

2024-08-07

在Ubuntu上安装和配置ddns-go实现内网穿透的步骤如下:

  1. 安装Go环境和Git:



sudo apt update
sudo apt install golang git -y
  1. 克隆ddns-go仓库:



mkdir -p ~/go/src/github.com/timothy-spencer/
cd ~/go/src/github.com/timothy-spencer/
git clone https://github.com/timothy-spencer/ddns-go.git
  1. 编译和安装ddns-go



cd ddns-go
go build -o ddns-go
  1. 创建配置文件config.json



{
  "accessKey": "your_access_key",
  "secretKey": "your_secret_key",
  "ipType": "IPv4",
  "hostnames": [
    {
      "zone": "yourdomain.com",
      "hostname": "ddns",
      "interval": 60
    }
  ],
  "notifications": [
    {
      "type": "email",
      "sender": "sender_email",
      "recipients": ["recipient_email"],
      "server": "mail.example.com:587",
      "username": "mail_username",
      "password": "mail_password",
      "interval": 300
    }
  ]
}

替换配置文件中的your_access_key, your_secret_key, yourdomain.com, ddns, sender_email, recipient_email, mail.example.com, mail_usernamemail_password为你的实际信息。

  1. 运行ddns-go



./ddns-go -c config.json

注意:在使用以上步骤时,你需要有有效的Access Key和Secret Key,可以从你的DNS服务提供商处获取。同时,确保你的邮件服务器设置正确,以便ddns-go能够发送通知。

以上步骤会在本地Ubuntu系统上安装ddns-go并根据提供的配置文件运行。如果你的网络环境允许,你可以通过配置正确的端口转发或者使用内网穿透服务来实现从外部网络访问你的内网服务。

2024-08-07



package main
 
import (
    "github.com/gin-gonic/gin"
    "github.com/mojocn/base64Captcha"
    "github.com/go-redis/redis"
    "time"
    "crypto/md5"
    "encoding/hex"
)
 
var redisClient *redis.Client
 
func init() {
    redisClient = redis.NewClient(&redis.Options{
        Addr:     "localhost:6379",
        Password: "", // 无密码
        DB:       0,  // 默认数据库
    })
}
 
func main() {
    r := gin.Default()
 
    // 生成验证码的处理函数
    r.GET("/captcha", func(c *gin.Context) {
        // 生成验证码
        captchaId, capValue, imgBuf := base64Captcha.GenerateCaptcha(320, 48, base64Captcha.ALL)
        
        // 计算验证码的MD5,用于存储时的key
        md5Ctx := md5.New()
        md5Ctx.Write([]byte(capValue))
        md5Value := hex.EncodeToString(md5Ctx.Sum(nil))
 
        // 存储验证码到Redis,设置过期时间
        err := redisClient.Set(md5Value, capValue, time.Minute*5).Err()
        if err != nil {
            c.String(500, "Error saving captcha to Redis")
            return
        }
 
        // 返回验证码ID和图片数据
        c.JSON(200, gin.H{
            "captchaId": captchaId,
            "imageData": imgBuf.String(),
        })
    })
 
    // 验证验证码的处理函数
    r.GET("/verifyCaptcha", func(c *gin.Context) {
        captchaId := c.Query("captchaId")
        captchaValue := c.Query("captchaValue")
 
        // 从Redis获取验证码
        md5Value, err := redisClient.Get(captchaId).Result()
        if err != nil {
            c.String(404, "Captcha not found or expired")
            return
        }
 
        // 比对验证码值,注意需要对比原始值,不是MD5
        if md5Value == captchaValue {
            c.String(200, "Captcha verified successfully")
        } else {
            c.String(401, "Captcha verification failed")
        }
    })
 
    r.Run(":8080")
}

这段代码首先初始化了一个Redis客户端用于与Redis服务器交互。然后,在HTTP服务器中定义了两个路由:/captcha用于生成新的验证码并将其存储到Redis,/verifyCaptcha用于验证用户输入的验证码值。在生成验证码时,我们计算了验证码的MD5值作为Redis中的key,存储时间设置为5分钟,并在验证时比对用户输入的验证码值。这样做可以防止用户直接猜测或缓存验证码的值。

2024-08-07



package main
 
import (
    "fmt"
    "github.com/gin-gonic/gin"
    "net/http"
    "time"
)
 
type User struct {
    Id        int
    Username  string `xorm:"unique"`
    Password  string
    Email     string `xorm:"unique"`
    CreatedAt time.Time
}
 
func Register(c *gin.Context) {
    var user User
    if err := c.ShouldBind(&user); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    if _, err := engine.Insert(&user); err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }
    c.JSON(http.StatusOK, gin.H{"status": "success", "data": user})
}
 
func Login(c *gin.Context) {
    var user User
    if err := c.ShouldBind(&user); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    var result User
    if err := engine.Where("username = ?", user.Username).Get(&result); err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }
    if result.Username == user.Username && result.Password == user.Password {
        c.JSON(http.StatusOK, gin.H{"status": "success", "data": result})
    } else {
        c.JSON(http.StatusUnauthorized, gin.H{"status": "fail", "message": "Invalid username or password"})
    }
}
 
func main() {
    r := gin.Default()
    r.POST("/register", Register)
    r.POST("/login", Login)
    r.Run(":8080")
}

这段代码实现了用户注册和登录的功能。它使用了Gin框架来简化HTTP请求的处理,并使用xorm来操作数据库。注册时,它会检查用户名和邮箱是否唯一,登录时,它会检查用户名和密码是否匹配。这个例子教会开发者如何使用Go语言和相关库来构建一个简单的IM系统的后端。

2024-08-07

题目描述:

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例 1:

输入:nums = [3,2,2,3], val = 3

输出:2, nums = [2,2]

解释:函数应返回新的长度 2, 并且 nums 中的前两个元素均不是 3。

示例 2:

输入:nums = [0,1,2,2,3,0,4,2], val = 2

输出:5, nums = [0,1,4,0,3]

解释:函数应返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 4, 0, 3。

提示:

<= nums.length <= 100

<= nums[i] <= 50

<= val <= 100

Java 代码实现:




class Solution {
    public int removeElement(int[] nums, int val) {
        int slowIndex = 0;
        for (int fastIndex = 0; fastIndex < nums.length; fastIndex++) {
            if (nums[fastIndex] != val) {
                nums[slowIndex] = nums[fastIndex];
                slowIndex++;
            }
        }
        return slowIndex;
    }
}

C 代码实现:




int removeElement(int* nums, int numsSize, int val) {
    int slowIndex = 0;
    for (int fastIndex = 0; fastIndex < numsSize; fastIndex++) {
        if (nums[fastIndex] != val) {
            nums[slowIndex] = nums[fastIndex];
            slowIndex++;
        }
    }
    return slowIndex;
}

Python3 代码实现:




class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        slowIndex = 0
        for fastIndex in range(len(nums)):
            if nums[fastIndex] != val:
                nums[slowIndex] = nums[fastIndex]
                slowIndex += 1
        return slowIndex

Go 代码实现:




func removeElement(nums []int, val int) int {
    slowIndex := 0
    for fastIndex := range nums {
        if nums[fastIndex] != val {
            nums[slowIndex] = nums[fastIndex]
            slowIndex++
        }
    }
    return slowIndex
}
2024-08-07



package main
 
import (
    "fmt"
    "math/rand"
    "time"
)
 
func main() {
    // 初始化随机数种子
    rand.Seed(time.Now().UnixNano())
 
    // 生成随机数并打印
    fmt.Println("随机数:", rand.Intn(10)) // 生成一个0到9之间的随机数
}

这段代码首先导入了必要的包,然后在main函数中初始化了随机数种子,最后生成了一个0到9之间的随机整数并打印出来。这个过程展示了如何在Go语言中使用math/rand包生成随机数,并且是学习Go语言开发的一个很好的入门示例。

2024-08-07

在 Go 语言中,从标准输入读取数据可以使用 os 包中的 Stdin 对象。以下是一些读取标准输入的方法:

  1. 使用 bufio 包创建一个 Scanner 对象,然后使用 Scanner 对象的 ScanText 方法读取输入。



package main
 
import (
    "bufio"
    "fmt"
    "os"
)
 
func main() {
    reader := bufio.NewReader(os.Stdin)
    fmt.Println("请输入一些文本:")
    text, _ := reader.ReadString('\n')
    fmt.Printf("你输入的文本是:%s", text)
}
  1. 直接使用 fmt.Scanfmt.Scanlnfmt.Scanf 读取输入。



package main
 
import (
    "fmt"
)
 
func main() {
    var text string
    fmt.Println("请输入一些文本:")
    fmt.Scanln(&text)
    fmt.Printf("你输入的文本是:%s", text)
}
  1. 使用 fmt 包的 fmt.Fscanfmt.Fscanln 从指定的 io.Reader 读取输入。



package main
 
import (
    "fmt"
    "os"
)
 
func main() {
    var text string
    fmt.Println("请输入一些文本:")
    fmt.Fscan(os.Stdin, &text)
    fmt.Printf("你输入的文本是:%s", text)
}

以上三种方法都可以从标准输入读取数据,你可以根据实际需求选择合适的方法。

2024-08-07

在Go语言中,并没有像其他面向对象语言那样的class和继承的概念,但是Go提供了结构体和接口,可以实现类似的功能。

  1. 封装

Go语言中通过结构体来实现封装,将对象的状态(字段)和行为(方法)捆绑在一起。




type Student struct {
    name string
    age  int
}
 
func (s Student) SayHello() {
    fmt.Printf("Hello, my name is %s, I am %d years old\n", s.name, s.age)
}
  1. 继承

Go语言中并不支持类似其他语言中的类继承,但是可以通过匿名字段的方式实现类似效果。




type Person struct {
    name string
    age  int
}
 
type Student struct {
    Person // 匿名字段
    school string
}
 
func (s Student) SayHello() {
    fmt.Printf("Hello, my name is %s, I am %d years old, I am studying in %s\n", s.name, s.age, s.school)
}
  1. 多态

Go语言中通过接口来实现多态。




type Speaker interface {
    Speak() string
}
 
type Chinese struct {}
 
func (c Chinese) Speak() string {
    return "Zhongguo Le"
}
 
type English struct {}
 
func (e English) Speak() string {
    return "Hello World"
}
 
func main() {
    var s Speaker
    s = Chinese{}
    fmt.Println(s.Speak())
 
    s = English{}
    fmt.Println(s.Speak())
}

以上代码中,Chinese和English分别实现了Speaker接口的Speak方法,根据实例化的类型不同,调用同样的方法可以产生不同的行为,这就是多态。