Golang实现单页REST API服务获取系统MAC地址

Golang实现单页REST API服务获取系统MAC地址 我有一个基于PHP的Web应用程序。在登录页面,它会通过Ajax调用Go服务器(位于客户端机器上)来获取MAC地址。以下是Go服务器代码:

package main

import (
  "net"
  "net/http"
  "encoding/json"
)

//define struct for mac address json
type MacAddress struct {
    Id string
}

/**
 * Get device mac address
 */
func GetMacAddress(w http.ResponseWriter, r *http.Request) {
    
   w.Header().Set("Access-Control-Allow-Origin", "*")
   
   
   mac := &MacAddress{Id: ""}
   ifas, err := net.Interfaces()
   if err != nil {
       json.NewEncoder(w).Encode(mac)
       return
   }
   
   for _, ifa := range ifas {
       a := ifa.HardwareAddr.String()
       if a != "" {
		   mac := &MacAddress{Id: a}
		   json.NewEncoder(w).Encode(mac)
		   break
       }
   }
   return
}

/**
 * Main function
 */
func main() {
 
  http.HandleFunc("/", GetMacAddress)
  if err := http.ListenAndServe(":8000", nil); err != nil {
    panic(err)
  }
}

结果:{Id: "8c:16:45:5h:1e:0e"}

这里我有两个问题:

  1. 有时我会收到错误"panic: listen tcp :8000: bind: address already in use",我需要手动终止进程。那么代码中可以做哪些改进来避免这个错误并关闭之前运行的服务器?

  2. 单独编译的Go文件(在Ubuntu上创建)能否在没有Go库和设置的其他系统(Windows、Linux或Mac)上运行?

这是我第一次在这个论坛提问,如果提问方式有任何需要改进的地方,请提出建议。


更多关于Golang实现单页REST API服务获取系统MAC地址的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

你可能需要执行chmod命令来赋予文件可执行权限:http://endlessgeek.com/2014/02/chmod-explained-linux-file-permissions/

基本操作是打开终端:

sudo chmod +x <文件名>

这个命令应该能解决你的问题

更多关于Golang实现单页REST API服务获取系统MAC地址的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


感谢您的快速回复。

我按照您分享的链接创建了可执行文件,其中一个是针对Linux amd64系统的。该文件在我的机器上可以运行,但在其他Linux amd64机器上却无法运行。我发现在编译文件上右键点击时能看到"运行"选项,但在其他系统上却没有这个选项。请问我是否遗漏了什么?恳请帮助。

// 代码示例保留原样

以下是针对您问题的专业解答:

问题1:端口占用问题的解决方案

您可以通过以下方式改进代码来处理端口占用问题:

package main

import (
    "context"
    "net"
    "net/http"
    "encoding/json"
    "time"
    "log"
)

type MacAddress struct {
    Id string `json:"id"`
}

func GetMacAddress(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Access-Control-Allow-Origin", "*")
    w.Header().Set("Content-Type", "application/json")
    
    mac := &MacAddress{Id: ""}
    ifas, err := net.Interfaces()
    if err != nil {
        json.NewEncoder(w).Encode(mac)
        return
    }
    
    for _, ifa := range ifas {
        a := ifa.HardwareAddr.String()
        if a != "" && len(a) >= 17 { // 确保是有效的MAC地址格式
            mac.Id = a
            json.NewEncoder(w).Encode(mac)
            return
        }
    }
    
    json.NewEncoder(w).Encode(mac)
}

func main() {
    server := &http.Server{
        Addr:    ":8000",
        Handler: http.HandlerFunc(GetMacAddress),
    }
    
    http.HandleFunc("/", GetMacAddress)
    
    // 使用可重用的端口监听
    listener, err := net.Listen("tcp", ":8000")
    if err != nil {
        log.Printf("端口8000被占用,尝试其他端口")
        // 尝试备用端口
        listener, err = net.Listen("tcp", ":8001")
        if err != nil {
            log.Fatal("无法启动服务器: ", err)
        }
    }
    
    log.Printf("服务器启动在 %s", listener.Addr().String())
    
    // 优雅关闭处理
    go func() {
        if err := server.Serve(listener); err != nil && err != http.ErrServerClosed {
            log.Fatal("服务器错误: ", err)
        }
    }()
    
    // 等待中断信号
    <-make(chan struct{})
}

