582337768。群是一堆牛人,你有问题一般不过分,很多人都会解答一二。添加群的时候,请说明来自于 汉克博客园

汉克书

http://hankbook.cn

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理
  1. 内网服务器是一个只能通信外网,不对外开放任何端口
  2. 服务端的端口号是部署在外网的
  3. 服务端如何告诉内网服务器消息

简单来说,就是服务端使用一个长连接的socket,然后内网服务器连接这个IP+端口号进行长连接。

服务端发现有人来连接,就可以通过这个连接进行消息发送

以下是让AI写的一个tcp+http服务的代码

package main

import (
    "bufio"
    "fmt"
    "log"
    "net"
    "net/http"
    "sync"
    "time"
)

// Client 代表一个连接的客户端
type Client struct {
    ID       string
    Conn     net.Conn
    Outgoing chan []byte // 用于向客户端发送数据
}

// 全局客户端管理器
var (
    clients    = make(map[string]*Client)
    clientsMu  sync.RWMutex
    nextID     = 1
    nextIDMu   sync.Mutex
)

// 生成唯一客户端 ID
func generateClientID() string {
    nextIDMu.Lock()
    id := fmt.Sprintf("client-%d", nextID)
    nextID++
    nextIDMu.Unlock()
    return id
}

// TCP 服务端:处理客户端连接
func tcpServer() {
    listener, err := net.Listen("tcp", ":8081")
    if err != nil {
        log.Fatal("TCP server failed:", err)
    }
    defer listener.Close()
    log.Println("TCP server listening on :8081")

    for {
        conn, err := listener.Accept()
        if err != nil {
            log.Println("Accept error:", err)
            continue
        }

        clientID := generateClientID()
        client := &Client{
            ID:       clientID,
            Conn:     conn,
            Outgoing: make(chan []byte, 100), // 带缓冲,避免阻塞
        }

        // 注册客户端
        clientsMu.Lock()
        clients[clientID] = client
        clientsMu.Unlock()

        log.Printf("New client connected: %s\n", clientID)

        // 启动读写 goroutine
        go handleClientRead(client)
        go handleClientWrite(client)
    }
}

// 从客户端读取数据(可选:用于心跳或日志)
func handleClientRead(client *Client) {
    reader := bufio.NewReader(client.Conn)
    for {
        msg, err := reader.ReadString('\n')
        if err != nil {
            log.Printf("Client %s disconnected: %v", client.ID, err)
            cleanupClient(client)
            return
        }
        log.Printf("Received from %s: %s", client.ID, msg)
    }
}

// 向客户端写入数据
func handleClientWrite(client *Client) {
    defer func() {
        client.Conn.Close()
    }()

    for msg := range client.Outgoing {
        _, err := client.Conn.Write(msg)
        if err != nil {
            log.Printf("Write to %s failed: %v", client.ID, err)
            cleanupClient(client)
            return
        }
    }
}

// 清理断开的客户端
func cleanupClient(client *Client) {
    clientsMu.Lock()
    delete(clients, client.ID)
    close(client.Outgoing)
    clientsMu.Unlock()
    log.Printf("Client %s removed", client.ID)
}

// HTTP 服务:提供 Web 页面和 API
func httpServer() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        clientsMu.RLock()
        defer clientsMu.RUnlock()

        w.Header().Set("Content-Type", "text/html; charset=utf-8")
        fmt.Fprintf(w, `
<!DOCTYPE html>
<html>
<head><title>TCP Client Control</title></head>
<body>
<h2>Send Command to Client</h2>
<form method="POST" action="/send">
  <label>Client:
    <select name="client_id" required>
      <option value="">-- Select --</option>
      %s
    </select>
  </label><br><br>
  <label>Command:<br>
    <textarea name="command" rows="4" cols="50" placeholder="Enter command (will append \\n)" required></textarea>
  </label><br><br>
  <button type="submit">Send</button>
</form>
</body>
</html>
`, buildClientOptions())
    })

    http.HandleFunc("/send", func(w http.ResponseWriter, r *http.Request) {
        if r.Method != "POST" {
            http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
            return
        }
        clientID := r.FormValue("client_id")
        command := r.FormValue("command")
        if clientID == "" || command == "" {
            http.Error(w, "Missing client_id or command", http.StatusBadRequest)
            return
        }

        clientsMu.RLock()
        client, exists := clients[clientID]
        clientsMu.RUnlock()

        if !exists {
            http.Error(w, "Client not found", http.StatusNotFound)
            return
        }

        // 发送命令(确保以 \n 结尾,便于客户端读取)
        if command[len(command)-1] != '\n' {
            command += "\n"
        }
        select {
        case client.Outgoing <- []byte(command):
            log.Printf("Sent to %s: %s", clientID, command)
            fmt.Fprintf(w, "✅ Sent to %s: %s", clientID, command)
        default:
            http.Error(w, "Client send buffer full", http.StatusInternalServerError)
        }
    })

    log.Println("HTTP server listening on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

// 构建客户端下拉选项
func buildClientOptions() string {
    clientsMu.RLock()
    defer clientsMu.RUnlock()

    var options string
    for id := range clients {
        options += fmt.Sprintf("<option value=\"%s\">%s</option>\n", id, id)
    }
    return options
}

func main() {
    go tcpServer()
    httpServer()
}
posted on 2026-01-13 20:01  汉克书  阅读(3)  评论(0)    收藏  举报