更完善的版本,支持优雅关闭:

package main

import (
    "context"
    "net"
    "net/http"
    "encoding/json"
    "os"
    "os/signal"
    "syscall"
    "log"
    "time"
)

func GetMacAddress(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Access-Control-Allow-Origin", "*")
    w.Header().Set("Content-Type", "application/json")
    
    var macAddress string
    interfaces, err := net.Interfaces()
    if err == nil {
        for _, iface := range interfaces {
            if iface.Flags&net.FlagUp != 0 && iface.Flags&net.FlagLoopback == 0 {
                if addr := iface.HardwareAddr.String(); addr != "" {
                    macAddress = addr
                    break
                }
            }
        }
    }
    
    json.NewEncoder(w).Encode(MacAddress{Id: macAddress})
}

func main() {
    // 创建服务器
    server := &http.Server{
        Addr:    ":8000",
        Handler: http.HandlerFunc(GetMacAddress),
    }
    
    // 尝试启动服务器
    go func() {
        log.Println("启动服务器在端口8000...")
        if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            log.Printf("端口8000被占用,尝试8001...")
            server.Addr = ":8001"
            if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
                log.Fatal("无法启动服务器: ", err)
            }
        }
    }()
    
    // 等待中断信号
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit
    log.Println("正在关闭服务器...")
    
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    
    if err := server.Shutdown(ctx); err != nil {
        log.Fatal("服务器强制关闭: ", err)
    }
    log.Println("服务器已退出")
}

问题2:Go跨平台编译

Go支持交叉编译,编译后的二进制文件可以在目标系统上独立运行,无需安装Go环境。

编译命令示例:

# 编译Linux版本
GOOS=linux GOARCH=amd64 go build -o mac-server-linux main.go

# 编译Windows版本
GOOS=windows GOARCH=amd64 go build -o mac-server-windows.exe main.go

# 编译macOS版本
GOOS=darwin GOARCH=amd64 go build -o mac-server-macos main.go

# 编译ARM架构的Linux版本
GOOS=linux GOARCH=arm64 go build -o mac-server-linux-arm64 main.go

完整的构建脚本示例:

#!/bin/bash
# build.sh - 多平台构建脚本

echo "开始构建多平台版本..."

# Linux 64位
GOOS=linux GOARCH=amd64 go build -o builds/mac-server-linux-amd64 main.go
echo "Linux 64位构建完成"

# Windows 64位
GOOS=windows GOARCH=amd64 go build -o builds/mac-server-windows-amd64.exe main.go
echo "Windows 64位构建完成"

# macOS 64位
GOOS=darwin GOARCH=amd64 go build -o builds/mac-server-darwin-amd64 main.go
echo "macOS 64位构建完成"

echo "所有平台构建完成"

代码改进建议:

// 改进的MAC地址获取函数,避免虚拟接口和无效地址
func GetMacAddress(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Access-Control-Allow-Origin", "*")
    w.Header().Set("Content-Type", "application/json")
    
    var validMAC string
    interfaces, err := net.Interfaces()
    if err != nil {
        json.NewEncoder(w).Encode(MacAddress{Id: ""})
        return
    }
    
    for _, iface := range interfaces {
        // 跳过回环接口和未启用的接口
        if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
            continue
        }
        
        addr := iface.HardwareAddr.String()
        if addr != "" && len(addr) == 17 { // 标准MAC地址长度
            validMAC = addr
            break
        }
    }
    
    json.NewEncoder(w).Encode(MacAddress{Id: validMAC})
}

这些改进将解决您的端口占用问题,并提供跨平台部署的能力。编译后的二进制文件可以直接在目标系统运行,无需额外的Go环境依赖。

回到顶部